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



我正在为第三方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帖子,这可以很好地工作。设置了正常和异常属性,我可以继续我的业务。

然而,对于表单数据的任何变体(表单数据、多报警/表单数据、x-urlended-form-data),异常属性都不能正确地反序列化到模型中,并且将始终为null。

有没有我遗漏的指示?

我已经尝试过您的示例,我的结论是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();
}

我认为DataContract的主要问题是它需要像XML这样的原始数据。您尝试POST的是HTML。

数据协定序列化程序支持的类型

经过一番抨击,我们或多或少地做了两件事的结合。

对于url编码的表单,我们遵循了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);
            }
        }    
  }
}

最新更新