JsonConverter equivalent for HTTP GET parameter

做~自己de王妃 提交于 2019-12-04 13:21:36

Enums are correctly deserialized by ASP.NET model binder. Try defininig some simple enum, e.g.

public enum Color
{
    None,
    Green,
    Red,
}

[Route("getSomething")]
[HttpGet]
public string Get(Color color)
{
    // ...
}

If you GET /api/values/color=Green, color will be correctly set to Color.Green. If you need some custom values conversion (like #FF0000 to Color.Red) approach with custom Type Converter (see below) will work for you.

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

public class OrderId
{
    public string DepartmentId { get; }

    public int OrderNumber { get; }

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

And it was required to pass such order ids via HTTP GET method:

[HttpGet]
public OrderDetails GetOrderDetails(OrderId orderId)

To solve this and make orderId correctly created from an Url parameter, we could implement custom Type Converter that converts string value to instance of OrderId:

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 OrderId class just add the TypeConverter attribute:

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

Now if we get Url /api/Orders/?orderId=NYC:123, action GetOrderDetails will be called with correctly filled instance of OrderId.

ASP.NET provides another extensibility points for binding the model from the URL. They are custom implementations of IModelBinder and IValueProvider interfaces. Check this article for more details.

If you can't set type converter for the type that you don't control, approach with custom model binder should work for you. Here is a sample of IModelBinder implementation for customizing conversion of enum 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)
{
    // ...
}

Wit a bit of additional work you could implement some sort of generic model binder that could aggregate your existing Json converters.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!