65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET MVC5 特性 - WebAPI 2

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (20投票s)

2014 年 8 月 10 日

CPOL

9分钟阅读

viewsIcon

68392

本文介绍 ASP.NET MVC5 的 WebAPI v2 特性。

引言

ASP.NET MVC5 附带了 WebAPI 的第 2 版。对于 WebAPI 的新手来说,它是由 ASP.NET 提供的一个新的 RESTful 服务,我们可以在其中使用 HTTP 动词 GET、POST、PUT、DELETE 等来实现自己的基于 REST 的服务,并使用 JSON 或 XML 进行数据传输。微软还提供了另一个使用 WCF REST 的 REST 服务。

对于 REST 服务的新手来说,这是一种拥抱 Web 并使用 HTTP 而不依赖任何工具包(SOAP 服务通常使用)的服务风格。有关 REST 的更多详细信息,请参阅 http://en.wikipedia.org/wiki/Representational_state_transfer. 

背景

这是我 ASP.NET MVC5 系列的第二篇文章。在这里,我将尝试从头开始解释 WebAPI,因此即使您对 Web API 版本 1 没有任何先验知识,也没关系。

让我们开始探索 WebAPI。

创建一个新的 ASP.NET 项目并选择 Web API 模板。

如果您愿意,可以更改身份验证类型为更适合您的类型,但我将保留“无身份验证”,并允许匿名用户访问。

如果您在不更改项目的情况下运行该项目,您将能够看到一个主页。如果您点击顶部的 API 菜单,您会看到一个 API 帮助页面,其中包含一些文档。稍后我会回来解释这个帮助页面的工作原理。但现在,您可以尝试浏览 api 帮助页面显示的一个 URI,例如 GET api/Values ,通过 https://:14129/api/Values 访问,您将看到一些 XML 格式的数据。

这意味着我们的 WebAPI 已经启动并运行,并且正在向我们提供数据。

让我们看一下解决方案项目中的代码。如果您打开 Controllers 文件夹,您会看到一个 HomeController,它是一个普通的 ASP.NET MVC 控制器(因此继承自 Controller 类)。此控制器返回我们之前看到的 Home 视图。第二个名为 ValuesController 的控制器是一个 WebAPI 控制器(请注意它继承自 ApiController)。如果您查看这些方法,它们基本上是用于执行 CRUD 操作的方法。

让我们看一下 CRUD(http://en.wikipedia.org/wiki/Create,_read,_update_and_delete)中的哪些方法执行哪些操作。

1. Get 方法用于检索数据。

有两个 Get() 方法,一个返回集合,一个返回特定值(还提供了一个参数)。这是 CRUD 中的 Read 操作,使用 HTTP 动词 GET。

通常,您会从数据库等数据源检索数据。

2. Post 方法用于创建记录。

Post 方法将通过通常从网页中的表单提交的数据(但不一定,也可以通过其他方式 POST)进行传递,并用于在数据存储中创建记录。

这是 CRUD 中的 Create 操作,使用 HTTP 动词 POST。

3. Put 方法用于更新记录。

Put 方法将接收一个 id 参数和一个 value 参数(通常是对象形式),并用于更新一个 id 已提供的记录。

这是 CRUD 中的 Update 操作,使用 HTTP 动词 PUT。

4. Delete 方法用于删除记录。

Delete 方法将接收一个 id 参数,并用于从数据存储中删除记录。

这是 CRUD 中的 Delete 操作,使用 HTTP 动词 DELETE。

现在让我们尝试创建我们自己的 ApiController 来执行一些更复杂的操作,并将其命名为 DepartmentController。

我们将选择一个 Empty controller 模板。

为了简单起见,我们将使用 IEnumerable 来存储部门和员工的静态集合。在实际应用程序中,我们会从数据库获取/存储这些数据。我们将在项目中添加一些模型类。

    public class Department
    {
        public int ID { get; set; }
        public string DepartmentName { get; set; }
        public ICollection<Employee> Employees { get; set; }
    }
    public class Employee
    {
        public int ID { get; set; }
        public string EmployeeName { get; set; }
    }

所以,让我们在 DepartmentController 的构造函数中创建一些虚拟数据。

public class DepartmentController : ApiController
    {
        public static List<Department> departments;
        public DepartmentController()
        {
            departments = new List<Department>() { 
                new Department{ ID = 1, 
                                DepartmentName = "Computer Science",
                                Employees = new List<Employee>{
                                                new Employee{ ID = 1, EmployeeName = "Robert Scott"},
                                                new Employee{ ID = 2, EmployeeName = "Martin Scale"}
                                                            }
                },
                new Department{ ID = 2, 
                                DepartmentName = "Mechanical",
                                Employees = new List<Employee>{
                                                new Employee{ ID = 3, EmployeeName = "Indiana Twain"},
                                                new Employee{ ID = 4, EmployeeName = "Tom Spain"}
                                                            }
                }
            };
        }
    }

现在,让我们编写第一个 Get 方法来获取部门列表。

public IEnumerable<Department> Get()
{
     return departments;
}

现在,我们尝试使用浏览器对 https://:14129/api/Department 进行 GET 请求,我们会收到部门集合的 XML 返回。

如果您是 Web API 的新手,您可能会想,我们从未调用过 Get() 操作方法,为什么我们却收到了部门数据。是的,这是 ASP.NET Web API 为我们做的,使用默认的路由配置。因此,任何以 Get 开头的方法名在收到 HTTP GET 请求时都会被映射。它只需要匹配参数,并且应该只有一个具有相同方法签名的操作,否则 Web API 会抛出 InvalidOperationException。

让我们看一下 App_Start 文件夹中 WebApiConfig 类中的默认路由。

public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Attribute based routing
            config.MapHttpAttributeRoutes();

            //Default web api route
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }

