JsonConverter等效于HTTP GET参数
在为HTTP POST函数编写C#Web API控制器时,我可以在参数对象的属性上使用Newtonsoft JSON的属性。 特别是,我可以在enum
类型的属性上使用JsonConverter
属性 ,将从客户端接收的字符串表示forms转换为其中一个enum
值(对于响应对象,则返回):
public class MyArgumentsDTO { [JsonConverter(typeof(SomeEnumConverter))] public SomeEnum MyValue { get; set; } } // in the controller: [Route("doSomething")] [HttpPost] public Boolean DoSomething(MyArgumentsDTO options);
但是,对于期望这种enum
类型的参数的HTTP GET方法,我该怎么办?
[Route("getSomething")] [HttpGet] public Boolean GetSomething(SomeEnum myValue);
是否有一个属性,我可以用相应的参数来装饰,以指示(字符串到枚举)转换器?
(要清楚,我使用枚举作为一个例子,因为我经常使用这种技术(使用HTTP POST)和枚举。假设任何适用于枚举的解决方案都适用于任何其他(可能是复杂的)数据类型。)
当然,我可以将参数声明为string
并在方法体中自行进行转换。 然而,这似乎是不洁净的,我同意相关答案中的陈述:
将所有枚举参数定义为字符串,然后在任何地方解析它们意味着您必须在每个操作上执行此操作,并且您需要提出一致的方法,以便所有解析错误都符合。
不幸的是,我不理解该答案中提出的解决方案,因为它甚至没有涉及使用该TypeEnum
提到的TypeEnum
。
当我需要一个enum参数时,使用HTTP POST代替HTTP GET方法似乎也有些错误。 我认为不应该根据这些内部技术来选择HTTP方法。
ASP.NET模型绑定器正确地对枚举进行反序列化。 尝试定义一些简单的枚举,例如
public enum Color { None, Green, Red, } [Route("getSomething")] [HttpGet] public string Get(Color color) { // ... }
如果您GET /api/values/color=Green
,则颜色将正确设置为Color.Green
。 如果您需要一些自定义值转换(如#FF0000
到Color.Red
),使用自定义类型转换器(见下文)的方法将适合您。
ASP.NET还提供了从URL中反序列化更复杂数据类型的可能性。 最简单的方法是实现自定义类型转换器。 这是我前一段时间开发的应用程序的示例。 它使用具有
格式的唯一标识符的订单,即NY:123
或LA:456
。 模型是
public class OrderId { public string DepartmentId { get; } public int OrderNumber { get; } public OrderId(string departmentId, int orderNumber) { DepartmentId = departmentId; OrderNumber = orderNumber; } }
并且需要通过HTTP GET方法传递这样的订单ID:
[HttpGet] public OrderDetails GetOrderDetails(OrderId orderId)
为了解决这个问题并使用Url参数正确创建orderId
,我们可以实现将字符串值转换为OrderId
实例的自定义Type Converter:
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); } }
要将此Type Converter与OrderId类关联,只需添加TypeConverter
属性:
[TypeConverter(typeof(OrderIdTypeConverter))] public class OrderId
现在,如果我们获得Url /api/Orders/?orderId=NYC:123
,将使用正确填充的OrderId
实例调用操作GetOrderDetails
。
ASP.NET提供了另一个可扩展点,用于从URL绑定模型。 它们是IModelBinder
和IValueProvider
接口的自定义实现。 查看此文章了解更多详细信息。
如果您无法为您不控制的类型设置类型转换器,则使用自定义模型绑定器的方法应该适合您。 以下是用于自定义枚举值转换的IModelBinder
实现示例:
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) { // ... }
您可以通过一些额外的工作来实现某种通用模型绑定器,它可以聚合您现有的Json转换器。