WebAPI在使用application/x-www-form-urlencoded进行序列化和反序列化时未使用DataMember名称。

10

已经花了几个小时尝试解决在处理HTTP POST表单请求(Content-Type: application/x-www-form-urlencoded)时被忽略的DataMemberAttribute中的Name属性的问题。

我正在使用运行在.NET 4.5上,由IIS托管的Microsoft.AspNet.WebApi 5.2.3应用程序。

我有这个模型(演示):

// library
public interface IPayload
{
    string DataId { get; set; }
    int RelationId { get; set; }
}

// web app project
[DataContract]
public class MyPayload : IPayload
{
    [Required]
    [DataMember(Name = "id")]
    public string DataId { get; set; }

    [Required]
    [DataMember(Name = "rel")]
    public int RelationId { get; set; }
}

那么我有一个控制器:

[HttpPost]
[Route("~/api/stuff")]
public async Task<HttpResponseMessage> DoMagic(MyPayload payload)
{
    // ... breakpoint
}

(请注意,我在控制器中真正使用的是模型类型而不仅仅是接口)


当我像这样发送数据:

curl -X POST --data '{"id":"foo","rel":1}' -H "Content-Type: application/json" -H "Content-Length: 20" http://localhost/api/stuff

我的模型被成功反序列化了。


然而,当我执行以下操作时:

curl --data "id=foo" --data "rel=1" http://localhost/api/stuff

......我得到了一个空模型 - 自定义名称被忽略,所有属性都具有默认值。

最后,当我像这样发送请求时:

curl --data "DataId=foo" --data "RelationId=1" http://localhost/api/stuff

...模型已正确序列化。


所以我在想,我做错了什么。我花了很多时间阅读,大多数情况下我发现都是关于缺少DataContractAttribute的问题,而这在我的情况下是存在的。

控制器参数前面的FromBody属性也没有改变任何东西。

在我的应用程序中,注册了以下格式化程序:

  • System.Net.Http.Formatting.JsonMediaTypeFormatter
  • System.Net.Http.Formatting.XmlMediaTypeFormatter
  • System.Net.Http.Formatting.FormUrlEncodedMediaTypeFormatter
  • System.Web.Http.ModelBinding.JQueryMvcFormUrlEncodedFormatter

只有最后两个包含application/x-www-form-urlencodedSupportedMediaTypes中。


你已经尝试过[FromUri]属性了吗? - boosts
说实话,我还没有内容,因为它实际上是在正文中 - 如果我尝试使用它,我实际上会得到“null”。我还尝试了ModelBinder(空的,因为我没有实现任何自定义绑定器),它也没有帮助我太多。此外,我不会这样做 - 对于JSON,它可以工作,那么为什么我要在这里实现绑定器呢? - Zdeněk
但是你说“处理HTTP POST表单请求(Content-Type:application/x-www-form-urlencoded)”? - boosts
抱歉造成困惑,我的意思是当我的操作处理程序“DoMagic”由于POST请求而被调用时。我期望在请求正文中收到数据(就像任何人从POST请求中所期望的一样,对吧?:)) - 所以基本上,我正在发送POST负载。如果我发送带有适当的“Content-Type”标头的JSON,则可以正常工作。但是,当我提交表单(“method = post”)时,它不起作用。 - Zdeněk
你的 RequestContext.RouteDataRequest.Content 是什么样子的?我不熟悉 curl,但是我在这里看到的与你的帖子有些不同。 - boosts
不太确定你的问题出在哪里:RouteData 指向正确的路由,HttpRouteValueDictionary 中没有值(这是正确的,在我的情况下没有查询字符串参数);Request.Content 设置了正确的头部 application/x-www-form-urlencoded,但是当我通过 Request.Content.ReadAsStringAsync() 读取它的内容时,得到的是空字符串。 - Zdeněk
1个回答

11
经过漫长的调试之后,我不得不自己解决问题。
由于我的模型并非派生自FormDataCollection或JS令牌类型,因此未使用System.Net.Http.Formatting.FormUrlEncodedMediaTypeFormatter。因此使用了格式化程序System.Web.Http.ModelBinding.JQueryMvcFormUrlEncodedFormatter
CompositeModelBinder中使用的绑定程序包括:
- TypeMatchModelBinder(跳过) - MutableObjectModelBinder(已使用)
我没有发现任何考虑DataMemberAttributeName属性的代码痕迹,因此我必须为我的类型实现自己的IModelBinder,以便进行自定义处理。
请注意:在序列化对象时,一切都按预期工作 - 上述问题仅与从请求正文反序列化有关。
来源:
- System.Net.Http.Formatting.Formatting - System.Web.Http.ModelBinding 希望我没有遗漏任何东西。如果有,请指出来。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接