Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# WebAPI中抽象动作参数的绑定_C#_Asp.net Web Api_Model Binding - Fatal编程技术网

C# WebAPI中抽象动作参数的绑定

C# WebAPI中抽象动作参数的绑定,c#,asp.net-web-api,model-binding,C#,Asp.net Web Api,Model Binding,我所处的情况是,我需要将传入的HTTP POST请求与正文中的数据绑定到具体类型,具体类型取决于数据中的ProductType分母。以下是我的Web API 2操作方法: [HttpPost, Route] public HttpResponseMessage New(ProductBase product) { // Access concrete product class... if (product is ConcreteProduct) // Do s

我所处的情况是,我需要将传入的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();
    }
}
差不多就是这样。现在,您可以发送以下请求:

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
,您可以执行相同的操作,并插入相应的序列化程序。

请求是如何编码的<代码>应用程序/json<代码>应用程序/xml
application/x-www-form-urlencoded
?application/json,但理想情况下它不应该依赖于请求编码。非常感谢Darin。我希望找到一个不特定于格式的解决方案,但我想这在WebAPI中是不可能的?很好的例子。然而,这种方法的一个缺点是文档工具(例如asp.NETWebAPI帮助页)只会公开抽象类的属性,并且api的使用者不会知道派生类型,除非他是同一个开发团队的成员。请求不能是强类型的。在这种方法中,我不能使用数据注释来验证模型。但我认为这主要归结为一个又一个折衷的决定
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"}