Web API 1入门之Self-Host寄宿及路由原理(二)

前言

刚开始表面上感觉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方法的参数。

总结

综上所述,对于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() { ... }
时间: 2024-10-10 07:10:09

Web API 1入门之Self-Host寄宿及路由原理(二)的相关文章

水果项目第3集-asp.net web api开发入门

app后台开发,可以用asp.net webservice技术. 也有一种重量级一点的叫WCF,也可以用来做app后台开发. 现在可以用asp.net web api来开发app后台. Asp.net web api 官方定义: ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mo

Web API 强势入门指南

Web API是一个比较宽泛的概念.这里我们提到Web API特指ASP.NET Web API. 这篇文章中我们主要介绍Web API的主要功能以及与其他同类型框架的对比,最后通过一些相对复杂的实例展示如何通过Web API构建http服务,同时也展示了Visual Studio构建.net项目的各种强大. 目录 什么是 Web API 为什么要用 Web API 功能简介 Web API vs MVC Web API vs WCF Web API 实战 (Web API + MongoDB

用Web api /Nancy 通过Owin Self Host简易实现一个 Http 服务器

过去做 端游的Http 服务器 用的WebApi 或者Mvc架构,都是放在iis...而我已经是懒出一个地步,并不想去配iis,或者去管理iis,所以我很喜欢 Self host 的启动方式. C#做 http 有2个轻量级的框架, 一个是Nancy ,一个是 微软官方的Web Api 都可以通过owin self host 在应用程序中启动监听 Web Api 官方教程 :https://www.asp.net/web-api/overview/hosting-aspnet-web-api/u

Web API学习——Web API 强势入门指南

Web API是一个比较宽泛的概念.这里我们提到Web API特指ASP.NET Web API. 这篇文章中我们主要介绍Web API的主要功能以及与其他同类型框架的对比,最后通过一些相对复杂的实例展示如何通过Web API构建http服务,同时也展示了Visual Studio构建.net项目的各种强大. 什么是 Web API 两个关键点——可以对接各种客户端(浏览器,移动设备),构建http服务的框架. Web API在ASP.NET完整框架中地位如下图,与SignalR一起同为构建Se

ASP.NET Web API 2 入门(一)

前言 HTTP 不是只是为了服务的 web 页.这也是建设公开服务和数据的 Api 的强大平台.HTTP 是简单的. 灵活的和无处不在.你能想到的几乎任何平台有 HTTP 库,因此,HTTP 服务可以达到范围广泛的客户,包括浏览器. 移动设备和传统的桌面应用程序. ASP.NET Web API 是一个用于构建 web Api 在.NET 框架上框架.在本教程中,您将使用 ASP.NET Web API 来创建一个 web API 返回的产品列表. 创建一个 Web API 项目 在本教程中,您

01Getting Started---Getting Started with ASP.NET Web API 2入门WebApi2

HTTP 不只是为了生成 web 页面.它也是建立公开服务和数据的 Api 的强大平台.HTTP 是简单的. 灵活的和无处不在.你能想到的几乎任何平台有 HTTP 库,因此,HTTP 服务可以达到范围广泛的客户,包括浏览器. 移动设备和传统的桌面应用程序. ASP.NET Web API 是用于生成 web Api 在.NET 框架上的框架.在本教程中,您将使用 ASP.NET Web API 来创建 web API 返回的产品列表. 在本教程中使用的软件版本 视觉工作室 2013 年 Web

ASP.NET Web API 2 入门教程

译者:jiankunking 出处:http://blog.csdn.net/jiankunking 源码下载 HTTP不仅提供web页面服务,在构建公开服务和数据api方面,它也是一个强大的平台.HTTP简单.灵活.无处不在.几乎你能想到的所有的平台,都有一个HTTP库,因此HTTP服务可以影响到广泛的客户端,包括浏览器.移动设备,和传统的桌面应用程序. ASP.NET Web API是一个基于.NET框架用于构建Web API的框架.在本教程中,您将使用ASP.NET Web API创建一个

[翻译]ASP.NET Web API 2入门

原文:Getting Started with ASP.NET Web API 2 Step 1:新建一个Empty的Web API Project. Step 2:添加一个Model: public class Product { public int Id { get; set; } public string Name { get; set; } public string Category { get; set; } public decimal Price { get; set; }

ASP.NET Web API 之一 入门篇

一.基于RESTful标准的Web Api 原文讲解:https://www.cnblogs.com/lori/p/3555737.html 微软的web api是在vs2012上的mvc4项目绑定发行的,它提出的web api是完全基于RESTful标准的,完全不同于之前的(同是SOAP协议的)wcf和webService,它是简单,代码可读性强的,上手快的,如果要拿它和web服务相比,我会说,它的接口更标准,更清晰,没有混乱的方法名称,有的只有几种标准的请求,如get,post,put,de