问题

我们想创建一个能够处理 HTML表单的 ASP.NET Web API 应用程序(使用 application/x-www-form-urlencoded 方式提交数据)。

解决方案

我们可以创建一个Controller Action接收一个Moddel,Model的结构和准备从HTML表单提交的准备处理的结构相似,模型绑定依赖于ASP.NET Web API来处理。模型中的属性名字和HTTP请求中要用的名字要匹配。

publicHttpResponseMessagePost(RegistrationModelmodel){//忽略}

另外,我们可以使用 System.Net.Http.Formatting.FormDataCollection作为 Action 方法的唯一参数。框架将会通过键值对集合的方式传值,我们就可以自己处理我们想要的值。

publicHttpResponseMessagePost(FormDataCollectionform){//忽略}


工作原理

当使用 ASP.NET Web API 构建 web 应用程序时,为了提升现有 Web 应用程序(MVC,Web Form,或者其他技术)而使用 Web API 的时候,提交 form-URL-encoded 数据是一种很常见的需求。

ASP.NET Web API 使用 MediaTypeFormatters 从 HttpRequestMessage 表单中提取数据,并且,传输这些数据给相应选择的 action 来处理请求。第四章会详细介绍模型绑定和格式化,在这里,我们仅仅接触了直接与处理 HTML 表单相关的概念。

两个现成的格式化程序能够处理的格式:

FormUrlEncodedMediaTypeFormatter,用于绑定使用 application/x-www-form-urlencoded 内容类型的 FormDataCollection。

JQueryMvcFormUrlEncodedFormatter,也是使用同样的内容类型,也可以绑定到 Model(DTO)

从设计角度来看,后面的应该是前面的子类。

使用FormDataCollection 来代替我们 action 参数,他不仅让我们能够访问原始表单数据,还通知了 ASP.NET Web API 不执行任何验证。

默认情况,Web API 仅仅读请求体一次,当使用模型绑定表单数据时,这种模式应该封装了所有的表单域。换句话说,不能在传输数据的时候,即使用请求体传输数据,同时又使用 URL 参数形式传输数据,在这样的情况下,还希望框架自动处理模型绑定。对于 MVC 开发者这个情况就很痛苦,因为他们习惯了这样的开发方式。不过,我们还是可以强制 Web API 使用像 MVC 样式的参数绑定。这部分将在本书 4-4 部分讨论。

如果你的表单处理的时二进制数据,例如,上传文件。这时候,表单将被替换为 multipart/form-data 的形式提交。ASP.NET Web API 没有提供任何内建的MediaTypeFomatter 来处理二进制数据。不过,他还是很容易处理这种方式提交的表单。他是使用 MultipleFormDataStreamProvider 来直接处理 HttpRequestMessage 的内容。如清单 1-7 所示。尽管处理文件上传超出了我们介绍的范围,还是会在 4-11 单独讨论。


清单1-7. 接收复杂请求的表单数据

publicasyncTaskPost(){if(!Request.Content.IsMimeMultipartContent()){thrownewHttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable,"请求格式化失败"));}varstreamProvider=newMultipartFormDataStreamProvider("d:/uploads/");awaitRequest.Content.ReadAsMultipartAsync(streamProvider);//访问FormDataCollection的实例streamProvider.FormData}

注意 在这一部分讨论的功能不是 ASP.NET 特有的,WebAPI 之外一样可以用。不过,作为 ASP.NET Web 应用程序的一部分来运行 Web API 的时候,我们通常还是要处理传统的网页表单。

代码演示

清单 1-8 展示了使用简单表单和JavaScript 提交到 ASP.NET Web API 的简单网页表单.

清单 1-8. 简单的网页表单

<formrole="form"method="post"action="/api/form"enctype="application/x-www-form-urlencoded"><divclass="form-group"><labelfor="name">Name</label><inputtype="text"class="form-control"name="name"placeholder="Entername"></div><divclass="form-group"><labelfor="email">Email</label><inputtype="email"class="form-control"name="email"placeholder="Enteremail"></div><divclass="radio"><label><inputtype="radio"name="gender"value="female"checked>Female</label></div><divclass="radio"><label><inputtype="radio"name="gender"value="male">Male</label></div><buttontype="submit"class="btnbtn-default">提交</button><buttonid="postJS"class="btnbtn-default">使用JS提交</button></form><scripttype="text/javascript">$(function(){$("#postJS").on("click",function(){vardata={name:$("input[name='name']").val(),email:$("input[name='email']").val(),gender:$("input[name='gender']:checked").val(),};$.ajax({data:data,datatype:"html",type:"POST",url:"/api/user"}).done(function(res){//处理handler});});});</script>

清单 1-9 展示了可以处理清单 1-8 表单的两个 ASP.NET Web API action。第一个是是使用相对传统的方式,使用 FormDataCollection 和 手动提取数据,然后在填充到服务器的模型里面。第二个依依赖与框架的自动模型绑定。

清单 1-9. Web API 控制器处理表单数据

publicasyncTaskPost(){if(!Request.Content.IsMimeMultipartContent()){thrownewHttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable,"请求格式化失败"));}varstreamProvider=newMultipartFormDataStreamProvider("d:/uploads/");awaitRequest.Content.ReadAsMultipartAsync(streamProvider);//访问FormDataCollection的实例streamProvider.FormData}publicclassUserModel{publicstringName{get;set;}publicstringEmail{get;set;}publicstringGender{get;set;}}publicclassFormController:ApiController{publicHttpResponseMessagePost(FormDataCollectionform){varuser=newUserModel{Email=form["Email"],Name=form["Name"],Gender=form["Gender"]};//处理用户...//忽略}}publicclassUserController:ApiController{publicHttpResponseMessagePost(UserModeluser){//processuser...//restomittedforbrevity}}