在POST中从表单序列化备用属性名称

时间:2014-10-18 14:27:59

标签: c# asp.net asp.net-web-api

我正在为第三方API创建一个回调端点。 调用者将POST多部分表单数据发送给它。

这样的事情:

public SomeApiController : ApiController {
  public AModel Post(AModel input) {
    return input; //for demonstration
  }
}

它发布的一些字段在名称中有破折号,它不能是实际的.NET属性名称。因此,我使用[DataContract]和[DataMember(name =“blah”)]来定义序列化规则。

型号:

//input model class
[DataContract]
public class AModel {
  [DataMember]
  public string NormalProperty {get; set;} //value set appropriately
  [DataMember(Name="abnormal-property")]
  public string AbnormalProperty {get; set;} //always null (not serializing)
}

使用标准的XML和JSON帖子,这很好用。设置正常和异常属性,我可以继续我的业务。

但是,对于表单数据的任何变体(form-data,multiplart / form-data,x-urlencoded-form-data,AbnormalProperty都没有正确地反序列化到模型中,并且始终为null。

是否有指令我缺少什么?

3 个答案:

答案 0 :(得分:4)

我试过你的例子,我的结论是ASP.NET MVC中的DefaultModelBinder不支持POST变量的命名。

一个明显的解决方案是不要在你的名字中使用破折号。

如果这不是一个选项,那么您可以为该特定模型实现自己的模型绑定器,以处理发送到MVC控制器的异常名称。以下是自定义模型绑定器的示例:

public class AModelDataBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType == typeof(AModel))
        {
            var request = controllerContext.HttpContext.Request;

            string normal = request.Form.Get("normalproperty");
            string abnormal = request.Form.Get("abnormal-property");

            return new AModel
            {
                NormalProperty = normal,
                AbnormalProperty = abnormal
            };
        }

        return base.BindModel(controllerContext, bindingContext);
    }
}

然后您必须在 Global.asax

中注册此自定义活页夹
ModelBinders.Binders.Add(typeof(AModel), new AModelDataBinder());

最后,您可以在控制器中使用自定义活页夹:

[HttpPost]
public ActionResult Index([ModelBinder(typeof(AModelDataBinder))]AModel input)
{
    // Handle POST

    return View();
}

答案 1 :(得分:1)

我认为主要问题是DataContract它期望原始数据如XML。您尝试POST的是HTML。

Types Supported by the Data Contract Serializer

答案 2 :(得分:0)

经过大量的抨击,我们或多或少地做了两件事的组合。

对于网址编码的表单,我们遵循The Zen Coder的示例,它可以完美运行。

但是,对于流式传输到服务器的传统多部分表单(它们可以包含文件数据),我们创建了一个简单的解决方案来从请求中读取键/值,然后手动序列化。

在我们的特定用例中,我们不必担心文件数据或任何内容,因此我们可以假定所有内容都是字符串并从那里开始进行序列化工作。但是,为实际文件添加ContentDisposition标头的简单检查可能是明智的。

示例:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;

namespace WebApplication1.Controllers
{
    public class ValuesController : ApiController
    {
        public async Task<Dictionary<string,string>> Post()
        {
            if (!Request.Content.IsMimeMultipartContent())
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
            var provider = new MultipartMemoryStreamProvider();
            var formData = new Dictionary<string,string>();
            try
            {
                await Request.Content.ReadAsMultipartAsync(provider);
                foreach (var item in provider.Contents)
                {
                    formData.Add(item.Headers.ContentDisposition.Name.Replace("\"",""), await item.ReadAsStringAsync());
                }

                return formData;
            }
            catch (Exception e)
            {
                throw new HttpResponseException(HttpStatusCode.InternalServerError);
            }
        }    
  }
}