ASP.NET Web API 入门教程:理解与实现






4.84/5 (96投票s)
在本文中,我们将讨论 ASP.NET Web API 的基础知识。
引言
在本文中,我们将讨论 ASP.NET Web API 的基础知识。我们将尝试理解什么是 Web API,以及 Web API 项目的基本结构。我们还将创建一个简单的应用程序,以演示如何使用 Web API 对一个简单实体执行 CRUD 操作。
背景从业务应用的角度来看,应用程序之间的连接性是一个非常重要的方面。如今,大量的移动应用和单页应用 (SPA) 被创建出来,这些应用需要一个强大的服务端,能够为它们提供数据以及对数据的 CRUD 操作。
SOAP 和 ASP.NET Web 服务
传统上,Web 服务为创建互联的 Web 应用程序提供了一种很好的方式。SOAP 和 XML 为创建互联的 Web 应用程序提供了出色的解决方案。SOAP 是一种基于 XML 的标准协议,通过 HTTP 进行通信。我们可以将 SOAP 视为一种使用 XML 在应用程序之间发送消息的消息格式。它独立于技术和平台,并且具有可扩展性。ASP.NET Web 服务为创建基于 SOAP 的 Web 服务提供了一种出色的方式。
注意: 关于 Web 服务的详细信息,请参阅此文章:理解 ASP.NET Web 服务基础[^]
SOAP 的问题
SOAP 提供了一种在应用程序之间传输数据的绝佳方式。但 SOAP 的问题在于,每次请求和响应除了传输数据外,还需要传输大量其他的元数据。这些额外信息是必需的,用于发现服务的功能以及与从服务器传输的数据相关的其他元数据。这使得即使是少量数据,有效载荷也会变得很重。此外,Web 服务需要客户端在自己的一端创建代理。这些代理将对 SOAP WSDL 进行编组和解组,从而使应用程序与 Web 服务之间的通信成为可能。这种代理的问题在于,如果服务更新了,而客户端的代理没有更新,
那么应用程序可能会出现行为不正确的情况。
欢迎来到 REST
REST 是“表述性状态转移 (Representational State Transfer)”的缩写。这是一种在分布式环境中交换数据的协议。REST 的核心思想是,我们应该将分布式服务视为一种资源,并且应该能够使用简单的 HTTP 协议对该资源执行各种操作。
当我们谈论数据库作为一种资源时,我们通常会用 CRUD 操作来描述,即创建 (Create)、检索 (Retrieve)、更新 (Update) 和删除 (Delete)。REST 的理念是,对于远程资源,所有这些操作都应该是可行的,并且应该能通过简单的 HTTP 协议来实现。
基本的 CRUD 操作与 HTTP 协议的映射关系如下:
- GET: 这对应于 CRUD 操作中的 R (Retrieve,检索)。它将用于从远程资源中检索所需的数据(数据的表示形式)。
- PUT: 这对应于 CRUD 操作中的 U (Update,更新)。该协议将更新远程服务器上数据的当前表示形式。
- POST: 这对应于 CRUD 操作中的 C (Create,创建)。这将为当前发送到服务器的数据创建一个新条目。
- DELETE: 这对应于 CRUD 操作中的 D (Delete,删除)。这将从远程服务器上删除指定的数据。
因此,如果我们举一个假设性的例子,一个远程资源包含一个书籍列表数据库。这个书籍列表可以通过如下 URL 进行检索:
www.testwebsite.com/books
要检索任何特定的书籍,假设我们有一些 ID 可以用来检索书籍,那么可能的 URL 可能是这样的:
www.testwebsite.com/books/1
由于这些是 GET 请求,数据只能从服务器检索。要执行其他操作,如果我们对类似的 URI 结构使用 PUT、POST 或 DELETE 操作,我们就应该能够从服务器上创建、更新和删除资源。我们将在实现部分看到如何做到这一点。
注意: 使用这些 URL 结构可以执行更复杂的查询。我们不会讨论通过各种 URL 模式可以执行的完整查询操作集。
现在,如果我们将 REST API 与 SOAP 进行比较,就可以看到 REST 的优势。首先,只有数据会在服务器之间来回传输,因为服务的功能已经映射到了 URI 和协议上。其次,客户端不需要有代理,因为传输的只是数据,应用程序可以直接接收和处理数据。
WCF REST 服务
Windows Communication Foundation (WCF) 的出现比 Web 服务晚得多。它提供了一种更安全、更成熟的方式来创建服务,以实现我们使用传统 Web 服务无法实现的功能,例如,支持其他协议,甚至双工通信。使用 WCF,我们可以一次性定义服务,然后通过配置使其能够通过 HTTP、TCP、IPC 甚至消息队列来使用。我们甚至可以将 WCF 服务配置为创建 REST 服务,即 WCF REST 服务。
注意: 关于 WCF REST 服务的详细信息,请参阅此文章:Windows Communication Foundation (WCF) 入门教程[^]
使用 WCF RESTful 服务的问题在于,我们需要对 WCF 服务进行大量配置才能使其成为 RESTful 服务。WCF 适用于那些我们需要创建支持特殊场景的服务的情况,例如单向消息传递、消息队列、双工通信,或者需要符合 WS-* 规范的服务。
但是,使用 WCF 来创建通过 HTTP 提供完全面向资源的 RESTful 服务有点复杂。尽管如此,如果存在使用 .NET 3.5 框架的限制,WCF 仍然是创建 RESTful 服务的唯一选择。
ASP.NET Web API 简介
微软最近推出了 ASP.NET Web API,以方便创建 RESTful 服务,这些服务能够为包括浏览器、移动设备和平板电脑在内的广泛客户端提供完全面向资源的服务。ASP.NET Web API 是一个框架,可以轻松、简单地构建 REST 服务。
使用代码
让我们从创建一个简单的 ASP.NET Web API 项目并查看项目模板开始讨论。要创建一个 Web API 项目,我们需要创建一个新的 ASP.NET MVC 4.0 Web 应用程序。

