asp_net_mvc4_web_api_的最新讲解极其详细介绍_第1页
asp_net_mvc4_web_api_的最新讲解极其详细介绍_第2页
asp_net_mvc4_web_api_的最新讲解极其详细介绍_第3页
asp_net_mvc4_web_api_的最新讲解极其详细介绍_第4页
asp_net_mvc4_web_api_的最新讲解极其详细介绍_第5页
已阅读5页,还剩13页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、/articles/eQzyEv不管是因为什么原因,结果是在新出的MVC中,增加了WebAPI,用于提供REST风格的WebService,个人比较喜欢REST风格的WebService,感觉比SOAP要轻量级一些,而且对客户端的要求也更少,更符合网络数据传输的一般模式,客户端完全摆脱了代理和管道来直接和WebService进行交互,具体的区别可以参见Web 服务编程,REST 与 SOAP(一)环境准备本机的环境是XP+VS2010,需要安装VS2010 SP1升级包,MVC4升级包,Vs2010安装SP1后会影响SQLServer2008的自动提示

2、功能,需要在安装补丁或插件,安装成功后可以新建如下的 MVC WebAPI 项目(二)概览新生成的WebAPI项目和典型的MVC项目一样,包含主要的Models,Views,Controllers等文件夹和Global.asax文件Views对于WebAPI来说没有太大的用途,Models中的Model主要用于保存Service和Client交互的对象,这些对象默认情况下会被转换为Json格式的数据进行传输,Controllers中的Controller对应于WebService来说是一个Resource,用于提供服务。和普通的MVC一样,Global.asax用于配置路由规则(三)Model

3、s和WCF中的数据契约形成鲜明对比的是,MVC WebAPI中的Model就是简单的POCO,没有任何别的东西,如,你可以创建如下的Model public class TestUseMode public string ModeKeyget;set; public string ModeValue get; set; 注意:Model必须提供public的属性,用于json或xml反序列化时的赋值(四)ControllersMVC WebAPI中的Controllers和普通MVC的Controllers类似,不过不再继承于Controller,而改为继承API的ApiController,

4、一个Controller可以包含多个Action,这些Action响应请求的方法与Global中配置的路由规则有关,在后面结束Global时统一说明(五)Global默认情况下,模板自带了两个路由规则,分别对应于WebAPI和普通MVC的Web请求,默认的WebAPI路由规则如下1 routes.MapHttpRoute(2 name: DefaultApi,3 routeTemplate: api/controller/id,4 defaults: new id = RouteParameter.Optional 5 );可以看到,默认路由使用的固定的api作为Uri的先导,按照微软官方的说

5、法,用于区分普通Web请求和WebService的请求路径:Note: The reason for using api in the route is to avoid collisions with ASP.NET MVC routing. That way, you can have /contacts go to an MVC controller, and /api/contacts go to a Web API controller. Of course, if you dont like this convention, you can change the default r

6、oute table.可以看到,默认的路由规则只指向了Controller,没有指向具体的Action,因为默认情况下,对于Controller中的Action的匹配是和Action的方法名相关联的:具体来说,如果使用上面的路由规则,对应下面的Controller: public class TestController : ApiController public static List allModeList = new List(); public IEnumerable GetAll() return allModeList; public IEnumerable GetOne(str

7、ing key) return allModeList.FindAll(mode) = if (mode.ModeKey.Equals(key) return true; return false; ); public bool PostNew(TestUseMode mode) allModeList.Add(mode); return true; public int Delete(string key) return allModeList.RemoveAll(mode) = if (mode.ModeKey = key) return true; return false; ); pu

8、blic int DeleteAll() return allModeList.RemoveAll(mode) = return true; ); public int PutOne(string key, string value) List upDataList = allModeList.FindAll(mode) = if (mode.ModeKey = key) return true; return false; ); foreach(var mode in upDataList) mode.ModeValue = value; return upDataList.Count; 则

