标签:style blog http color io os ar 使用 java
Web API 是ASP.NET平台新加的一个特性,它可以简单快速地创建Web服务为HTTP客户端提供API。Web API 使用的基础库是和一般的MVC框架一样的,但Web API并不是MVC框架的一部分,微软把Web API相关的类从 System.Web.Mvc 命名空间下提取了出来放在 System.Web.Http 命名空间下。这种理念是把 Web API 作为ASP.NET 平台的核心之一,以使Web API能使用在其他的Web应用中,或作为一个独立的服务引擎。本文将先带大家理解Web API,再教大家在MVC中使用Web API。
本文目录
为了更好的理解Web API,先带大家了解一下 REST 和 RESTful Web API。以下内容大多来自维基百科。
REST(全名:Representational State Transfer),中文翻译是表征状态转移(也有叫表述性状态转移),是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。
REST 从资源的角度来观察整个网络,分布在各处的资源由URI确定,而客户端的应用通过URI来获取资源的表征。获得这些表征致使这些应用程序转变了其状态。随着不断获取资源的表征,客户端应用不断地在转变着其状态,所谓表征状态转移(Representational State Transfer)。
目前使用Web服务的三种主流的方式是:远程过程调用(RPC),面向服务架构(SOA)以及表征性状态转移(REST),其中REST模式的Web服务与复杂的SOA和RPC对比来讲显的更加简洁,越来越多的web服务开始采用REST风格设计和实现。
需要注意的是,REST是设计风格而不是标准,但REST设计风格常基于使用HTTP,URI,和XML以及HTML这些现有的广泛流行的协议和标准。REST设计风格有如下要点:
另外,使用REST需要满足一些要求,如客户端和服务器结构、连接协议具有无状态性、能够利用Cache机制增进性能等。
RESTful Web API(也称为RESTful Web服务)是一个使用HTTP并遵循REST原则的Web服务。它从以下请求资源的三个方面进行定义:
本文要讲的ASP.NET Web API 就是RESTful Web API的一种。下表列出了在实现 RESTful Web API 时HTTP请求方法的典型用途:
不像基于SOAP的Web服务,RESTful Web服务并没有“正式”的标准。这是因为REST是一种架构,而SOAP只是一个协议。虽然REST不是一个标准,但在实现RESTful Web服务时可以使用其他各种标准(比如HTTP,URL,XML,PNG等)。
那么REST和本文要讲的ASP.NET API又有什么关系呢?请继续往下阅读。
ASP.NET Web API(本文简称Web API),是基于ASP.NET平台构建RESTful应用程序的框架。可以说 Web API 就是为在.NET平台下构建RESTful应用程序而生的,这也是本文要先介绍REST的原因。
Web API基于在 MVC 应用程序中添加的一个特殊的 Controller,这种 Controller 称为 API Controller,和MVC普通的 Controller 相比它主要有如下两个不同的特点:
第一个不同点很好理解,第二个不同点可能读者不太理解,一会看完本文的示例就理解了。
从API Controller的Action方法返回给客户端的Model对象是经过JSON编码的。API Controller的设计仅是为了提供传递Web数据的服务,因此它不支持View、Layout 和其它HTML呈现相关的特性。Web API 能支持任何有Web 功能的客户端,但最常用的是为Web应用程序中的Ajax请求提供服务。
正如在 ASP.NET MVC 小牛之路]14 - Unobtrusive Ajax 中演示的,我们也可以通过普通的Controller中创建Action方法来返回JSON格式的数据来支持Ajax,但 API controller 的方式可以使得数据相关的Action和View相关的Action分开,并且使得创建只为数据服务的应用更快更简单。
一般我们会在下面这两种情况下选择使用API Controler:
如果你对上面这些概念还不太理解,没关系,当你阅读完本文后再回头看一下,你会对这些概念理解得更深些。
下面我会通过例子来解释Web API是如何工作的,它非常简单,因为很多东西都和我们之前讲过的MVC的东西相同。
作为本文的示例,我们创建一个名为 WebServices 的MVC应用程序,选择Web API模板。在本文的这个例子中,我们创建一个名为 Reservation 的Model,代码如下:
namespace WebServices.Models { public class Reservation { public int ReservationId { get; set; } public string ClientName { get; set; } public string Location { get; set; } } }
为这个Model创建一个Repository 接口和它的一个简单的实现。如果你对 Repository 这个词不太理解,可以阅读:[ASP.NET MVC 小牛之路]05 - 使用 Ninject 。为了简单,我们直接在Models文件夹中添加一个名为 IReservationRepository 的接口,代码如下:
namespace WebServices.Models { public interface IReservationRepository { IEnumerable<Reservation> GetAll(); Reservation Get(int id); Reservation Add(Reservation item); void Remove(int id); bool Update(Reservation item); } }
创建一个名为 ReservationRepository 的类,实现 IReservationRepository 接口,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace WebServices.Models { public class ReservationRepository : IReservationRepository { private List<Reservation> data = new List<Reservation> { new Reservation {ReservationId = 1, ClientName = "Adam", Location = "London"}, new Reservation {ReservationId = 2, ClientName = "Steve", Location = "New York"},new Reservation {ReservationId = 3, ClientName = "Jacqui", Location = "Paris"}, }; private static ReservationRepository repo = new ReservationRepository(); public static IReservationRepository getRepository() { return repo; } public IEnumerable<Reservation> GetAll() { return data; } public Reservation Get(int id) { var matches = data.Where(r => r.ReservationId == id); return matches.Count() > 0 ? matches.First() : null; } public Reservation Add(Reservation item) { item.ReservationId = data.Count + 1; data.Add(item); return item; } public void Remove(int id) { Reservation item = Get(id); if (item != null) { data.Remove(item); } } public bool Update(Reservation item) { Reservation storedItem = Get(item.ReservationId); if (storedItem != null) { storedItem.ClientName = item.ClientName; storedItem.Location = item.Location; return true; } else { return false; } } } }
简单起见,我们这里没有真正实现数据持久化,只是简单的模拟。
在我们创建好Web API应用程序时,VS已经添加好了一个默认的HomeController和Index.cshtml View。我们不打算在HomeControllerr 的Action中传递Model给View,因为一会要在View中使用JavaScript调用Web API来获取所有数据。在一个MVC工程中,你可以自由混合地使用普通Controller和API Controller。
修改 Index.cshtml 如下:
@{ ViewBag.Title = "Index";} @section scripts { <script src="~/Scripts/jquery.unobtrusive-ajax.js"></script> } <div id="summaryDisplay" class="display"> <h4>Reservations</h4> <table> <thead> <tr> <th class="selectCol"></th> <th class="nameCol">Name</th> <th class="locationCol">Location</th> </tr> </thead> <tbody id="tableBody"> <tr><td colspan="3">The data is loading</td></tr> </tbody> </table> <div id="buttonContainer"> <button id="refresh">Refresh</button> <button id="add">Add</button> <button id="edit">Edit</button> <button id="delete">Delete</button> </div> </div> <div id="addDisplay" class="display"> <h4>Add New Reservation</h4> @{ AjaxOptions addAjaxOpts = new AjaxOptions { // options will go here }; } @using (Ajax.BeginForm(addAjaxOpts)) { @Html.Hidden("ReservationId", 0) <p><label>Name:</label>@Html.Editor("ClientName")</p> <p><label>Location:</label>@Html.Editor("Location")</p> <button type="submit">Submit</button> } </div> <div id="editDisplay" class="display"> <h4>Edit Reservation</h4> <form id="editForm"> <input id="editReservationId" type="hidden" name="ReservationId"/> <p><label>Name:</label><input id="editClientName" name="ClientName" /></p> <p><label>Location:</label><input id="editLocation" name="Location" /></p> </form> <button id="submitEdit" type="submit">Save</button> </div>
并把 _Layout.cshtml 中的一些没用的内容清理一下,删除 /Content/Site.css 中的样式后添加如下样式代码:
table { margin: 10px 0;} th { text-align: left;} .nameCol {width: 100px;} .locationCol {width: 100px;} .selectCol {width: 30px;} .display { float: left; border: thin solid black; margin: 10px; padding: 10px;} .display label {display: inline-block;width: 100px;}
到这,我们的程序运行起来是这样的:
接下来我们需要创建一个API Controller,通过它我们可以用JavaScript代码和Repository的内容进行交互。
右击 Controllers 文件夹,选择“添加”--"控制器",在弹出的对话框中修改Controller的名称为 ReservationController,并从模板中选择 Empty API。添加好后,修改 ReservationController 如下:
using System.Collections.Generic; using System.Web.Http; using WebServices.Models; namespace WebServices.Controllers { public class ReservationController : ApiController { IReservationRepository repo = ReservationRepository.getRepository(); public IEnumerable<Reservation> GetAllReservations() { return repo.GetAll(); } public Reservation GetReservation(int id) { return repo.Get(id); } public Reservation PostReservation(Reservation item) { return repo.Add(item); } public bool PutReservation(Reservation item) { return repo.Update(item); } public void DeleteReservation(int id) { repo.Remove(id); } } }
ReservationController的基类是 System.Web.Http.ApiController,也实现了 IController 接口(在[ASP.NET MVC 小牛之路]09 - Controller 和 Action (1)中有介绍),它和普通Controller的基类System.Web.Mvc.Controller 处理请求的方式基本上是一样的。
到这我们就已经创建好了一个Web API了。
我们先来看看创建好的 API Controller 能否工作,下文将通过 API Controller 对数据的处理结果来解释API Controller 如何工作。
运行程序,URL定位到 /api/reservation。你看到的结果将依赖于你所使用的版本,如果你使用的是IE 10/11,会弹出一个提示保存文件的对话框,该文件的内容是以下JSON数据:
[{"ReservationId":1,"ClientName":"Adam","Location":"London"}, {"ReservationId":2,"ClientName":"Steve","Location":"New York"}, {"ReservationId":3,"ClientName":"Jacqui","Location":"Paris"}]
如果你使用的是另外一种浏览器,如Chrome或Firefox,浏览器将显示如下XML数据:
<ArrayOfReservation> <Reservation> <ClientName>Adam</ClientName> <Location>London</Location> <ReservationId>1</ReservationId> </Reservation> <Reservation> <ClientName>Steve</ClientName> <Location>New York</Location> <ReservationId>2</ReservationId> </Reservation> <Reservation> <ClientName>Jacqui</ClientName> <Location>Paris</Location> <ReservationId>3</ReservationId> </Reservation> </ArrayOfReservation>
对于看到的结果,我们有两点感兴趣。第一,我们请求 /api/reservation URL时,服务器返回了Model对象的列表,根据该列表的数据,我们可以推断调用的是ReservationController中的 GetAllReservations Action方法。第二,不同的浏览器接收到了不同格式的数据,我们可以猜测是由于不同版本的浏览器发送请求的方式不一样。
实际上,之所以会有不同格式的数据结果,是因为 Web API 使用HTTP请求报文头部的Accept信息来判断客户端更愿意接收何种类型的数据。IE 接收到JSON格式的数据是因为它发送的Accept头部信息是:
...
Accept: text/html, application/xhtml+xml, */*
...
浏览器通过这段报文信息告诉服务器它最想要的是 text/html 内容,其次是 application/xhtml+xml。最后的 */* 意思是如果前两种不满足,就接收任何类型的数据。
Web API 支持JSON和XML,但它会优先选择JSON格式。即IE的发送Accept信息中的 */* 使得Web API生成了JSON格式的数据。下面是 Chrome 浏览器发送的Accept头部信息:
...
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
...
这段信息的 application/xml 比 */* 优先级高,所以Web API选择为Chrome浏览器返回XML格式的数据。
对于Web服务,JSON已经开始大幅度替代XML了,因为XML冗长难以处理,尤其是在JavaScript中。
通过观察Web API返回结果的数据,我们似乎有点理解API Controller是如何工作的了。如果你再将请求URL改为 /api/reservation/3,你将看到这样的JSON数据(使用IE):
{"ReservationId":3,"ClientName":"Jacqui","Location":"Paris"}
这时,你可能更加明白了什么。这时返回的数据是 ReservationId 为 3 的Reservation对象,我们可以推测Web API根据请求的URL(/api/reservation/3)调用的是GetReservation这个Action方法。这让我们想到了之前讲过的路由知识[ASP.NET MVC 小牛之路]07 - URL Routing。
API Controller 在 /App_Start/WebApiConfig.cs 中有它们自己的路由配置,你可以打开该文件看看VS默认注册好的路由:
public static void Register(HttpConfiguration config) { config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); }
API Controller使用的路由配置(WebApiConfig.cs)和普通MVC使用的配置(RouteConfig.cs)是在两个分开的文件中,注册方法接收的参数(HtttpConfiguration 对象)和添加路由配置的方法(MapHttpRoute)也不一样。原理都是和 [ASP.NET MVC 小牛之路]07 - URL Routing 讲的一样的,这里不再累述了。
这个默认的Web API路由有一个静态片段(api),还有controller和 id片段变量。和MVC路由一个关键不同点是,它没有action片段变量。当然也可以和MVC一样定义action片段变量,你可以阅读 Routing in ASP.NET Web API 文章来了解更多Web API路由的细节。
当应用程序接收到一个和Web API 路由匹配的请求时,Action方法的调用将取决于发送HTTP请求的方式。当我们用 /api/reservation URL测试API Controller时,浏览器指定的是GET方式的请求。API Controller的基类 ApiController根据路由信息知道需要调用哪个Controller,并根据HTTP请求的方式寻找适合的Action方法。
一般约定在Action方法前加上HTTP请求方式名作为前缀。这里前缀只是个约定,Web API能够匹配到任何包含了HTTP请求方式名的Action方法。也就是说,本文示例的GET请求将会匹配 GetAllReservations 和 GetReservation,也能够匹配 DoGetReservation 或 ThisIsTheGetAction。
对于两个含有相同HTTP请求方式的Action方法,API Controlller会根据它们的参数和路由信息来寻找最佳的匹配。例如请求 /api/reservation URL,GetAllReservations 方法会被匹配,因为它没有参数;请求 /api/reservation/3 URL,GetReservation 方法会被匹配,因为该方法的参数名和URL的 /3 片段对应的片段变量名相同。我们还可以使用 POST、DELETE 和 PUT请求方式来指定ReservationController的其它Action方法。这就是前文提到的REST的风格。
但有的时候为了用HTTP方式名来给Action方法命名会显得很不自然,比如 PutReservation,习惯上会用 UpdateReservation。不仅用PUT命名不自然,POST也是一样的。这时候就需要使用类似于MVC的Controller中使用的Action方法选择器了。在System.Web.Http 命名空间下同样包含了一系列用于指定HTTP请求方式的特性,如下所示:
public class ReservationController : ApiController { ... [HttpPost] public Reservation CreateReservation(Reservation item) { return repo.Add(item); } [HttpPut] public bool UpdateReservation(Reservation item) { return repo.Update(item); } ... }
从这我们也看到,在API Controller中使用东西很多都是和MVC中的Controller是一样的,但它们分别在 System.Web.Http 和 System.Web.Mvc两个不同的命名空间下,正如本文开始所说,Web API把MVC中的很多东西抽取出来放在System.Web.Http命名空间中,以使Web API作为ASP.NET平台的一个独立的核心。
在Scripts文件夹下添加一个Home文件夹,在该文件夹下添加一个 Index.js 文件。在我们写JS代码之前,先把这个JS文件引用到Index.cshtml中,如下:
... @section scripts { <script src="~/Scripts/jquery.unobtrusive-ajax.js"></script> <script src="~/Scripts/Home/Index.js"></script> } ...
在 Index.js 中先把一些基本的方法写好,如下:
function selectView(view) { $(‘.display‘).not(‘#‘ + view + "Display").hide(); $(‘#‘ + view + "Display").show(); } function getData() { $.ajax({ type: "GET", url: "/api/reservation", success: function (data) { $(‘#tableBody‘).empty(); for (var i = 0; i < data.length; i++) { $(‘#tableBody‘).append(‘<tr><td><input id="id" name="id" type="radio"‘ + ‘value="‘ + data[i].ReservationId + ‘" /></td>‘ + ‘<td>‘ + data[i].ClientName + ‘</td>‘ + ‘<td>‘ + data[i].Location + ‘</td></tr>‘); } $(‘input:radio‘)[0].checked = "checked"; selectView("summary"); } }); } $(document).ready(function () { selectView("summary"); getData(); $("button").click(function (e) { var selectedRadio = $(‘input:radio:checked‘) switch (e.target.id) { case "refresh": getData(); break; case "delete": break; case "add": selectView("add"); break; case "edit": selectView("edit"); break; case "submitEdit": break; } }); });
我们定义了三个方法。第一个 selectView 方法用于控制div的显示和隐藏。第二个 getData 方法使用jQuery Ajax通过GET请求/api/reservation URL,并将返回的JSON数据填充到summaryDisplay的table中。第三个是jQuery的ready函数,在页面内容加载完成时执行。这时的效果如下:
接下来一步一步完美编辑、保存和删除的功能。添加“编辑”功能代码如下:
... case "edit": $.ajax({ type: "GET", url: "/api/reservation/" + selectedRadio.attr(‘value‘), success: function (data) { $(‘#editReservationId‘).val(data.ReservationId); $(‘#editClientName‘).val(data.ClientName); $(‘#editLocation‘).val(data.Location); selectView("edit"); } }); break; ...
当用户点击编辑时,先会取得radio button的value值,并以此组成一个URL(如/api/reservation/1),HTTP请求方式(GET)和URL将使API Controller调用 GetReservation 方法获取一个Reservation对象的JSON,并将其填充到editDisplay的文本框中。效果如下:
下面再完善一下删除和保存功能。代码如下:
... case "delete": $.ajax({ type: "DELETE", url: "/api/reservation/" + selectedRadio.attr(‘value‘), success: function (data) { selectedRadio.closest(‘tr‘).remove(); } }); break; ... case "submitEdit": $.ajax({ type: "PUT", url: "/api/reservation/" + selectedRadio.attr(‘value‘), data: $(‘#editForm‘).serialize(), success: function (result) { if (result) { var cells = selectedRadio.closest(‘tr‘).children(); cells[1].innerText = $(‘#editClientName‘).val(); cells[2].innerText = $(‘#editLocation‘).val(); selectView("summary"); } } }); break; ...
根据Ajax的DELETE请求,API Controller将调用 DeleteReservation 将选中的Reservation对象从集合中删除。同样,根据PUT请求API Controller 将调用PutReservation方法。
最后完善一下添加功能,该ajax请求使用的是 Unobtrusive Ajax,修改 Index.cshtml 如下:
... <h4>Add New Reservation</h4> @{ AjaxOptions addAjaxOpts = new AjaxOptions { OnSuccess = "getData", Url = "/api/reservation" }; } @using (Ajax.BeginForm(addAjaxOpts)) { @Html.Hidden("ReservationId", 0) <p><label>Name:</label>@Html.Editor("ClientName")</p> <p><label>Location:</label>@Html.Editor("Location")</p> <button type="submit">Submit</button> } ...
Ajax.BeginForm生成的表单默认使用的是POST请求,相应的 API Controller将调用PostReservation 方法添加Reservation对象。效果如下:
通过这个完整的示例我们可以看到,熟悉MVC后,使用Web API也非常简单,操作上基本和MVC类似,主要的不同体现在 ApiController 和 Action方法的匹配上。
参考:《Pro ASP.NET MVC 4 4th Edition》
[ASP.NET MVC 小牛之路]18 - Web API
标签:style blog http color io os ar 使用 java
原文地址:http://www.cnblogs.com/willick/p/3441432.html