之后,系统会要求我们选择项目模板。在这里,我们需要选择 Web API 模板。

一旦我们准备好了 Web API 项目模板,我们可以看到 Web API 项目的解决方案结构与 MVC 4.0 网站的结构非常相似。但在某些方面存在细微的差异和新增内容,比如路由、控制器和视图。
控制器: 我们会得到一个 `ValuesController`,它将包含处理 HTTP 请求的代码。这个控制器派生自 `APIController` 类,并为每个 HTTP 操作包含相应的操作方法。
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
public void Post([FromBody]string value)
{
}
// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
在这里,每个操作方法都与一个 HTTP 方法相关联。路由: 如果我们查看 App_Start 文件夹,可以找到两个路由配置文件。一个是 `RouteConfig.cs`,它包含 MVC 4.0 应用程序的常规路由。另一个是 `WebApiConfig.cs`
它包含 WebAPI 控制器的路由。
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
路由模板以单词 "api" 开头,以区分该 URI 指向的是一个 API 而不是一个页面。控制器将映射到我们上面看到的控制器类。 有趣的是,路由不包含操作 (action) 的信息。原因在于,操作信息将作为 HTTP 方法/操作的一部分传入,而不是在 URI 中,因此 URI 不需要知道任何关于操作的信息。第三个参数 id 是将传递给操作方法的值。
视图: WebAPI 控制器没有视图,因为这些控制器将以 XML 或 JSON 格式(基于内容协商)提供原始数据,它们不需要任何特定的视图与之关联。
关于内容协商的说明
内容协商意味着客户端和服务器将共同商定它们之间传输的数据格式。如果客户端希望数据是 XML 或 JSON 格式,它可以在 HTTP 请求的
标头中指定这一点,然后服务器将以指定的选项提供数据。返回 XML 格式或 JSON 格式的选项可以分别指定为 `application/xml` 和 `application/json`。
让我们创建一个示例 Web API
让我们创建一个简单的 Web API,它将对一个示例数据库执行所有基本的 CRUD 操作。让我们创建一个包含 Books 表的示例数据库。我们将尝试对这个表执行 CRUD 操作。

