为HTTP POST函数编写C#Web API控制器时,我可以在参数对象的属性上使用Newtonsoft JSON的属性。特别是,我可以在enum
类型的属性上使用JsonConverter
attribute将从客户端接收的字符串表示转换为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
并在方法体中自行进行转换。然而,这似乎是不洁净的,我同意related answer中的陈述:
将所有枚举参数定义为字符串,然后在任何地方解析它们意味着您必须在每个操作上执行此操作,并且您需要提出一致的方法,以便所有解析错误都符合。
不幸的是,我不理解该答案中提出的解决方案,因为它甚至没有涉及使用该问题中提到的TypeEnum
。
当我需要枚举参数时,使用HTTP POST代替HTTP GET方法似乎也有些错误。我认为不应该根据这些内部技术来选择HTTP方法。
答案 0 :(得分:1)
ASP.NET模型绑定器正确地反序列化枚举。尝试定义一些简单的枚举,例如
public enum Color
{
None,
Green,
Red,
}
[Route("getSomething")]
[HttpGet]
public string Get(Color color)
{
// ...
}
如果您获得/api/values/color=Green
,则颜色将正确设置为Color.Green
。如果您需要一些自定义值转换(例如#FF0000
到Color.Red
),使用自定义类型转换器(见下文)的方法将适合您。
ASP.NET还提供了从URL中反序列化更复杂数据类型的可能性。最简单的方法是实现自定义类型转换器。这是我前一段时间开发的应用程序的示例。它使用格式为<department>:<order number>
的唯一标识符的订单,即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
实例的自定义类型转换器:
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
现在,如果我们获得了网址/api/Orders/?orderId=NYC:123
,则会使用正确填充的GetOrderDetails
实例调用操作OrderId
。
ASP.NET提供了另一个可扩展点,用于从URL绑定模型。它们是IModelBinder
和IValueProvider
接口的自定义实现。有关详细信息,请查看此article。
如果您无法为您不控制的类型设置类型转换器,则使用自定义模型绑定器的方法应该适合您。以下是用于自定义枚举值转换的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转换器的通用模型绑定器。