标签:
刚开始表面上感觉Web API内容似乎没什么,也就是返回JSON数据,事实上远非我所想,不去研究不知道,其中的水还是比较深,那又如何,一步一个脚印来学习都将迎刃而解。
public class Product { public int Id { get; set; } public string Name { get; set; } public string Category { get; set; } public decimal Price { get; set; } }
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关于主机的设置,接下来就是建立一个客户端来访问此服务器上的资源。
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); } }
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方法。
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 方法来注册路由到路由表中。
例如,用下面的URI来匹配默认的路由
/api/contacts /api/contacts/1 /api/products/gizmo1
【注意】为何在Web API的控制器中以API开头的原因是避免和MVC框架中的路由冲突,如/contacts会进入到MVC路由中,而/api/contacts会进入到Web API框架中,当然我们可以通过改变默认的路由表来改变约定,但是不建议这么做。
一旦一个匹配的路由被找到,Web API根据默认约定来选择Controller和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 类实现,此类实现的算法有如下三点:
在查找到控制器之后,框架将会通过 IHttpActionSelector 接口中的 SelectAction 方法来查找Action方法,该方法参数要获取一个控制器上下文即 HttpControllerContext 返回一个 HttpActionDescriptor 对象实例。其默认实现是通过 APiControllerActionSelector 来提供的,要查找到Action要经过如下三点:
综上所述,对于Web API上的一个路由系统总共有三个阶段
当然你可以对路由模板进行自定义以及相关参数利用正则表达式进行约束等,这就不再详细描述。
接下来我们就上述叙述来进行相关例子
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....方法。
假设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"; }
除了使用约定的HTTP方法之外,我们也可以使用特性HttpGet、HttpPost、HttpPut以及HttpDelete来修饰Action方法来显式指定一个Action的HTTP方法。
public class ProductsController : ApiController { [HttpGet] public Product FindProduct(id) {} }
我们可以使用 AcceptVerbs 特性对一个Action使用多个HTTP方法或者说是使用HTTP方法而不是POST、DELETE、GET以及PUT,如下:
[AcceptVerbs("GET", "Post")] public Product FindProduct(int id) { } 或者 [AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)] public Product FindProduct(int id) { }
public Product FindProduct(int id) { } [AcceptVerbs(HttpVerbs.Post] public Product FindProduct(string guid) { }
由于默认的路由模板,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相似。
public class ProductsController : ApiController { [HttpGet] [ActionName("Thumbnail")] public HttpResponseMessage GetThumbnailImage(int id); [HttpPost] [ActionName("Thumbnail")] public void AddThumbnailImage(int id); }
// Not an action method. [NonAction] public string GetPrivateData() { ... }
Web API 1入门之Self-Host寄宿及路由原理(二)
标签:
原文地址:http://www.cnblogs.com/CreateMyself/p/4813004.html