JsonConverter equivalent for HTTP GET parameter

When writing C # web API controllers for HTTP POST functions, I can use the Newtonsoft JSON attributes for the properties of the parameter object. In particular, I can use the JsonConverter attribute for properties of the enum type to convert the string representation received from the client into one enum value (and vice versa, for response objects):

 public class MyArgumentsDTO { [JsonConverter(typeof(SomeEnumConverter))] public SomeEnum MyValue { get; set; } } // in the controller: [Route("doSomething")] [HttpPost] public Boolean DoSomething(MyArgumentsDTO options); 

However, what should I do for HTTP GET methods that expect a parameter of this type enum ?

 [Route("getSomething")] [HttpGet] public Boolean GetSomething(SomeEnum myValue); 

Is there an attribute that I can decorate with the appropriate parameter to indicate the converter (string to enum)?

(To be clear, I use enumerations as an example, because I regularly use this method (with HTTP POST) with enums. Presumably, any solution that works for enumerations will work just as well for any other (possibly complex) data types.)


Of course, I can simply declare the parameter as string and do the conversion myself, in the body of the method. However, this seems unclean, and I agree with the statement in the linked answer :

Defining all the enumeration parameters as strings and then parsing them all over the world means that you have to do this on every single action, and you will need to develop a consistent approach to match all parsing errors.

Unfortunately, I do not understand the solution proposed in this answer, given that it does not even affect the use of TypeEnum mentioned in this question.


Using HTTP POST instead of HTTP GET for methods when I need the enum parameter also seems wrong. I do not think that the HTTP method should be chosen based on such internal technical characteristics.

+3
source share
1 answer

Emulators are correctly deserialized using ASP.NET model middleware. Try defining some simple listings, for example.

 public enum Color { None, Green, Red, } [Route("getSomething")] [HttpGet] public string Get(Color color) { // ... } 

If you get /api/values/color=Green , the color will be set correctly to Color.Green . If you need to convert custom values โ€‹โ€‹(for example, from #FF0000 to Color.Red ) with a custom type converter (see below), you will work.

ASP.NET also provides the ability to deserialize more complex data types from a URL. The easiest way is to implement a custom type converter. Here is an example from an application that I developed some time ago. He worked with orders with unique identifiers in the format <department>:<order number> , i.e. NY:123 or LA:456 . Model

 public class OrderId { public string DepartmentId { get; } public int OrderNumber { get; } public OrderId(string departmentId, int orderNumber) { DepartmentId = departmentId; OrderNumber = orderNumber; } } 

And it was necessary to go through the following order identifiers using the HTTP GET method:

 [HttpGet] public OrderDetails GetOrderDetails(OrderId orderId) 

To solve this problem and make orderId correctly created from the Url parameter, we could implement a custom Type Converter that converts a string value into an orderId instance:

 public class OrderIdTypeConverter : TypeConverter { private static readonly Regex OrderIdRegex = new Regex("^(.+):(\\d+)$", RegexOptions.Compiled); public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { var str = value as string; if (str != null) { int orderId; var match = OrderIdRegex.Match(str); if (match.Success && Int32.TryParse(match.Groups[2].Value, out orderId)) { return new OrderId(match.Groups[1].Value, orderId); } } return base.ConvertFrom(context, culture, value); } } 

To associate this type converter with the OrderId class, simply add the TypeConverter attribute:

 [TypeConverter(typeof(OrderIdTypeConverter))] public class OrderId 

Now, if we get Url /api/Orders/?orderId=NYC:123 , the GetOrderDetails action is called with a properly filled instance of orderId .

ASP.NET provides other extensibility points for binding a model to a URL. These are custom implementations of the IModelBinder and IValueProvider . See the article for more details.

If you cannot install a type converter for a type that you do not control, go for a custom template that should work for you. The following is an example implementation of IModelBinder to configure the conversion of enumeration values:

 public class CustomEnumModelBinder : IModelBinder { public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) { if (bindingContext.ModelType != typeof(Color)) { return false; } ValueProviderResult val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (val == null) { return false; } string rawValue = val.RawValue as string; if (rawValue == null) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Incorrect input value type"); return false; } // Your logic for converting string to enum. if (rawValue == "FF0000") { bindingContext.Model = Color.Red; return true; } bindingContext.ModelState.AddModelError(bindingContext.ModelName, $"Cannot convert {rawValue} to Color"); return false; } } [Route("getSomething")] [HttpGet] public string Get([ModelBinder(typeof(CustomEnumModelBinder))] Color color) { // ... } 

With a little extra work, you could implement a kind of universal binder that could aggregate existing Json converters.

+1
source

All Articles