前言

刚开始表面上感觉Web API内容似乎没什么,也就是返回JSON数据,事实上远非我所想,不去研究不知道,其中的水还是比较深,那又如何,一步一个脚印来学习都将迎刃而解。

Self-Host

我们知道Web API它可以快速为HTTP客户端提供API来创建Web服务,为何如此这样说呢?因为我们可以将其作为主机也就是一个服务器来用完全不需要IIS,这就是我们下面要讲的第一个内容Self-Host,实现对Web API寄宿的方式有多种并且都是独立于ASP.NET框架之外,如下Self-Host寄宿是存在于Web API 1中的,而在Web API 2中实现寄宿是采用Web-Host来进行寄宿(通过程序包packages中的Web-Host以及Web Client可得知),因为Web API本身是无法提供【请求-响应】的机制,所以需要寄宿来实现,即通过具体的应用程序来为Web API运行提供一个环境。下面且听我娓娓道来。既然是进行交互必然有服务器和客户端,下面我们将从建立控制台应用程序开始来进行了解。

Web API服务器

第一步

建立一个SelfHost的控制台应用程序,添加【Microsoft.AspNet.WebApi.SelfHost】程序包,搜索时会出现多个包注意不是【AspNetWebApi.SelfHost.】如下:

第二步

添加类,如下:

    public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
}

第三步

添加派生自APiController控制的类以及要演示的数据,如下:

    public class ProductsController : ApiController
{
Product[] products = new Product[]
{
new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
}; public IEnumerable<Product> GetAllProducts()
{
return products;
} public Product GetProductById(int id)
{
var product = products.FirstOrDefault((p) => p.Id == id);
if (product == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound); //如果未找到数据并返回状态码404
}
return product;
} public IEnumerable<Product> GetProductsByCategory(string category)
{
return products.Where(p => string.Equals(p.Category, category,
StringComparison.OrdinalIgnoreCase));
}
}

第四步

在控制台主程序中配置服务器以及添加路由

var config = new HttpSelfHostConfiguration("http://localhost:8080"); //配置主机

