I know that in ASP.NET Core it is possible to automatically convert some query string to an object using special model binding attributes - [FromRoute], [FromQuery] and so on.
Is it possible to perform such a conversion manually? Something like:
var queryString = Request.QueryString;
var obj = SomeMagic.QueryStringToObject<MyClass>(queryString);
CodePudding user response:
You can use QueryHelpers.ParseQuery(String)
to parse a query string into a dictionary.
If you want the actual same behavior as provided by the [FromQuery]
attribute I'd look at the QueryParameterValueSupplier.RenderParametersFromQueryString
method, which does most of the heavy-lifting. I'm not sure if this is meant to be used outside of the existing ASP.NET Core framework infrastructure.
Note that a query string is just a collection of string-based name-value pairs. There's no standard that dictates how this should be mapped to something more complex like a Java or C# class. So frameworks like ASP.NET Core build their own convention on top of that, in order to make their complex binding mechanisms work. (e.g. foo.bar[2]=123
). ASP.NET Core actually has two ways of binding query strings to a model (the "MVC" way and the "jQuery" way), see JQueryKeyValuePairNormalizer
.
// This is a helper method for Model Binding over a JQuery syntax.
// Normalize from JQuery to MVC keys. The model binding infrastructure uses MVC keys.
// x[] --> x
// [] --> ""
// x[12] --> x[12]
// x[field] --> x.field, where field is not a number
private static string NormalizeJQueryToMvc(StringBuilder builder, string key)
Finally on a personal note I tend to avoid the query string for anything more complex than simple name-value pairs. When you start to pull in more complex data structures you also run into many limitations. For instance: differentiating between null
and empty strings; awkward syntax for handling collections; etc. If I really must use the query string for passing along complex data structures, I fallback to a single Base64 encoded JSON-string and handle that manually within my code.
CodePudding user response:
Finally I found more generic solution than just parsing a query string. Here I get an instance of IModelBinder (actually an instance of ComplexObjectModelBinder) and use that as a service.
// DTO
//
public class PersonName
{
public string FirstName { get;set; }
public string LastName { get;set; }
}
// Action handler
// Here I want to convert HTTP request to an instance of PersonName manually
// Example: /SearchByName?LastName=Doe&FirstName=John
//
[AcceptVerbs("GET")]
public async Task<IActionResult> SearchByName(
[FromServices] IModelMetadataProvider modelMetadataProvider,
[FromServices] IModelBinderFactory modelBinderFactory)
{
var valueProvider = await CompositeValueProvider.CreateAsync(ControllerContext);
var modelMetadata = modelMetadataProvider.GetMetadataForType(typeof(PersonName));
var modelBinderFactoryContext = new ModelBinderFactoryContext
{
Metadata = modelMetadata,
CacheToken = modelMetadata
};
var modelBinder = modelBinderFactory.CreateBinder(modelBinderFactoryContext);
var modelBindingContext= DefaultModelBindingContext.CreateBindingContext(ControllerContext, valueProvider, modelMetadata, new BindingInfo(), string.Empty);
await modelBinder.BindModelAsync(modelBindingContext);
var personName = modelBindingContext.Model as PersonName;
// ...
return Ok(personName);
}