• 编码为multipart/form-data自定义类型(包括文件)如何自动绑定到webapi的action的参数里


    application/x-www-form-urlencoded与 multipart/form-data:

      Fom表单中如果没有type=file的控件,用默认的application/x-www-form-urlencoded就可以了。但是如果有type=file的话,就要用到multipart/form-data了。浏览器会把整个表单以控件为单位分割,并为每个部分加上 Content-Disposition(form-data或者file),Content-Type(默认为text/plain),name(控件 name)等信息,并加上分割符(boundary)

     

     

    multipart/form-data被webapi能够识别,需要自定义MediaTypeFormatter,关于webapi中的媒体类型格式化器(Media-type Formatter),它是一种能够做以下工作的对象:

    • Read CLR objects from an HTTP message body
      从HTTP消息体读取CLR(公共语言运行时)对象
    • Write CLR objects into an HTTP message body
      将CLR对象写入HTTP消息体

    Web API提供了用于JSON和XML的媒体类型格式化器。框架已默认将这些格式化器插入到消息处理管线之中。客户端在HTTP请求的Accept报头中可以请求JSON或XML。

    以下代码是multipart/form-data的格式化方法:

    using System;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Formatting;
    using System.Net.Http.Headers;
    using System.Threading.Tasks;
    using MultipartDataMediaFormatter.Converters;
    using MultipartDataMediaFormatter.Infrastructure;
    using MultipartDataMediaFormatter.Infrastructure.Logger;
    
    namespace MultipartDataMediaFormatter
    {
        public class FormMultipartEncodedMediaTypeFormatter : MediaTypeFormatter
        {
            private const string SupportedMediaType = "multipart/form-data";
    
            public FormMultipartEncodedMediaTypeFormatter()
            {
                SupportedMediaTypes.Add(new MediaTypeHeaderValue(SupportedMediaType));
            }
    
            public override bool CanReadType(Type type)
            {
                return true;
            }
    
            public override bool CanWriteType(Type type)
            {
                return true;
            }
    
            public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
            {
                base.SetDefaultContentHeaders(type, headers, mediaType);
    
                //need add boundary
                //(if add when fill SupportedMediaTypes collection in class constructor then receive post with another boundary will not work - Unsupported Media Type exception will thrown)
                if (headers.ContentType == null)
                    headers.ContentType = new MediaTypeHeaderValue(SupportedMediaType);
                
                if (!String.Equals(headers.ContentType.MediaType, SupportedMediaType, StringComparison.OrdinalIgnoreCase))
                    throw new Exception("Not a Multipart Content");
                
                if (headers.ContentType.Parameters.All(m => m.Name != "boundary"))
                    headers.ContentType.Parameters.Add(new NameValueHeaderValue("boundary", "MultipartDataMediaFormatterBoundary1q2w3e"));
            }
    
            public override async Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content,
                                                                   IFormatterLogger formatterLogger)
            {
                var httpContentToFormDataConverter = new HttpContentToFormDataConverter();
                FormData multipartFormData = await httpContentToFormDataConverter.Convert(content);
    
                IFormDataConverterLogger logger;
                if (formatterLogger != null)
                    logger = new FormatterLoggerAdapter(formatterLogger);
                else 
                    logger = new FormDataConverterLogger();
    
                var dataToObjectConverter = new FormDataToObjectConverter(multipartFormData, logger);
                object result = dataToObjectConverter.Convert(type);
    
                logger.EnsureNoErrors();
    
                return result;
            }
    
            public override async Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content,
                                                    TransportContext transportContext)
            {
                    if (!content.IsMimeMultipartContent())
                        throw new Exception("Not a Multipart Content");
    
                    var boudaryParameter = content.Headers.ContentType.Parameters.FirstOrDefault(m => m.Name == "boundary" && !String.IsNullOrWhiteSpace(m.Value));
                    if (boudaryParameter == null)
                        throw new Exception("multipart boundary not found");
                    
                    var objectToMultipartDataByteArrayConverter = new ObjectToMultipartDataByteArrayConverter();
                    byte[] multipartData = objectToMultipartDataByteArrayConverter.Convert(value, boudaryParameter.Value);
    
                    await writeStream.WriteAsync (multipartData, 0, multipartData.Length);
                    
                    content.Headers.ContentLength = multipartData.Length;
            }
        }
    }

    以IIs为宿主的webapi,加入以下代码

    GlobalConfiguration.Configuration.Formatters.Add(new FormMultipartEncodedMediaTypeFormatter());    

    如果webapi是自我宿主,加入以下代码

    new HttpSelfHostConfiguration(<url>).Formatters.Add(new FormMultipartEncodedMediaTypeFormatter());      

    发送对象时使用FormMultipartEncodedMediaTypeFormatter(测试代码如下)

    private ApiResult<T> PostModel<T>(T model, string url)
    {
     var mediaTypeFormatter = new FormMultipartEncodedMediaTypeFormatter();
     using (new WebApiHttpServer(BaseApiAddress, mediaTypeFormatter))
     using (var client = CreateHttpClient(BaseApiAddress))
     using (HttpResponseMessage response = client.PostAsync(url, model, mediaTypeFormatter).Result)
     {
      if (response.StatusCode != HttpStatusCode.OK)
      {
        var err = response.Content.ReadAsStringAsync().Result;
        Assert.Fail(err);
      }
    
      var resultModel = response.Content.ReadAsAsync<ApiResult<T>>(new[] { mediaTypeFormatter }).Result;
      return resultModel;
      }
    }

    使用MultipartDataMediaFormatter.Infrastructure.FormData这个类访问原始http数据

    [HttpPost]
    public void PostFileBindRawFormData(MultipartDataMediaFormatter.Infrastructure.FormData formData)
      {
          HttpFile file;
          formData.TryGetValue(<key>, out file);
      }

    绑定自定义Model

    //model example
    public class PersonModel
    {
       public string FirstName {get; set;}
    
       public string LastName {get; set;}
    
       public DateTime? BirthDate {get; set;}
    
       public HttpFile AvatarImage {get; set;}
    
       public List<HttpFile> Attachments {get; set;}
    
       public List<PersonModel> ConnectedPersons {get; set;}
    }
    
    //api controller example
    [HttpPost]
    public void PostPerson(PersonModel model)
    {
       //do something with the model
    }

     实例下载

  • 相关阅读:
    哈尔滨理工大学第六届程序设计团队 H-Permutation
    哈尔滨理工大学第六届程序设计团队 E-Mod
    哈尔滨理工大学第六届程序设计团队 I-Team
    HDU Today
    最短路
    Pseudoforest(伪最大生成树)
    Connect the Cities(prim)用prim都可能超时,交了20几发卡时过的
    Jungle Roads(最小生成树)
    linux读取yaml文件的某个属性值
    dos查看电脑配置
  • 原文地址:https://www.cnblogs.com/kingCpp/p/4901268.html
Copyright © 2020-2023  润新知