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, , 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
}

实例下载

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

  1. Idea_学习_03_IDEA中使自定义类型的文件进行代码高亮识别

    如果你只是想用xml的编辑模式来编辑*.screen文件的话,可以在 Settings->Editor->File Types 中,在Recognized File Types选中XML, ...

  2. HTTP请求中的Form Data与Request Payload的区别

    前端开发中经常会用到AJAX发送异步请求,对于POST类型的请求会附带请求数据.而常用的两种传参方式为:Form Data 和 Request Payload. GET请求 使用get请求时,参数会以 ...

  3. html5 file upload and form data by ajax

    html5 file upload and form data by ajax 最近接了一个小活,在短时间内实现一个活动报名页面,其中遇到了文件上传. 我预期的效果是一次ajax post请求,然后在 ...

  4. HTTP 请求中的 Form Data 与 Request Payload 的区别

    HTTP 请求中的 Form Data 与 Request Payload 的区别 前端开发中经常会用到 AJAX 发送异步请求,对于 POST 类型的请求会附带请求数据.而常用的两种传参方式为:Fo ...

  5. Sending forms through JavaScript[form提交 form data]

    https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript As in the ...

  6. sruts2 自定义类型转换器

    1.1.1    Struts2中自定义类型转换器:(了解) 类型转换的过程是双向的过程: JSP---->Action参数提交:String---Date. Action---->JSP ...

  7. asp.net query string 及 form data 遇到的编码问题

    当遇到此问题时,脑海里闪过的第一个解决方案是设置 web.config 的编码.但一想,就某一个页面的需求而导致其他跟着妥协,不是好的解决方案.于是网上搜索答案,下面做个小分享,遗憾的是研究不够深入, ...

  8. Qt自定义类型使用QHash等算法(Qt已经自定义了34种类型,包括int, QString, QDate等基本数据类型)

    自定义类型 #include <QCoreApplication> #include <QSet> #include <QDebug> class testCust ...

  9. [整理]Ajax Post请求下的Form Data和Request Payload

    Ajax Post请求下的Form Data和Request Payload 通常情况下,我们通过Post提交表单,以键值对的形式存储在请求体中.此时的reqeuest headers会有Conten ...

随机推荐

  1. 理解odbc

    1.解决什么样的问题?不同的数据库产品,具有不同的特性,也就是方言.因此应用程序针对不同的数据库产品,编写不同的代码.如果换了一个数据库产品,还需要重新编写数据库交互部分,不具备扩展和移植.odbc对 ...

  2. Javascript中typeof instanceof constructor的区别

    typeof typeof,是一个运算符,运算中需要一个操作数,运算的结果就是这个操作数的类型,运算的结果是一个字符串.他有一定的局限性,对于对象类型的值,只能得到一个object结果,却不能精确得到 ...

  3. js-------》(小效果)实现倒计时及时间对象

    js实现倒计时及时间对象 JS实现倒计时效果代码如下: 1 <!doctype html> 2 <html> 3 <head> 4 <meta charset ...

  4. js object(对象)

    http://www.cnblogs.com/pingchuanxin/p/5773326.html Object(对象)是在所有的编程语言中都十分重要的一个概念,对于事物我们可以把他们看作是一个对象 ...

  5. form属性method="get/post

    form属性method="get/post"的两种方式对比 在B/S应用程序中,前台与后台的数据交互,都是通过HTML中Form表单完成的.Form提供了两种数据传输的方式——g ...

  6. Sqlserver_insert语法

    1. INSERT INTO SELECT 通过 SQL,您可以从一个表复制信息到另一个表.   INSERT INTO SELECT 语句从一个表复制数据,然后把数据插入到一个已存在的表中. 我们可 ...

  7. fedora 关闭、禁止selinux

    Fedora关闭/禁用SELinux三种方法 在Fedora中有时候我们想关闭SELinux,因为有时候本是合法的操作也总是弹出窗口阻止我们的操作.下面介绍三种方法来关闭/禁用SELinux. 1.在 ...

  8. 注意64位整形,int64,long long

    在Xplorer中编译运行: int64_t  mask = 0xffffffffffffffffLL; if((j+64)>width) { printf("%x %d " ...

  9. 循环效率对比 js node c# mssql

  10. drupal 2016-11-3

    我随意定义了一个hook menu发现里面的内容很快就加入到了navigation menu里面.