Web API 2 附带了属性路由,下面这行代码启用了基于属性的路由。请注意,它放在默认 Web API 路由之前,因此我们可以选择进行基于属性的路由。

config.MapHttpAttributeRoutes();

让我们尝试添加另一个接受 id 作为参数并返回 Department 的 Get 方法。

public Department GetDepartmentByID(int id)
{
     return departments.FirstOrDefault(x => x.ID == id);
}

让我们尝试在浏览器中对上述 api 方法进行 GET 请求 https://:14129/api/Department/1

我们收到了特定部门 1 的 XML 数据。

现在,如果我们查看部门和员工的数据,每个部门内部都有一个员工集合。在只有默认路由的情况下,让我们尝试检索特定部门的员工集合。

例如,https://:14129/api/Department/1/Employees,我们收到一个 HTTP 404,表示资源未找到。好吧,默认路由不处理这种情况,因为它只有一个路由映射,接受 routeTemplate: "api/{controller}/{id}"。我们可以添加另一个路由来解决这个问题。

但是,使用基于属性的路由则更容易,并且我们的 WebApiConfig 也会保持整洁。

我们将添加另一个 api 方法并用属性对其进行装饰。

        [Route("api/department/{id}/employees")]
        public IEnumerable<Employee> GetEmployeesByDepartmentID(int id)
        {
            return departments.FirstOrDefault(x => x.ID == id).Employees;
        }

现在,当我们尝试运行 https://:14129/api/Department/1/Employees 时,我们收到了员工集合数据。因此,使用基于属性的路由,我们只需通过 C# 属性装饰即可根据需要添加 api 方法。

让我们尝试为上面的属性路由添加更多规则。

[Route("api/department/{id:int:min(1):max(2):maxlength(1)}/employees")] 

所以在这里我们说 id 应该是 int 类型,最小值是 1,最大值是 2,最大长度是 1。因此,我们可以根据需要为我们的属性添加任意数量的规则。

我们可以为一个方法添加多个路由,可以应用于整个控制器,而且别忘了这些规则在普通的 MVC 控制器中也同样有效。

我将从以下链接提供一个详尽的规则列表 http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx

约束 描述 示例
alpha 匹配大写或小写拉丁字母(a-z,A-Z) {x:alpha}
bool 匹配布尔值。 {x:bool}
datetime 匹配 DateTime 值。 {x:datetime}
decimal 匹配十进制值。 {x:decimal}
double 匹配 64 位浮点值。 {x:double}
float 匹配 32 位浮点值。 {x:float}
guid 匹配 GUID 值。 {x:guid}
int 匹配 32 位整数值。 {x:int}
length 匹配指定长度或在指定长度范围内字符串。 {x:length(6)} 
{x:length(1,20)}
long 匹配 64 位整数值。 {x:long}
max 匹配最大值的整数。 {x:max(10)}
maxlength 匹配最大长度的字符串。 {x:maxlength(10)}
min 匹配最小值的整数。 {x:min(10)}
minlength 匹配最小长度的字符串。 {x:minlength(10)}
range 匹配值范围内的整数。 {x:range(10,50)}
regex 匹配正则表达式。 {x:regex(^\d{3}-\d{3}-\d{4}$)}