9、,会有下面的对应关系:简单使用JS调用上面提供的数据接口 1 function getAll() 2 $.ajax( 3 url: api/Test/, 4 type: GET, 5 success: function (data) 6 document.getElementById(modes).innerHTML = ; 7 $.each(data, function (key, val) 8 var str = val.ModeKey + : + val.ModeValue; 9 $(, html: str ).appendTo($(#modes);10 );11 12 ).fail(1

10、3 function (xhr, textStatus, err) 14 alert(Error: + err);15 );16 17 18 19 20 function add() 21 22 $.ajax(23 url: api/Test/,24 type: POST,25 dataType: json,26 data: ModeKey: document.getElementById(txtKey).value, ModeValue: document.getElementById(txtValue).value ,27 success: function (data) 28 getAl

11、l();29 30 ).fail(31 function (xhr, textStatus, err) 32 alert(Error: + err);33 );34 35 36 37 function find() 38 39 $.ajax(40 url: api/Test/ + document.getElementById(txtFindKey).value,41 type: GET,42 success: function (data) 43 document.getElementById(modes).innerHTML = ;44 $.each(data, function (key

12、, val) 45 var str = val.ModeKey + : + val.ModeValue;46 $(, html: str ).appendTo($(#modes);47 );48 49 ).fail(50 function (xhr, textStatus, err) 51 alert(Error: + err);52 );53 54 55 function removeAll() 56 $.ajax(57 url: api/Test/,58 type: DELETE,59 success: function (data) 60 document.getElementById(

13、modes).innerHTML = ;61 getAll();62 63 ).fail(64 function (xhr, textStatus, err) 65 alert(Error: + err);66 );67 68 69 function remove() 70 $.ajax(71 url: api/Test/+document.getElementById(txtRemoveKey).value,72 type: DELETE,73 success: function (data) 74 document.getElementById(modes).innerHTML = ;75

14、 getAll();76 77 ).fail(78 function (xhr, textStatus, err) 79 alert(Error: + err);80 );81 82 83 function update() 84 $.ajax(85 url: api/Test/,86 type: PUT,87 dataType: json,88 data: key: document.getElementById(txtUpdateKey).value, value: document.getElementById(txtUpdateValue).value ,89 success: fun

15、ction (data) 90 document.getElementById(modes).innerHTML = ;91 getAll();92 93 ).fail(94 function (xhr, textStatus, err) 95 alert(Error: + err);96 );97 这样就实现了最基本的CRUD操作。(六)路由规则扩展和普通的MVC一样,MVC WebAPI支持自定义的路由规则,如:在上面的操作中,路由规则使用api/controller/id则限定了使用GET方式利用URL来传值时,controller后面的接收参数名为id,但是在Controller中,G

16、etOne方法的接收参数名为key,是不会被匹配的,这是只需要新增一个新的路由规则,或修改原先的路由规则为:api/controller/key当然,可以对路由进行更深的扩展,如:扩展成和普通MVC一样的路由:api/controller/action/id这样,就要求同时使用Action和HTTP方法进行匹配当然,根据微软的说法,这种使用是不被推荐的,因为这不符合大家对WebService的一般认知:For a RESTful API, you should avoid using verbs in the URIs, because a URI should identify a reso

17、urce, not an action.(七)使用Attribute声明HTTP方法有没有感觉默认的使用方法名来匹配HTTP Method的做法很傻?或者我有一些方法是自己用的,不想暴露出来,又该怎么办?还是使用attribute做这些工作感觉优雅一些,比如,上面的Action我可以更改为: HttpGet public IEnumerable FindAll() HttpGet public IEnumerable FindByKey(string key) HttpPost public bool Add(TestUseMode mode) HttpDelete public int Re

18、moveByKey(string key) HttpDelete public int RemoveAll() HttpPut public int UpdateByKey(string key, string value) NonAction public string GetPrivateData()当然,我只列出了方法名,而不是这些方法真的没有方法体.方法体是不变的,NoAction表示这个方法是不接收请求的,即使以GET开头。如果感觉常规的GET,POST,DELETE,PUT不够用,还可以使用AcceptVerbs的方式来声明HTTP方法,如: AcceptVerbs(MKCOL,

19、HEAD) public int UpdateByKey(string key, string value) List upDataList = allModeList.FindAll(mode) = if (mode.ModeKey = key) return true; return false; ); foreach(var mode in upDataList) mode.ModeValue = value; return upDataList.Count; *在上篇文章中和大家一起学习了建立基本的WebAPI应用,立刻就有人想到了一些问题:1.客户端和WebService之间文件传输

20、2.客户端或者服务端的安全控制要解决这些问题,要了解一下WebAPI的基本工作方式。(一)WebAPI中工作的Class在MVC中大家都知道,获取Request和Response使用HttpRequest和HttpResponse两个类,在WebAPI中使用两外两个类:HttpRequestMessage和HttpResponseMessage,分别用于封装Requset和Response。除了这两个类之外,还有一个常见的抽象 类:HttpMessageHandler,用于过滤和加工HttpRequestMessage和HttpResponseMessage(二)解决第一个问题其 实第一个问题

21、之所以被提出来应该是和客户端有关,如果客户端的请求是我们手写提交的,比如使用HttpClient封装的请求,则要传递文件之前,我们一 般会进行一次序列化,转化为二进制数组之类的,在网络上传输。这样的话,在Controller中的Action参数里,我们只需要接收这个二进制数组类 型的对象就可以了。但是如果客户端是Web Form呢,比如我们提交一个Form到指定的Controller的Action中,这个Action要接收什么类型的参数呢?或者我们问另外一个问题,如果我将Web Form提交到一个WebAPI的Action中 ,我要怎么去取出这个表单中的数据呢?其 实我们应该想到:我们的Act

22、ion设置的参数之所以能够被赋值,是因为WebAPI的架构中在调用Action时将HTTP请求中的数据解析出来分别赋 值给Action中的参数,如果真是这样的话,我们只需要在Action中获取到HTTP请求,然后直接获取请求里面的数据,就能解决上面的问题。这 种想法是正确的,只不过,此时的HTTP请求已经不是最原始的HTTP Request,而是已经被转化成了HttpRequestMessage,在Action中,我们可以直接调用base.Requet来得到这个HttpRequestMessage实例,通过这个实例我们就可以随心所欲的取出HTTP请求中想要的数据。2.1从RequestMess

23、age中获取普通表单数据这里的普通表单是指不包含File的表单,也就是说表单的enctype值不是multipart/form-data,这时,表单的数据默认情况下是以Json来传递的如下页面 捕获到的请求为提交到对应的Action为: HttpPost public async void submitForm() StringBuilder sb = new StringBuilder(); HttpContent content = Request.Content; JsonObject jsonValue = await content.ReadAsOrDefaultAsync(); f

24、oreach (var x in jsonValue) sb.Append(x.Key); string va ; if (x.Value.TryReadAs(out va) sb.Append(va); 这样最后可以得到 Json的值:key:123,value:123 sb处理后的值为:key123value123注:在该action中使用到了关键字async和await,这些在4.5中新提出的关键字主要是用于进行多线程取值的,在MVCAPI的设计中,大部分的方法都被设计成类似于下面的方法public static Task ReadAsOrDefaultAsync(this HttpCo

25、ntent content);返 回值是一个Task,这种返回新线程的方法虽然可以提高系统的响应能力,但是多线程取值会给编码带来不便,所以新出的关键字await用于阻塞当前线程并 获取目标线程的返回值,在方法体中使用await关键字后要求将方法声明为async用来表示该方法是异步的,并且返回值必须为void或者将返回者封装 在一个Task中当然,如果你不喜欢这种写法,上面的action也可以写为: Task readTask = content.ReadAsOrDefaultAsync().ContinueWith(task) = jsonValue = task.Result; ); rea

26、dTask.Wait();2.2从RequestMessage中获取multipart表单数据将view页面改写为 此时捕获到得请求是这里的文件内容被捕获软件解析成字符串,当然如果我上传的是其他的非文本格式的文件,文件会被转化为二进制数组这时如果我们不更改action,而直接调用,会发生错误,原因很明显,这个HTTP的报文内容是无法被转换为JSON的,这时我们需要将表单的报文解析成另外一种格式 IEnumerable bodyparts = await content.ReadAsMultipartAsync(); foreach (var bodypart in bodyparts) str

27、ing name; name = bodypart.Headers.ContentDisposition.Name; sb.Append(name + :); if (bodypart.Headers.Contains(filename) Stream stream = await bodypart.ReadAsStreamAsync(); StreamReader reader = new StreamReader(stream); sb.Append(reader.ReadToEnd(); sb.Append(-); else string val = await bodypart.Rea

28、dAsStringAsync(); sb.Append(val); sb.Append(-); 得到的处理后的sb值为:key:123-value:123-file:*文件的内容*-整合后的Action为 HttpPost public async void submitForm() StringBuilder sb = new StringBuilder(); HttpContent content = Request.Content; if (content.IsMimeMultipartContent() IEnumerable bodyparts = await content.Rea

29、dAsMultipartAsync(); foreach (var bodypart in bodyparts) string name; name = bodypart.Headers.ContentDisposition.Name; sb.Append(name + :); if (bodypart.Headers.Contains(filename) Stream stream = await bodypart.ReadAsStreamAsync(); StreamReader reader = new StreamReader(stream); sb.Append(reader.Rea

30、dToEnd(); sb.Append(-); else string val = await bodypart.ReadAsStringAsync(); sb.Append(val); sb.Append(-); else JsonObject jsonValue = await content.ReadAsOrDefaultAsync(); foreach (var x in jsonValue) sb.Append(x.Key); string va; if (x.Value.TryReadAs(out va) sb.Append(va); (三)WebAPI工作方式要想解决第二个问题就

31、没这么容易了,我们需要更深入的理解WebAPI的工作方式。其实对于WebAPI来说,它最初被设计为和WCF一样的:客户端、服务端两套结构,我们到现在之所以还没有提到客户端,是因为我们的请求别的方式来封装成HTTP请求或接收HTTP相应的,比如AJAX和Form表单提交。在这里先给出一个服务端的响应工作流,让大家有个大体上的认识(不得已在图片中加了水印,因为看到自己辛苦写的东西被人直接拿走,也不给出原文链接,心里真的不好受.希望不会影响大家的阅读.)由于图片大小限制,所有的HttpRequestMessage被简写为HttpRequestMsg,HttpResponseMessage被简写了Ht

32、tpResponseMsg大 家可以看到,HTTP的请求最先是被传递到HOST中的,如果WebAPI是被寄宿在IIS上的,这个HOST就是IIS上,HOST是没有能力也没有必 要进行请求的处理的,请求通过HOST被转发给了HttPServer此时已经进入WebAPI的处理加工范围,HttpServer是System.Net.HTTP中的一个类,通过HttpServer,请求被封装成了WebAPI中的请求承载 类:HttpRequestMessage,这个封装后的请求可以经过一系列自定义的Handler来处理,这些handler串联成一个pipeline,最后请求会被传递给HttpControl

33、Dispather,这个类通过对路由表的检索来确定请求将被转发到的具体的Controller中的Action。Client端的处理与服务端类似,直接上图:其实根据微软的说法,他们本身就被设计成类似但是可以独立运行的结构ASP.NET Web API has a pipeline for processing HTTP messages on both the client and server. The client and server sides are designed to be symmetrical but independent; you can use each half by

34、 itself. Both sides are built on some common objects: HttpRequestMessage represents the HTTP request. HttpResponseMessage represents the HTTP response. HttpMessageHandler objects process the request and response.直接看图,在客户端,Handlers pipeline最终是被传递到HttpClientHandler上的,由他负责HttpRequestMessage到HTTP请求的转换。这

35、里只说明一下Request,Response与其类似。(四)解决第二个问题由 此我们早就可以看出,想要解决第二个问题,可以直接在Handler PipeLine中进行,这种AOP风格的过滤器(拦截器)在REST的Webservice的安全验证中应用很广,一般大家比较乐于在HTTP头或者在HTTP请求的URL中加上身份验证字段进行身份验证,下面举一个在Http头中添加身份验证信息的小例子4.1客户端客户端的customhandler用于将身份验证信息添加入报头 class RequestUpHandler : DelegatingHandler protected override Task S

36、endAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) request.Headers.Add(key, 11234); return base.SendAsync(request, cancellationToken); 注:1.customhandler继承自DelegatingHandler类,上面已经说过,WebAPI的客户端和服务端被设计为相互对应的两套结构,所以不论是在客户端还是服务端,customhandler都是继承自DelegatingHandler类

37、2.DelegatingHandler的sendAsync方法便是处理请求和接受请求时会被调用的方法,该方法返回值是HttPResponseMessage,接收的值为HttpRequestMessage,符合我们的一般认知3.方法的最后,调用base.SendAsync是将Request继续向该pipeline的其他customHandler传递,并获取其返回值。由于该方法不包含Response的处理逻辑,只需直接将上一个CustomHandler的返回值直接返回客户端主程序 static void Main(string args) HttpClient client = new HttpClient(new RequestUpHandler() InnerHandler = new HttpClientHandler() ); HttpResponseMessage response = client.GetAsync(http:/localhost:60023/api/FormSubmit).Result; response.Content.ReadAsAsync().ContinueWith(str) = Console.WriteLine(str.Result); ); Console.Re

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论