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. apt-get的常用用法

    我们装完linux后的第一件事情就是安装软件了,下面的命令可以帮助你在Ubuntu发行版或基于Debain的发行版上快速的安装软件: sudo apt-get install package-name ...

  2. MATLAB中的nargin与varargin的用法

    nargin的用法: nargin:number of function input arguments,指的是一个函数的输入变量的个数. 用法:nargin或着nargin(fx), 其中fx指的是 ...

  3. Java源码初学_AbstractList&AbstractCollection

    一.AbstractCollection抽象类:(提供了Collection接口的骨干实现,以减少实现接口所需要的工作) 1.contains方法 contains方法,通过迭代器对于列表的每一个元素 ...

  4. 算法_栈的Java的通用数组实现

    栈是一个常用的最简单的数据结构,这里提供了其实现.内部维护了一个数组,并且可以动态的调整数组的大小.而且,提供了迭代器支持后进先出的迭代功能.Stack的实现是所有集合类抽象数据类型实现的模板,它将所 ...

  5. (一)S5PV210开发板常用易忘操作记录

    一.调试串口 2.SD卡槽 (三)启动方式选择 蜂鸣器下面的白色2针插座(图中红色线圈出来的那个)为选择USB/SD卡启动的开关.默认情况下为SD卡启动,如果需要USB启动则使用短路帽(若没有短路帽, ...

  6. arr.sort()排序方法

    <!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8&qu ...

  7. hdu 5755(高斯消元——模线性方程组模板)

    PS. 看了大神的题解,发现确实可以用m个未知数的高斯消元做.因为确定了第一行的情况,之后所有行的情况都可以根据第一行推. 这样复杂度直接变成O(m*m*m) 知道了是高斯消元后,其实只要稍加处理,就 ...

  8. python模块以及导入出现ImportError: No module named 'xxx'问题

    python中,每个py文件被称之为模块,每个具有__init__.py文件的目录被称为包.只要模块或者包所在的目录在sys.path中,就可以使用import 模块或import 包来使用如果你要使 ...

  9. scala调用java的方法,返回了一个对象链表List<Student>,在scala中遍历该链表获取指定Student的名字name

    假设Student类如下: class Student { private int no; private String name; public int getNo() { return no; } ...

  10. 显示win7桌面网络.reg

    显示win7桌面网络.reg Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\C ...