在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用

📅 2026/7/4 2:07:33
在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用
为了让读者朋友们先对ASP.NET Web API具有一个感性认识接下来我们以实例演示的形式创建一个简单的ASP.NET Web API应用。这是一个用于实现“联系人管理”的单页Web应用我们以Ajax的形式调用Web API实现针对联系人的CRUD操作。[源代码从这里下载]目录构建解决方案定义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下面的列表给出了包括它在内的所有项目的类型和扮演的角色。·Common这是一个空的类库项目仅仅定义了表示联系人的数据类型而已。之所以将数据类型定义在独立的项目中只要是考虑到它会被多个项目WebApi和ConsoleApp所使用。WebApi这是一个空的类库项目表现为HttpController类型的Web API就定义在此项目中它具有对Common的项目引用。WebHost这是一个空的ASP.NET Web应用它实现了针对ASP.NET Web API的Web Host寄宿该项目具有针对WebApi的项目引用。SelfHost这是一个空的控制台应用旨在模拟ASP.NET Web API的Self Host寄宿模式它同样具有针对WebApi的项目引用。WebApp这是一个空的ASP.NET Web应用代表“联系人管理器”的网页就存在于该项目之中至于具体的联系人管理功能自然通过以Ajax的形式调用Web API来完成。ConsoleApp这是一个空的控制台应用我们用它来模拟如何利用客户端代理来实现对Web API的远程调用它具有针对Common的项目引用。二、定义Web API在正式定义Web API之前我们需要在项目Common中定义代表联系人的数据类型Contact。简单起见我们仅仅为Contact定义了如下几个简单的属性它们分别代表联系人的ID、姓名、联系电话、电子邮箱和联系地址。1: public class Contact2: {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: ApiController2: {3: static ListContact contacts;4: static int counter 2;5:6: static ContactsController()7: {8: contacts new ListContact();9: contacts.Add(new Contact { Id 001, Name 张三,10: PhoneNo 0512-12345678, EmailAddress zhangsangmail.com,11: Address 江苏省苏州市星湖街328号 });12: contacts.Add(new Contact { Id 002, Name 李四,13: PhoneNo 0512-23456789, EmailAddress lisigmail.com,14: Address 江苏省苏州市金鸡湖大道328号 });15: }16:17: public IEnumerableContact Get(string id null)18: {19: return from contact in contacts20: 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方法我们也省略了必要的验证对于本书后续的演示的实例我们基本上也会采用这种“简写”的风格。三、以Web Host方式寄宿Web API我们在上面已经提到过了虽然被命名为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\”中找到。System.Web.Http.dll\ Microsoft.AspNet.WebApi.Core.5.0.0\lib\net45\System.Net.Formatting.Http.dll\Microsoft.AspNet.WebApi.Client.5.0.0\lib\net45\System.Web.Http.WebHost.dll\Microsoft.AspNet.WebApi.WebHost.5.0.0\lib\net45\System.Net.Http.dll与ASP.NET MVC一样如果采用Web Host的方式来寄宿Web APIASP.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.HttpApplication2: {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 StudioVS 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.12: Host: localhost3: Connection: keep-alive4: Cache-Control: max-age05: Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.86: User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.367: Accept-Encoding: gzip,deflate,sdch8: Accept-Language: en-US,en;q0.8,zh-CN;q0.6,zh-TW;q0.4为了进一步验证并演示ASP.NET Web API的内容协商机制我们现在改用Fiddler来发送调用Web API的HTTP请求。如左图所示我们利用Fiddler发送了一个针对目标地址“http://localhost/webhost/api/contacts/001”的HTTP-GET请求并添加了一个值为“application/json”的“Accept”报头请求发送之后确实得到了以JSON格式表示的联系人列表。支持PUT和DELETE请求在定义ContactsController的时候我们严格按照RESTful Web API关于“使用标准的HTTP方法”的指导方针分别采用GET、POST、PUT和DELETE作为获取、创建、修改和删除联系人的操作所支持的HTTP方法。但是IIS在默认情况下并不提供针对 PUT和DELETE请求的支持。如右图所示我们利用Fiddler发送了一个针对地址“http://localhost/webhost/api/contacts/001”的HTTP-DELETE请求旨在删除ID为“001”的联系人。但是遗憾的是我们得到了一个状态为“405Method 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: configuration2: ...3: system.webServer4: modules runAllManagedModulesForAllRequeststrue5: remove nameWebDAVModule /6: /modules7: /system.webServer8: /configuration四、 以Self Host方式寄宿Web API与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\”中找到。System.Web.Http.dll\ Microsoft.AspNet.WebApi.Core.5.0.0\lib\net45\System.Net.Formatting.Http.dll\Microsoft.AspNet.WebApi.Client.5.0.0\lib\net45\System.Web.Http.SelfHost.dll\Microsoft.AspNet.WebApi.SelfHost.5.0.0\lib\net45\System.Net.Http.dll通过上面的介绍我们可以看到以Web Host的方式寄宿Web API需要做的唯一一件事情是路由注册。但是对于Self Host来说除了必需的路由注册外我们还需要完成额外的一件事情即手工加载定义了HttpController类型的程序集。整个寄宿工作通过如下几行简单的代码就可以实现。1: class Program2: {3: static void Main(string[] args)4: {5: Assembly.Load(WebApi, Version1.0.0.0, Cultureneutral, PublicKeyTokennull);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”后我们依然会得到上面的结果。五、利用HttpClient调用Web API对于一个.NET客户端程序它可以利用HttpClient来进行Web API的调用。由于Web API的调用本质上就是一次普通的发送请求/接收响应的过程所以HttpClient其实可以作为一般意义上发送HTTP请求的工具。在ConsoleApp代表的控制台应用中我们利用HttpClient来调用以Self Host方式寄宿的Web API。由于我们需要使用到代表联系人的数据类型Contact所以需要为该项目添加针对Common的项目引用。HttpClient定义在程序集“System.Net.Http.dll”中所以针对该程序集的引用也是必需的。除此之外我们还需要添加针对程序集“System.Net.Formatting.Http.dll”的引用因为序列化请求和反序列化响应的相关类型定义在此程序集中。如下所示的是整个Web API调用程序的定义我们利用HttpClient调用Web API实现了针对联系人的获取、添加、修改和删除。由于HttpClient提供的大部分方法都采用针对Task的异步编程形式所以我们将所有的操作定义在一个标记为“async”的静态方法Process中以便我们可以使用“await”关键字编写同步代码。1: class Program2: {3: static void Main(string[] args)4: {5: Process();6: Console.Read();7: }8:9: private async static void Process()10: {11: //获取当前联系人列表12: HttpClient httpClient new HttpClient();13: HttpResponseMessage response await httpClient.GetAsync(http://localhost/selfhost/api/contacts);14: IEnumerableContact contacts await response.Content.ReadAsAsyncIEnumerableContact();15: Console.WriteLine(当前联系人列表);16: ListContacts(contacts);17:18: //添加新的联系人19: Contact contact new Contact { Name 王五, PhoneNo 0512-34567890, EmailAddress wangwugmail.com };20: await httpClient.PostAsJsonAsyncContact(http://localhost/selfhost/api/contacts, contact);21: Console.WriteLine(添加新联系人“王五”);22: response await httpClient.GetAsync(http://localhost/selfhost/api/contacts);23: contacts await response.Content.ReadAsAsyncIEnumerableContact();24: ListContacts(contacts);25:26: //修改现有的某个联系人27: response await httpClient.GetAsync(http://localhost/selfhost/api/contacts/001);28: contact (await response.Content.ReadAsAsyncIEnumerableContact()).First();29: contact.Name 赵六;30: contact.EmailAddress zhaoliugmail.com;31: await httpClient.PutAsJsonAsyncContact(http://localhost/selfhost/api/contacts/001, contact);32: Console.WriteLine(修改联系人“001”信息);33: response await httpClient.GetAsync(http://localhost/selfhost/api/contacts);34: contacts await response.Content.ReadAsAsyncIEnumerableContact();35: ListContacts(contacts);36:37: //删除现有的某个联系人38: await httpClient.DeleteAsync(http://localhost/selfhost/api/contacts/002);39: Console.WriteLine(删除联系人“002”);40: response await httpClient.GetAsync(http://localhost/selfhost/api/contacts);41: contacts await response.Content.ReadAsAsyncIEnumerableContact();42: ListContacts(contacts);43: }44:45: private static void ListContacts(IEnumerableContact contacts)46: {47: foreach (Contact contact in contacts)48: {49: Console.WriteLine({0,-6}{1,-6}{2,-20}{3,-10}, contact.Id, contact.Name, contact.EmailAddress, contact.PhoneNo);50: }51: Console.WriteLine();52: }53: }如上面的代码片段所示我们创建了一个HttpClient对象并调用其GetAsync方法向目标地址“http://localhost/selfhost/api/contacts”发送了一个GET请求返回的对象HttpResponseMessage表示接收到的响应。该HttpResponseMessage对象的Content属性返回一个表示响应主体内容的HttpContent对象我们调用其ReadAsAsyncT方法读取响应主体内容并将其反序列化成一个Contact集合。我们将表示当前联系人列表的Contact集合输出在控制台上。我们接下来调用HttpClient的PostAsJsonAsyncT方法向目标地址“http://localhost/selfhost/api/contacts”发送一个POST请求以添加一个新的联系人。正如方法名称所体现的作为参数的Contact对象将以JSON格式被写入请求的主体部分。请求被正常发送并接收到响应之后我们会打印出当前联系人列表。在此之后我们向目标地址“http://localhost/selfhost/api/contacts/001”发送一个GET请求以获取ID为“001”的联系人。在修改了联系人的姓名“赵六”和电子邮箱“zhaoliugmail.com”之后我们将其作为参数调用HttpClient的PutAsJsonAsyncT方法以此向目标地址“http://localhost/selfhost/api/contacts/001”发送一个PUT请求以更新对应联系人的相关信息。联系人信息是否正常更新同样通过输出当前所有联系人列表来证实。我们最后调用HttpClient的DeleteAsync方法向地址“http://localhost/selfhost/api/contacts/002”发送一个DELETE请求以删除ID为“002”的联系人并通过输出当前所有联系人列表来证实删除参数是否成功完成。我们在运行宿主程序SelfHost之后启动此ConsoleApp程序会在控制台上得到下所示的输出结果由此可以看出通过调用HttpClient的GetAsync、PostAsJsonAsync、PutAsJsonAsync和DeleteAsync方法帮助我们成功完成了针对联系人的获取、添加、修改和删除。1: 当前联系人列表2: 001 张三 zhangsangmail.com 0512-123456783: 002 李四 lisigmail.com 0512-234567894:5: 添加新联系人“王五”6: 001 张三 zhangsangmail.com 0512-123456787: 002 李四 lisigmail.com 0512-234567898: 003 王五 wangwugmail.com 0512-345678909:10: 修改联系人“001”信息11: 002 李四 lisigmail.com 0512-2345678912: 003 王五 wangwugmail.com 0512-3456789013: 001 赵六 zhaoliugmail.com 0512-1234567814:15: 删除联系人“002”16: 003 王五 wangwugmail.com 0512-3456789017: 001 赵六 zhaoliugmail.com 0512-12345678六、创建一个“联系人管理器”应用我们最后来创建一个叫做“联系人管理器”的Web应用。这是一个单网页应用我们采用Ajax的请求的形式调用以Web Host模式寄宿的Web API实现针对联系人的CRUD操作。在正式介绍编程实现之前我们不妨来看看该应用运行起来的效果。如右图所示当页面被加载之后当前联系人列表会以表格的形式呈现出来。我们可以利用每条联系人记录右侧的“修改”和“删除”链接实现针对当前联系人的编辑和删除。除此之外我们还可以点击左下方的“添加联系人”按钮添加一个新的联系人。如果我们点击“删除”链接当前联系人会直接被删除。如果我们点击了“修改”链接或者“添加联系人”按钮被修改或者添加的联系人信息会显示在如左图所示的一个弹出的“模态”对话框中。在我们输入联系人相关资料后点击“保存”按钮联系人会被成功修改或者添加。被修改的现有联系人信息或者被添加的联系人会立即体现在列表之中。虽然这仅仅是一个简单的Web应用但是我刻意使用了3个主流的Web前端开发框架它们分别是jQuery、Bootstrap和KnockOut这三个框架的使用体现在页面引用的CSS和JavaScript文件上。