在WebAPI中绑定抽象动作参数

2023-11-25

我遇到的情况是,我需要将传入的 HTTP POST 请求与正文中的数据绑定到具体类型,具体取决于ProductType数据中的分母。这是我的 Web API 2 操作方法:

[HttpPost, Route]
public HttpResponseMessage New(ProductBase product)
{
    // Access concrete product class...

    if (product is ConcreteProduct)
        // Do something
    else if (product is OtherConcreteProduct)
        // Do something else
}

我首先考虑使用自定义模型绑定器,但它似乎此时无法访问请求正文:

对于复杂类型,Web API 尝试从消息中读取值 正文,使用媒体类型格式化程序。

我真的不明白媒体类型格式化程序如何解决这个问题,但我可能错过了一些东西。你会如何解决这个问题?


根据请求内容类型,您必须决定要实例化哪个具体类。让我们举个例子application/json。对于这种开箱即用的内容类型,Web API 使用 JSON.NET 框架将请求正文有效负载反序列化为具体对象。

因此,您必须连接到该框架才能实现所需的功能。这个框架中一个很好的扩展点是编写一个自定义的JsonConverter。假设您有以下课程:

public abstract class ProductBase
{
    public string ProductType { get; set; }
}

public class ConcreteProduct1 : ProductBase
{
    public string Foo { get; set; }
}

public class ConcreteProduct2 : ProductBase
{
    public string Bar { get; set; }
}

以及以下操作:

public HttpResponseMessage Post(ProductBase product)
{
    return Request.CreateResponse(HttpStatusCode.OK, product);
}

让我们编写一个自定义转换器来处理这种类型:

public class PolymorphicProductConverter: JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(ProductBase);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var obj = JObject.Load(reader);
        ProductBase product;
        var pt = obj["productType"];
        if (pt == null)
        {
            throw new ArgumentException("Missing productType", "productType");
        }

        string productType = pt.Value<string>();
        if (productType == "concrete1")
        {
            product = new ConcreteProduct1();
        }
        else if (productType == "concrete2")
        {
            product = new ConcreteProduct2();
        }
        else
        {
            throw new NotSupportedException("Unknown product type: " + productType);
        }

        serializer.Populate(obj.CreateReader(), product);
        return product;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

最后一步是在WebApiConfig:

config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(
    new PolymorphicProductConverter()
);

差不多就是这样了。现在您可以发送以下请求:

POST /api/products HTTP/1.1
Content-Type: application/json
Host: localhost:8816
Content-Length: 39

{"productType":"concrete2","bar":"baz"}

服务器将正确反序列化该消息并响应:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
Date: Sat, 25 Jan 2014 12:39:21 GMT
Content-Length: 39

{"Bar":"baz","ProductType":"concrete2"}

如果您需要处理其他格式,例如application/xml您可以执行相同的操作并插入相应的序列化器。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在WebAPI中绑定抽象动作参数 的相关文章

随机推荐