config.Routes.MapHttpRoute(    //配置路由
"API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional }); using (HttpSelfHostServer server = new HttpSelfHostServer(config)) //监听HTTP
{
server.OpenAsync().Wait(); //开启来自客户端的请求
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
}

以上就是关于Web API关于主机的设置,接下来就是建立一个客户端来访问此服务器上的资源。

Web API客户端

第一步

同理建立一个ClientApp的控制台应用程序,同时添加Web API客户端程序包【Microsoft.AspNet.WebApi.Client】如下:

第二步

既然是要访问服务器上的资源,自然要添加对Web API服务器(SelfHost)的引用了。

第三步

接下来就是建立客户端并读取服务器上的资源

 static HttpClient client = new HttpClient(); //利用此对象进行对Web API的调用

 static void ListAllProducts()
{
HttpResponseMessage resp = client.GetAsync("api/products").Result;
resp.EnsureSuccessStatusCode(); var products = resp.Content.ReadAsAsync<IEnumerable<SelfHost.Product>>().Result;
foreach (var p in products)
{
Console.WriteLine("{0} {1} {2} ({3})", p.Id, p.Name, p.Price, p.Category);
}
} static void ListProduct(int id)
{
var resp = client.GetAsync(string.Format("api/products/{0}", id)).Result;
resp.EnsureSuccessStatusCode(); var product = resp.Content.ReadAsAsync<SelfHost.Product>().Result;
Console.WriteLine("ID {0}: {1}", id, product.Name);
} static void ListProducts(string category)
{
Console.WriteLine("Products in '{0}':", category); string query = string.Format("api/products?category={0}", category); var resp = client.GetAsync(query).Result;
resp.EnsureSuccessStatusCode(); var products = resp.Content.ReadAsAsync<IEnumerable<SelfHost.Product>>().Result;
foreach (var product in products)
{
Console.WriteLine(product.Name);
}
}
  • 通过调用HttpClient.GetAsync来发出一个Get请求来请求服务器上的Uri资源。

  • 通过调用HttpResponseMessage.EnsureSuccessStatusCode方法来确定请求是否成功,若失败即返回错误状态码则抛出一个异常。

  • 通过调用ReadAsAsync<T>将HTTP中响应的数据类型进行反序列化,该方法为一个扩展方法。

GetAsync和ReadAsync为异步方法,直到获得结果值即Result属性的值操作完成,否则将一直阻塞线程。

第四步

最后在客户端控制台主程序中建立客户端与服务器端的通信服务即可

    client.BaseAddress = new Uri("http://localhost:8080");

    ListAllProducts();
ListProduct(1);
ListProducts("toys"); Console.WriteLine("Press Enter to quit.");
Console.ReadLine();

接下来就是启动Web API服务器程序,通过Web API客户端来访问服务器并获得其请求的资源。【注意】windows 8系统启动服务器必须以管理员身份运行,否则报错。

访问资源成功,如下:

路由原理

如果对MVC框架中路由熟悉的话,Web API的路由原理和其相似,但是不同的是Web API是使用的HTTP方法来选择Action方法而不是通过URI路径来选择Action方法。

路由表

在Web API中处理HTTP请求的是一个控制器类,控制器中的公有方法叫做Action方法,当Web API框架获得一个请求时,会根据请求路由到一个Action方法上。为了决定调用哪个Action方法,Web API框架利用路由表来决定,当创建项目模板时将在App_Start文件下的 WebApiConfig 创建一个默认的路由,如下:

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

当我们需要将Web API作为服务器时此时必须直接在HttpSelfHostConfiguration上设置路由表【上述关于SelfHost已经演示】

注册路由

在Web API路由配置文件中有一个 Register 方法,该方法中有一个 HttpConfiguration 实例参数,该HttpConfiguration是消息管道中的全局对象,我们可以通过其对管道某一个行为作出相关操作,从而达到我们所需。通过HttpConfiguration上的属性_routes即 HttpRouteCollection 对象中的 CreateRoute 方法再依据默认的路由模板、默认值以及相关约束来创建实现了 IHttpRoute 接口的路由对象即 HttpRoute ,并通过HttpConfiguration中的Routes对象中的扩展方法MapHttpRoute方法来达到注册路由映射的目的,当然我们也可以直接通过调用路由集合HttpRouteCollection中的 Add 方法来注册路由到路由表中。

路由表中每一条都包含一个对应的路由模板,Web API默认的路由模板是api/{controller}/{id} ,在此模板下,api是一个路径段,而{controller}和{id}是占位符。当Web API收到HTTP请求时,它尝试去匹配路由表中的路由模板之一的URI,若没匹配上,则返回404。

例如,用下面的URI来匹配默认的路由

/api/contacts

/api/contacts/1

/api/products/gizmo1

【注意】为何在Web API的控制器中以API开头的原因是避免和MVC框架中的路由冲突,如/contacts会进入到MVC路由中,而/api/contacts会进入到Web API框架中,当然我们可以通过改变默认的路由表来改变约定,但是不建议这么做。

一旦一个匹配的路由被找到,Web API根据默认约定来选择Controller和Action:

  • Web API会用Controller替代占位符{controller}的值中来找到控制器。

  • Web API着眼根据HTTP方法来找到Action方法,然后找到一个以一个HTTP方法的名称开始的Action方法,例如,对于一个Get请求,Web API会找到一以Get开头的Action方法,如:GetContact或者GetAllContact的Action方法。这种约定同样也适用于GET、POST、PUT以及DELETE方法。我们通过在控制器上使用特性来启用其他的方法。

  • 在路由模板中的其他的占位符,如{id}被映射到action方法参数上。

查找控制器

当一个URI资源请求过来时Web API框架会查找已经注册的路由表中的路由并进行匹配,如果匹配通过,此时将创建一个包含每个占位符的值的字典,字典的键为占位符的名称,当然不包括大括号,字典的值为请求过来的URI中的值或者是路由模板中的默认值,然后这个字典将会保存在 IHttpRouteData 对象中的字典中。该对象还存在一个路由对象IHttpRoute。当请求的URI过来时,此时会通过请求信息即 HttpRequestMessage  对象中的 RquestUri 属性来获得其URI,接下来HttpRoute会接受到HttpRequestMessage中的URI并进行解析,然后将通过调用 GetRouteData 等方法来封装路由数据给实现了IHttpRouteData接口的 HttpRouteData ,通过HttpRouteData中的构造函数中的路由对象来获取传递过来的路由对象HttpRoute(该路由对象也根据请求过来的路由变量来绑定到路由模板中最终生成一个完整的URL),同时因为实现了IHttpRouteData接口则此时该接口的字典将传递给HttpRouteData构造函数中的路由字典即 HttpRouteValueDictionary 。【注意】GetRouteData方法中的参数有一个为virtualPathRoot即虚拟根路径,当执行此方法时得到的是相对路径,也就是说通过路由模板进行匹配是根据相对路径来进行匹配的。

查找控制器是通过 IHttpControllerSelector 接口上的 SelectController  ,此方法参数为HttpRequestMessage的实例并返回一个 HttpControllerDescriptor ,此接口的默认实现是通过 DefaultHttpControllerSelector  类实现,此类实现的算法有如下三点:

  • 在上述路由字典即HttpRouteValueDictionary中查找controller的键。

  • 获取键的值并将字符串Controller作为控制器的类型名。

  • 用此类型名来查找一个Web API控制器。

查找Action

在查找到控制器之后,框架将会通过 IHttpActionSelector 接口中的 SelectAction 方法来查找Action方法,该方法参数要获取一个控制器上下文即 HttpControllerContext 返回一个 HttpActionDescriptor 对象实例。其默认实现是通过 APiControllerActionSelector 来提供的,要查找到Action要经过如下三点:

  • 请求的HTTP方法。

  • 在路由模板中占位符{action}对应的值。

  • 在控制器上Action方法的参数。

【注意】如何确定在控制上的方法是Action方法呢?当查找Action方法时,仅仅只着眼于控制上的公有的实例方法(不包括从APiController上继承的特殊名称的方法,如:重载、事件、构造等等)并且是从APiController类上继承的方法。

总结

综上所述,对于Web API上的一个路由系统总共有三个阶段

  • 匹配一个URI到一个路由模板。

  • 选择一个控制器。

  • 选择一个Action方法。

当然你可以对路由模板进行自定义以及相关参数利用正则表达式进行约束等,这就不再详细描述。

接下来我们就上述叙述来进行相关例子

public class ProductsController : ApiController
{
public void GetAllProducts() { }
public IEnumerable<Product> GetProductById(int id) { }
public HttpResponseMessage DeleteProduct(int id){ }
}

对于以下可能的HTTP请求,会对于每个请求对应应该被调用的Action方法。

HTTP Method URI PATH Action Parameter
GET api/products GetAllProducts (none)
GET api/products/4 GetProductById 4
DELETE api/products/4 DeleteProduct 4
POST api/products (no match)  

【注意】在上述中的URI的{id},如果存在则会被映射到Action方法中的id参数中,如上述中两种Get方法,一个有id参数一个没有没有id参数。同时也应注意POST请求将会失败,因为在控制器中没有定义一个POST....方法。

根据约定来进行映射Action方法注意

假设Web API控制器名称ProductController并继承APiController,在该控制器下有如下两个方法:

        public Product GetProductById(int id)
{
var product = products.FirstOrDefault((p) => p.Id == id);
if (product == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return product;
} public string Get(int id)
{
return "value";
}

当发出Get(如:/api/product/1)请求时将会出错,出错原因为匹配到多个路由,默认能够匹配到GetProductById方法就不用说了,此时同样能匹配到Get方法,所以此时需要将Get标记为【NonAction】或者采用其他方法来区别这两个路由。

HTTP方法

除了使用约定的HTTP方法之外,我们也可以使用特性HttpGet、HttpPost、HttpPut以及HttpDelete来修饰Action方法来显式指定一个Action的HTTP方法。

在下面的例子中,FindProduct方法会被映射到GET请求。

public class ProductsController : ApiController
{
[HttpGet]
public Product FindProduct(id) {}
}

我们可以使用 AcceptVerbs 特性对一个Action使用多个HTTP方法或者说是使用HTTP方法而不是POST、DELETE、GET以及PUT,如下:

多个请求到同一Action

        [AcceptVerbs("GET", "Post")]
public Product FindProduct(int id)
{ } 或者
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public Product FindProduct(int id)
{ }

根据请求不同来响应相同Action,如下:

        public Product FindProduct(int id)
{ } [AcceptVerbs(HttpVerbs.Post]
public Product FindProduct(string guid) { }

通过Action名称配置路由

由于默认的路由模板,Web API使用HTTP方法来选择Action,但是我们可以创建一个包括在URI中的Action名称的路由。如下:

routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);

在上述路由模板中,{action} 参数名称命名了在控制器上的Action方法,在此种路由下,可以使用特性来指定HTTP方法,例如,允许控制器有如下方法:

public class ProductsController : ApiController
{
[HttpGet]
public string Details(int id);
}

在此种情况下,对于api/products/details的GET请求会映射到Details方法,这种路由风格和MVC相似。

通过使用ActionName特性来覆盖Action的名称,例如下列例子,有两种映射到如api/products/thumbnail/id的方法,一种支持GET请求,一种支持POST请求

public class ProductsController : ApiController
{
[HttpGet]
[ActionName("Thumbnail")]
public HttpResponseMessage GetThumbnailImage(int id); [HttpPost]
[ActionName("Thumbnail")]
public void AddThumbnailImage(int id);
}

Non-Actions

为了阻止一个方法作为Action方法被调用,通过使用Non-Actions特性来将其标记为不是一个Action方法,即使这个方法匹配到了路由规则

// Not an action method.
[NonAction]
public string GetPrivateData() { ... }

Web APi入门之Self-Host寄宿及路由原理的更多相关文章

  1. Web API 入门指南 - 闲话安全

    Web API入门指南有些朋友回复问了些安全方面的问题,安全方面可以写的东西实在太多了,这里尽量围绕着Web API的安全性来展开,介绍一些安全的基本概念,常见安全隐患.相关的防御技巧以及Web AP ...

  2. 转载-Web API 入门

    An Introduction to ASP.NET Web API 目前感觉最好的Web API入门教程 HTTP状态码 Web API 强势入门指南 Install Mongodb Getting ...

  3. Web API入门指南(安全)转

    安全检测的工具站点:https://www.owasp.org/index.php/Category:Vulnerability_Scanning_Tools Web API入门指南有些朋友回复问了些 ...

  4. Web API 入门指南

    Web API 入门指南 - 闲话安全2013-09-21 18:56 by 微软互联网开发支持, 231 阅读, 3 评论, 收藏, 编辑 Web API入门指南有些朋友回复问了些安全方面的问题,安 ...

  5. (转)Web API 入门指南 - 闲话安全

    原文地址:http://www.cnblogs.com/developersupport/p/WebAPI-Security.html Web API入门指南有些朋友回复问了些安全方面的问题,安全方面 ...

  6. 【ASP.NET Web API教程】1 ASP.NET Web API入门

    原文 [ASP.NET Web API教程]1 ASP.NET Web API入门 Getting Started with ASP.NET Web API第1章 ASP.NET Web API入门 ...

  7. Web API 入门 二 媒体类型

    还是拿上面 那篇 Web API 入门 一  的那个来讲 在product类中加一个时间属性

  8. ASP.NET Web API WebHost宿主环境中管道、路由

    ASP.NET Web API WebHost宿主环境中管道.路由 前言 上篇中说到ASP.NET Web API框架在SelfHost环境中管道.路由的一个形态,本篇就来说明一下在WebHost环境 ...

  9. ASP.NET Web API Selfhost宿主环境中管道、路由

    ASP.NET Web API Selfhost宿主环境中管道.路由 前言 前面的几个篇幅对Web API中的路由和管道进行了简单的介绍并没有详细的去说明一些什么,然而ASP.NET Web API这 ...

  10. Web APi入门之Self-Host寄宿及路由原理(二)

    前言 刚开始表面上感觉Web API内容似乎没什么,也就是返回JSON数据,事实上远非我所想,不去研究不知道,其中的水还是比较深,那又如何,一步一个脚印来学习都将迎刃而解. Self-Host 我们知 ...

随机推荐

  1. Ionic的页面堆栈与Tabs菜单相遇的问题(页面堆栈有多个)

    本来的需求: 新建的Ionic项目是Tabs菜单,假设有两个选项卡 A 和 B(从左到右),对应的两个页面的代码完全一样,使用了echarts 插件,并且使用了一个获取页面元素的方法,给自己的一个变量 ...

  2. thinkPHP框架5.0 类图下载

    thinkPHP5.0 类图下载

  3. 用keras实现基本的回归问题

    数据集介绍 共有506个样本,拆分为404个训练样本和102个测试样本 该数据集包含 13 个不同的特征: 人均犯罪率. 占地面积超过 25000 平方英尺的住宅用地所占的比例. 非零售商业用地所占的 ...

  4. 第二届强网杯wp

    web web签到 利用了md5碰撞 payload为 param1 =%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b% ...

  5. 20165223 实验四 Android开发基础

    实验四 Android开发基础 目录 一.实验报告封面 二.具体实验内容 (一)Android Stuidio的安装测试 (二)Activity测试 (三)UI测试 (四)布局测试 (五)教材代码测试 ...

  6. 牛客练习赛28 B数据结构(线段树)

    链接:https://www.nowcoder.com/acm/contest/200/B来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言5242 ...

  7. Building Microservices with Spring Boot and Apache Thrift. Part 1 with servlet

    https://dzone.com/articles/building-microservices-spring In the modern world of microservices it's i ...

  8. C# DateTimePicker控件获取他的年,月,日,时,分,秒

    CustomFormat属性设置为: yyyy-MM-dd HH:mm:ss 记住还要修改一个属性值,DateFormat属性 可选项改为Custom,默认是Long

  9. 2018.02.12 noip模拟赛T2

    二兵的赌注 Description游戏中,二兵要进入了一家奇怪的赌场.赌场中有n个庄家,每个庄家都可以猜大猜小,猜一次一元钱.每一次开彩前,你都可以到任意个庄家那里下赌注.如果开彩结果是大,你就可以得 ...

  10. A1120. Friend Numbers

    Two integers are called "friend numbers" if they share the same sum of their digits, and t ...