CORS 支持(跨域资源共享)

CORS 意味着从一个域名的浏览器(通常是 Javascript)向另一个域名发起请求并获取响应。通常,由于安全原因,浏览器会阻止这样做。但是,如果发起请求的域名在 CORS 调用中向另一个域名发送了来源信息,并且该域名同意发送响应,则它会发送一个 HTTP 头部信息 Access-Control-Allow-Origin,将值设置为源域,然后发送响应。在这种情况下,浏览器会成功收到响应。

http://en.wikipedia.org/wiki/Cross-origin_resource_sharing

在 Web API 的前一个版本中,我们需要进行一些配置并添加一些文件才能实现此功能。本文介绍了一些在 Web API 1 中实现 CORS 的方法 http://blogs.msdn.com/b/carlosfigueira/archive/2012/02/21/implementing-cors-support-in-asp-net-web-apis-take-2.aspx

现在让我们看看如何在 Web API 2 中实现。

1. 安装 nuget 包 Microsoft.AspNet.WebApi.Cors

2. 配置 Web API 以启用 CORS 支持。

- 在全局级别,我们可以在 WebApiConfig 文件中完成

 public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            //Enable CORS support
            config.EnableCors(new EnableCorsAttribute("https://:9001", "*", "GET,POST,PUT"));

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }

它接受的参数是 origin、headers 和 http verbs。

- 我们还可以通过使用属性在控制器级别配置 CORS。

[EnableCors("https://:9001", "*", "GET,POST")]
    public class DepartmentController : ApiController{}

现在要测试 CORS 支持,请创建一个具有不同端口的另一个网站,并向上面的 DepartmentController 发起 AJAX 调用,您应该能够获取数据。

IHttpActionResult

让我向您介绍 Web API 2 的另一个功能。此版本的 Web API 引入了一个新接口 IHttpActionResult,用于返回 HTTP 响应消息。是的,听起来和 MVC ActionResult 相似,在某种程度上确实相似。

例如,实现包括 ConflictResult、OkResult、NotFoundResult 等。您会在下面的快照中找到很多。

 

让我们尝试使用 IHttpActionResult 修改其中一个 API 方法。

        public IHttpActionResult GetDepartmentByID(int id)
        {
            var department = departments.FirstOrDefault(x => x.ID == id);
            if(department == null)
            {
                //Return not found result
                return NotFound();
            }
            //Return successful ok result
            return Ok(department);
        }

因此,您可以看到,如果我们使用 IHttpActionResult 接口,返回适当的 HTTP 响应消息(如 NotFound 或 Ok)是多么容易。更不用说,由于我们使用的是接口,单元测试也变得更容易了。

 

帮助页面文档

一开始我告诉您一些关于 API 帮助页面的信息。现在让我们看看它是如何工作的。

一旦我们进入我们的示例 Web API 解决方案的主页,然后点击顶部的 API 菜单 https://:14129/Help,我们就会进入一个页面,在该页面上我们可以看到项目中存在的 ApiControllers 的列表和文档。

有一个 nuget 包 Microsoft.AspNet.WebApi.HelpPage 在后台完成这项神奇的工作。此 nuget 包的内容位于我们 webapi 项目的 HelpPage Areas 中。

 要为我们的文档添加描述信息,我们需要执行以下操作:

第一步是在项目生成属性中,勾选 XML 文档文件。

第二步是为我们的 API 方法添加 XML 注释。

        /// <summary>
        /// Gets the department collection.
        /// </summary>
        /// <returns>Department collection</returns>
        public IEnumerable<Department> Get()
        {
            return departments;
        }

第三步 - 在 HelpPage Areas 的 App_Start 文件夹中的 HelpPageConfig 类中,取消注释第一行。同时,如上所示,正确设置 XML 文件路径。

 public static void Register(HttpConfiguration config)
        {
            //// Uncomment the following to use the documentation from XML documentation file.
            config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/bin/SampleWebAPIv2.XML")));

现在运行项目并转到 API 菜单,我们就可以看到我们添加的文档了。

 

历史

这是我 ASP.NET MVC 5 特性系列文章的延续。

© . All rights reserved.