为了在服务内部执行数据库操作,我们使用 Entity Framework。这也可以通过使用 ADO.NET 调用或其他 ORM 来完成,但我选择了 Entity Framework。(关于 Entity Framework 的信息,请参考:Entity Framework 完全入门介绍[^])。生成的实体将如下所示。

现在让我们创建一个 `BooksController` 来处理请求,并看看如何在这个控制器中为每个 HTTP 方法实现相应的操作。
public class BooksController : ApiController
{
// GET api/values
public IEnumerable<book> Get()
{
using (SampleDbEntities entities = new SampleDbEntities())
{
return entities.Books.ToList<book>();
}
}
// GET api/values/5
public Book Get(int id)
{
using (SampleDbEntities entities = new SampleDbEntities())
{
return entities.Books.SingleOrDefault<book>(b => b.ID == id);
}
}
// POST api/values
public HttpResponseMessage Post(Book value)
{
try
{
if (ModelState.IsValid)
{
using (SampleDbEntities entities = new SampleDbEntities())
{
entities.Books.AddObject(value);
entities.SaveChanges();
return Request.CreateResponse(HttpStatusCode.OK);
}
}
else
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, "Invalid Model");
}
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
}
}
// PUT api/values/5
public HttpResponseMessage Put(int id, Book value)
{
try
{
using (SampleDbEntities entities = new SampleDbEntities())
{
Book foundBook = entities.Books.SingleOrDefault<book>(b => b.ID == id);
foundBook.BookName = value.BookName;
entities.SaveChanges();
return Request.CreateResponse(HttpStatusCode.OK);
}
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
}
}
// DELETE api/values/5
public HttpResponseMessage Delete(int id)
{
try
{
using (SampleDbEntities entities = new SampleDbEntities())
{
Book foundBook = entities.Books.SingleOrDefault<book>(b => b.ID == id);
entities.Books.DeleteObject(foundBook);
entities.SaveChanges();
return Request.CreateResponse(HttpStatusCode.OK);
}
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
}
}
}
现在我们有了一个简单的 Web API,可以对 Books 数据库执行 CRUD 操作。让我们现在来测试这些方法。要测试 GET 方法,我们只需在浏览器中使用 URI,就可以看到结果。 目标: 获取所有书籍的列表,URI: https://:51377/api/Books,方法: HTTP GET

目标:使用 id 获取任何特定的书籍,URI: https://:51377/api/Books/1,方法: HTTP GET

目标:添加一本新书,URI: https://:51377/api/Books,方法: HTTP POST
为此,我们可以使用 Postman 并将以下数据 POST 到 API。
<Book xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/WebAPISample.Models">
<BookName>Yet Another Book</BookName>
</Book>
目标:更新现有书籍的详细信息,URI: https://:51377/api/Books/2,方法: HTTP PUT为此,我们可以使用 Postman 并将以下数据 PUT 到 API。
<Book xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/WebAPISample.Models">
<BookName>Updated book</BookName>
</Book>
目标:删除一本现有的书籍,URI: https://:51377/api/Books/2,方法: HTTP DELETE为此,我们可以使用 Postman,并使用 DELETE 方法,将 id 在 URI 中传递以删除该书籍。
注意:示例项目还包含一个名为 `webapitestclient` 的 NuGet 包。这可以用来通过访问 https://:51377/help 从应用程序本身测试 Web API。此外,示例代码包含 NuGet 包依赖项,但实际的包未包含在示例项目中,因此在运行应用程序之前,可能需要下载所有依赖的包。
值得关注的点:
在本文中,我们讨论了 ASP.NET Web API 的基础知识。我们还看到了一个示例实现,演示了如何实现一个基本的 Web API 来执行 CRUD 操作。本文是从初学者的角度编写的。我希望它能提供有用的信息。
历史
- 2013年9月26日: 初版。