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. js学习笔记-编写高效、规范的js代码-Tom

    编写高效.规范的js代码: 1.变量命名空间问题,尽量使用局部变量,防止命名冲突(污染作用域中的全局变量):全局空间命名的变量可以在对应的文档域任意位置中使用window调用. 2.尽量使用单var定 ...

  2. VNC的安装与配置

    一,安装tigervnc-server VNC软件包 [root@localhost ~]# yum install tigervnc-server 设置开机自启动 [root@localhost ~ ...

  3. JavaSE复习_2 对象与类

    △java中的制表符.'\t'制表符."\t"也可以. △方法内不能再定义一个方法,互相平级. △数组中boolean类型的变量默认为false;char默认为'\u0000'(\ ...

  4. Manacher 算法(hdu 3068 && hdu 3294)

    今天打算补前晚 BC 的第二题,发现要用到能在 O(n) 时间求最大回文子串长度的 Manacher 算法,第一次听,于是便去百度了下,看了大半天,总算能看懂了其思想,至于他给出的代码模板我没能完全看 ...

  5. golang文件上传和下载

    [代码]golang 实现的文件服务(包括上传,下载的server端和client端) (2013-09-20 02:03:52) 转载▼ 标签: golang go 文件服务器 it 分类: GO相 ...

  6. OpenGL的glRotatef旋转变换函数详解

    OpenGL的glRotatef旋转变换函数详解 先看一下函数定义:void glRotatef(GLfloat angle,  GLfloat x,     GLfloat y,     GLflo ...

  7. 微信jssdk

    http://203.195.235.76/jssdk/?from=timeline&isappinstalled=0

  8. commonJS — 通用方法(for COM)

    for COM github: https://github.com/laixiangran/commonJS/blob/master/src/forCOM.js 代码 /** * Created b ...

  9. mysql spider之拆库无忧

    数据库的三板斧 先上MySQL,之后再上读写分离,然后呢? 后面典型的做法是垂直拆库和水平分表. 一旦数据库拆了之后,代价就来了. 1.事务不能跨库了(少,但是很重要,可以适当改写) 2.相关的关联查 ...

  10. 在线读取Mongodb数据库下载EXCEL文件

    版本:Mongodb2.4.8 通过页面下载Excel文件 jsp <%@ page language="java" contentType="text/html; ...