ASP.NET Web API具有与ASP.NET MVC类似的编程方式,相关内容较少,如下通过一个实例讲解WebApi的集中寄宿方式。
目录
构建解决方案
定义Web API
以Web Host方式寄宿Web API
以Self Host方式寄宿Web API
利用HttpClient调用Web API
创建一个“联系人管理器”应用
Visual Studio为我们提供了专门用于创建ASP.NET Web API应用的项目模板,借助于此项目模板提供的向导,我们可以“一键式”创建一个完整的ASP.NET Web API项目。在项目创建过程中,Visual Studio会自动为我们添加必要的程序集引用和配置,甚至会为我们自动生成相关的代码,总之一句话:这种通过向导生成的项目在被创建之后其本身就是一个可执行的应用。
对于IDE提供的这种旨在提高生产效率的自动化机制,我个人自然是推崇的,但是我更推荐读者朋友们去了解一下这些自动化机制具体为我们做了什么?做这些的目的何在?哪些是必需的,哪些又是不必要的?正是基于这样的目的,在接下来演示的实例中,我们将摒弃Visual Studio为我们提供的向导,完全在创建的空项目中编写我们的程序。这些空项目体现在如右图所示的解决方案结构中。
如右图所示,整个解决方案一共包含6个项目,上面介绍的作为“联系人管理器”的单页Web应用对应着项目WebApp,下面的列表给出了包括它在内的所有项目的类型和扮演的角色。
在正式定义Web API之前,我们需要在项目Common中定义代表联系人的数据类型Contact。简单起见,我们仅仅为Contact定义了如下几个简单的属性,它们分别代表联系人的ID、姓名、联系电话、电子邮箱和联系地址。
1: public class Contact
2: {
3: public string Id { get; set; }
4: public string Name { get; set; }
5: public string PhoneNo { get; set; }
6: public string EmailAddress { get; set; }
7: public string Address { get; set; }
8: }
表现为HttpController的Web API定义在WebApi项目之中,我们一般将ApiController作为继承的基类。ApiController定义在“System.Web.Http.dll”程序集中,我们可以在目录“%ProgramFiles%\Microsoft ASP.NET\ASP.NET Web Stack 5\Packages\”中找到这个程序集。具体来说,该程序集存在于子目录“Microsoft.AspNet.WebApi.Core.5.0.0\lib\net45”中。
Web API体现在如下所示的ContactsController类型中。在该类型中,我们定义了Get、Post、Put和Delete这4个Action方法,它们分别实现了针对联系人的查询、添加、修改和删除操作。Action方法Get具有一个表示联系人ID的可缺省参数,如果该参数存在则返回对应的联系人,否则返回整个联系人列表。由于ASP.NET Web API默认实现了Action方法与HTTP方法的映射,所以方法名也体现了它们各自所能处理请求必须采用的HTTP方法。
1: public class ContactsController: ApiController
2: {
3: static List<Contact> contacts;
4: static int counter = 2;
5:
6: static ContactsController()
7: {
8: contacts = new List<Contact>();
9: contacts.Add(new Contact { Id = "001", Name = "张三",
10: PhoneNo = "0512-12345678", EmailAddress = "[email protected]",
11: Address = "江苏省苏州市星湖街328号" });
12: contacts.Add(new Contact { Id = "002", Name = "李四",
13: PhoneNo = "0512-23456789", EmailAddress = "[email protected]",
14: Address = "江苏省苏州市金鸡湖大道328号" });
15: }
16:
17: public IEnumerable<Contact> Get(string id = null)
18: {
19: return from contact in contacts
20: where contact.Id == id || string.IsNullOrEmpty(id)
21: select contact;
22: }
23:
24: public void Post(Contact contact)
25: {
26: Interlocked.Increment(ref counter);
27: contact.Id = counter.ToString("D3");
28: contacts.Add(contact);
29: }
30:
31: public void Put(Contact contact)
32: {
33: contacts.Remove(contacts.First(c => c.Id == contact.Id));
34: contacts.Add(contact);
35: }
36:
37: public void Delete(string id)
38: {
39: contacts.Remove(contacts.First(c => c.Id == id));
40: }
41: }
简单起见,我们利用一个静态字段(contacts)表示存储的联系人列表。当ContactsController类型被加载的时候,我们添加了两个ID分别为“001”和“002”的联系人记录。至于实现联系人CRUD操作的Action方法,我们也省略了必要的验证,对于本书后续的演示的实例,我们基本上也会采用这种“简写”的风格。
我们在上面已经提到过了,虽然被命名为ASP.NET Web API,但是其核心的消息处理管道却是独立于ASP.NET平台的,所以我们可以对相同的Web API实施不同的寄宿方式。寄宿的本质就是利用一个具体的应用程序为Web API提供一个运行的环境,并最终解决“请求的接收和响应的回复”问题。作为寄宿的一种主要形式,Web Host就是创建一个ASP.NET Web应用作为Web API的宿主。
采用Web Host方式寄宿Web API的宿主程序WebHost是一个空的ASP.NET应用。除了让它引用定义ContactsController的WebApi项目之外,我们还需要为其添加如下这些必需的程序集引用。除了程序集“System.Net.Http.dll”(它属于.NET Framework 原生的程序集)之外,其余3个均可以在目录“%ProgramFiles%\Microsoft ASP.NET\ASP.NET Web Stack 5\Packages\”中找到。
与ASP.NET MVC一样,如果采用Web Host的方式来寄宿Web API,ASP.NET自身的路由系统会成为接收请求的第一道屏障。在将请求递交给ASP.NET Web API自己的消息处理管道之前,路由系统会解析出当前请求访问的目标HttpController和Action的名称。我们需要做的就是根据需求注册相应的路由,这也是采用Web Host寄宿方式所需的唯一操作。
我们在WebHost项目中添加一个Global.asax文件,并按照如下的形式在其Application_Start方法中注册了一个模板为“api/{controller}/{id}”的路由。此模板由3部分组成,静态文本“api”表示其前缀,后面是两个路由参数。前者({controller})表示目标HttpController的名称,后者({id})可以映射为目标Action方法的同名参数(比如ContractsController的Get方法的参数id),这是一个可以缺省的路由参数(RouteParameter.Optional)。
1: public class Global : System.Web.HttpApplication
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: GlobalConfiguration.Configuration.Routes.MapHttpRoute(
6: Name : "DefaultApi",
7: routeTemplate : "api/{controller}/{id}",
8: defaults : new { id = RouteParameter.Optional });
9: }
10: }
如上面的代码片断所示,路由注册是通过调用代表全局路由表的HttpRouteCollection对象的扩展方法MapHttpRoute来完成的。GlobalConfiguration的静态属性Configuration返回一个代表当前配置的HttpConfiguration对象,全局路由表就注册在它的Routes属性上。
如果你了解ASP.NET MVC的路由注册,可能觉得奇怪:注册路由的模板中并没有表示目标Action的路由参数,ASP .NET Web API如何根据请求确定哪个Action方法应该被调用呢?答案其实很简单:它能根据请求采用HTTP方法来确定目标Action方法。当然,在注册路由模板中提供代表Action名称的路由参数({action})也是支持的。
在默认情况下,通过Visual Studio(VS 2012或者VS 2013,本书采用的是后者)创建的Web应用总是使用IIS Express作为服务器,它会自动为我们指定一个可用的端口号。为了更好地模拟真实发布环境,同时避免“跨域资源共享”带来的困扰,我们采用本地IIS作为服务器。如下图所示,WebHost项目在IIS中映射的Web应用采用的URL为“http://localhost/webhost”。
实际上到此为止,Web API的Web Host寄宿工作就已经完成,我们可以利用浏览器来调用寄宿的Web API来判断寄宿工作是否成功。由于浏览器在默认情况下访问我们在地址栏中输入的地址总是采用HTTP-GET请求,所以我们只能利用它来调用支持HTTP-GET的Action方法,即定义在ContactsController中的Get方法。
根据我们注册的路由,如果我们访问目标地址“http://localhost/webhost/api/contacts”可以获得所有联系人列表;如果目标地址为“http://localhost/webhost/api/contacts/001”,则可以得到ID为“001”的联系人信息,右图证实了这一点。
从右图可以看到,我们采用的浏览器为Chrome,获取的联系人列表总是表示为XML,这是为什么呢?在前面介绍REST的时候,我们曾经提及一种旨在识别客户端期望的资源表示形式并被称为“内容协商”的机制,它可以根据请求携带的相关信息来判断客户端所期望的响应资源表现形式。
对于ASP.NET Web API来说,它会优先利用请求报头“Accept”携带的媒体类型来确定响应内容采用的表现形式。如下所示的是Chrome访问“http://localhost/webhost/api/contacts/001”发送请求的内容,它之所以会得到以XML表示的响应是因为“Accept”报头指定的媒体类型列表中只有“application/xml”被ASP.NET Web API支持。如果我们采用IE,请求的“Accept”报头将携带不同的媒体类型列表,我们实际上会得到以JSON格式表示的响应结果。
1: GET http://localhost/webhost/api/contacts/001 HTTP/1.1
2: Host: localhost
3: Connection: keep-alive
4: Cache-Control: max-age=0
5: Accept: text/html,application/xhtml+xml,application/xml ;q=0.9,image/webp,*/*;q=0.8
6: User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
7: Accept-Encoding: gzip,deflate,sdch
8: Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh-TW;q=0.4
为了进一步验证并演示ASP.NET Web API的内容协商机制,我们现在改用Fiddler来发送调用Web API的HTTP请求。如左图所示,我们利用Fiddler发送了一个针对目标地址“http://localhost/webhost/api/contacts/001”的HTTP-GET请求,并添加了一个值为“application/json”的“Accept”报头,请求发送之后确实得到了以JSON格式表示的联系人列表。
在定义ContactsController的时候,我们严格按照RESTful Web API关于“使用标准的HTTP方法”的指导方针,分别采用GET、POST、PUT和DELETE作为获取、创建、修改和删除联系人的操作所支持的HTTP方法。但是IIS在默认情况下并不提供针对 PUT和DELETE请求的支持。
如右图所示,我们利用Fiddler发送了一个针对地址“http://localhost/webhost/api/contacts/001”的HTTP-DELETE请求,旨在删除ID为“001”的联系人。但是遗憾的是,我们得到了一个状态为“405,Method Not Allowed”的响应,意味着服务端并不支持HTTP-DELETE方法。
IIS拒绝PUT和DELETE请求是由默认注册的一个名为“WebDAVModule”的自定义HttpModule导致的。WebDAV的全称为“Web-based Distributed Authoring and Versioning”,它是一个在多用户之间辅助协同编辑和管理在线文档的HTTP扩展。该扩展使应用程序可以直接将文件写到 Web Server 上,同时支持文件的加锁和版本控制。
微软是推动WebDAV成为一个标准的主导力量,它自己利用自定义的HttpModule实现了IIS针对WebDAV的支持。但是这个默认注册(注册名称为“WebDAVModule”)会拒绝HTTP方法为PUT和DELETE的请求,如果我们的站点不需要提供针对WebDAV的支持,解决这个问题最为直接的方式就是利用如下的配置将注册的HttpModule移除。
1: <configuration>
2: ...
3: <system.webServer>
4: <modules runAllManagedModulesForAllRequests="true">
5: <remove name="WebDAVModule" />
6: </modules>
7: </system.webServer>
8: </configuration>
与WCF类似,寄宿Web API不一定需要IIS的支持,我们可以采用Self Host的方式使用任意类型的应用程序(控制台、Windows Forms应用、WPF应用甚至是Windows Service)作为宿主。对于我们演示的实例来说,项目SelfHost代表的控制台程序就是一个采用Self Host寄宿模式的宿主。
对于SelfHost这么一个空的控制台应用来说,除了需要添加针对WebApi的项目引用之外,还需要添加如下4个程序集引用。除了程序集“System.Net.Http.dll”(它属于.NET Framework 原生的程序集)之外,其余3个均可以在目录“%ProgramFiles%\Microsoft ASP.NET\ASP.NET Web Stack 5\Packages\”中找到。
通过上面的介绍我们可以看到以Web Host的方式寄宿Web API需要做的唯一一件事情是路由注册。但是对于Self Host来说,除了必需的路由注册外,我们还需要完成额外的一件事情,即手工加载定义了HttpController类型的程序集。整个寄宿工作通过如下几行简单的代码就可以实现。
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: Assembly.Load("WebApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
6:
7: HttpSelfHostConfiguration configuration = new HttpSelfHostConfiguration("http://localhost/selfhost");
8: using (HttpSelfHostServer httpServer = new HttpSelfHostServer(configuration))
9: {
10: httpServer.Configuration.Routes.MapHttpRoute(
11: name : "DefaultApi",
12: routeTemplate : "api/{controller}/{id}",
13: defaults : new { id = RouteParameter.Optional });
14:
15: httpServer.OpenAsync();
16: Console.Read();
17: }
18: }
19: }
ASP.NET Web API的Self Host寄宿方式通过HttpSelfHostServer来完成。如上面的代码片断所示,在手工加载了定义ContactsController类型的程序集“WebApi.dll”之后,我们根据指定的基地址(“http://localhost/selfhost”),注册路由的URL模板将是以此作为基地址的相对地址)创建了一个HttpSelfHostConfiguration对象,HttpSelfHostServer由该对象创建。接下来,我们利用创建的HttpSelfHostConfiguration对象(对应着HttpSelfHostServer的Configuration属性)的Routes得到全局路由表,并调用扩展方法MapHttpRoute注册了与Web Host寄宿方式一样的路由。当我们调用OpenAsync方法成功开启HttpSelfHostServer之后,服务器开始监听来自网络的调用请求。
如果读者朋友们对WCF比较熟悉的话,应该清楚在进行WCF服务寄宿的时候我们必须指定寄宿服务的类型,但是对于ASP.NET Web API的寄宿来说,不论是Web Host还是Self Host,我们都无需指定HttpController的类型。换句话说,WCF服务寄宿是针对具体某个服务类型的,而ASP.NET Web API的寄宿则是批量进行的。
ASP.NET Web API的批量寄宿源自它对HttpController类型的智能解析,它会从“提供的”的程序集列表中解析出所有HttpController类型(所有实现了IHttpController接口的类型)。对于Web Host来说,它会利用BuildManager获得当前项目直接或者间接引用的程序集,但是对于Self Host来说,HttpController类型的解析在默认情况下只会针对加载到当前应用程序域中的程序集列表,这也是我们为何需要手工加载定义了ContactsController类型的程序集的原因所在。
如果现在运行这个作为宿主的控制台程序,我们依然可以对寄宿其中的Web API发起调用。同样采用浏览器作为测试工具,在分别访问目标地址“http://localhost/selfhost/api/contacts”和“http://localhost/selfhost/api/contacts/001”后,我们依然会得到上面的结果。
参考:http://www.cnblogs.com/artech/p/web-api-sample.html