webapi 筆記記錄(1)

打開vs2017,文件-新建-項目
在這裏插入圖片描述
  選擇空模板,勾選webapi
在這裏插入圖片描述

添加模型類,在右側資源管理器的Models文件夾上右鍵-添加-類

類的代碼如下:

  namespace ProductsApp.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

添加空的控制器,在Controllers文件夾上右鍵-添加-控制器,選擇Web API2 控制器 - 空

控制器名稱爲:ProductsController,代碼如下:

  using ProductsApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Http;

namespace ProductsApp.Controllers
{
    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 IHttpActionResult GetProduct(int id)
        {
            var product = products.FirstOrDefault((p) => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }
            return Ok(product);
        }
    }
}

調用路徑:http://localhost/api/Products

首先,我們看看App_Start文件夾下的WebApiConfig.cs文件,這個文件是用來配置路由的,代碼如下: 
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API 配置和服務
// Web API 路由
config.MapHttpAttributeRoutes();

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

上面的代碼,實質上就是定義了一個默認的路由規則。

我們再看看Global.asax

    protected void Application_Start()
    {
        GlobalConfiguration.Configure(WebApiConfig.Register);
    }

很明顯,在程序第一次啓動的時候,我們的路由規則就被配置加載了。這個默認的規則就是 api/{controller}/{id},其中{controller}匹配一個控制類,例如ProductsController,{id}是可選的,匹配的是public方法(也就是action)的參數。那麼,Controller裏的public方法,也就是action該如何匹配呢?

從官方的文檔可以查到這麼幾句話:If you are familiar with ASP.NET MVC, Web API routing is very similar to MVC routing. The main difference is that Web API uses the HTTP method, not the URI path, to select the action。意思就是,如果你熟悉MVC,那麼API的路由是跟MVC的路由非常相似的。兩者之間的不同,是Web API使用http方法,而非URI路徑去選擇action。這裏的action,就是我們Controller裏面的public 方法。

也就是說,默認路由api/{controller}/{id},首先匹配一個Controller類,然後用http請求方法匹配Action方法名,最後,用{id},匹配Action中的參數。

http請求方法是什麼東西?如果你是傳統的asp開發者,或是php開發者,相信都會非常熟悉。例如我們以前寫表單html,通常都會這樣寫:

....   裏面的method就是我們所說的http請求方法,最常見的就是get和post,get的話,就是將參數放到url上去提交,post的話,參數不會顯示在url中。更多的http方法,可以點擊這裏。

既然知道WebAPI的默認路由,是用http請求方法去匹配控制類中的action,那麼就好辦了,我們在地址欄輸入地址:http://localhost:1111/api/Products ,其實就是相當於在使用get方法與ProductsController中的Action進行匹配了。

然而,上面代碼中,兩個Action方法都沒明確表明是用什麼http請求方法,那怎麼確定調用哪一個方法呢?get跟GetAllProducts()到底有什麼關係呢,以至於GetAllProducts()可以被默認調用?或許有的人已經看出來了,沒錯,調用的方法GetAllProducts()那麼巧,也是以Get開頭的。這就是我們匹配的其中一個條件。如果Controller中,public方法的名字(也就是action的名字),是以"Get", “Post”, “Put”, “Delete”, “Head”, “Options”, 或 "Patch"開頭,那麼按照約定,該方法(action)匹配對應的http請求方法的調用。如果開頭沒有上述的關鍵字,默認表示該方法只支持Post。

例如GetAllProducts()方法,就表示使用http的get方法調用。DeleteProduct(int id)就表示用http的deletel方法調用。由於我們調用的地址是:http://localhost:1111/api/Products,翻譯成匹配規則就是,匹配ProductsController中,一個使用get,同時沒有參數的Action(也就是public 方法),即GetAllProducts()。如果我們有另一個Get方法,同時也是沒有參數的話,就會報錯。例如,我們增加一個方法:

    public string GetTest()
    {
        return "GetTest is called";
    }

該方法明顯也是匹配Get方法,同時沒有參數。重新生成下項目,然後用PostMan調用一下,會發現匹配多個的錯誤。

路由精準定位

首先,我們要打開App_Start文件夾中的WebApiConfig.cs文件,確保一下這句代碼存在:

public static void Register(HttpConfiguration config)
 {
     // 確保開啓了屬性路由
     config.MapHttpAttributeRoutes();

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

其次,要引入命名空間:using System.Web.Http;就是這麼簡單,我們就可以使用屬性路由。看到WebApiConfig.cs的代碼,有人擔心,會不會是因爲config.MapHttpAttributeRoutes()代碼在前,所以優先級才大於約定的路由呢,我們可以換一下次序,代碼改成這樣:

public static void Register(HttpConfiguration config)
{
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
        //次序在後
    config.MapHttpAttributeRoutes();
}

結果發現,屬性路由的優先級依然大於約定的路由,如果你還擔心,大可直接刪除約定的路由,將上述的代碼改成這樣:

    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
    }

這樣,你就可以完全不用考慮約定的路由對屬性路由的影響,一切的映射都在你的牢牢掌握中。不過壞處就是,你的每個方法,都要顯式指定http方法和路由。但我覺得這個代價是值得的,因爲我們再不用花時間去繞來繞去,再不用擔心增加了新方法會不會造成路由衝突等問題,我們只需定好命名規則,保證我們每個方法定義的路由不重複即可。說着說着,我也覺得自己跟文章開頭說的那位程序猿一樣,在走向一個極端,他是在玩命的用路由,而我是在拼命的阻止路由的多匹配性,追求唯一確定,儘量不讓路由造成我的負擔,也許,這也是一種風格?

剩下的都是很簡單,例如,路由前綴,還是用官方的例子:

public class BooksController : ApiController
{
    [Route("api/books")]
    public IEnumerable<Book> GetBooks() { ... }

    [Route("api/books/{id:int}")]
    public Book GetBook(int id) { ... }

    [Route("api/books")]
    [HttpPost]
    public HttpResponseMessage CreateBook(Book book) { ... }
}

每個方法的路由前綴都是“api/books",是不是顯得很重複,我們可以將前綴抽取,爲整個控制器增加公共的前綴,代碼如下:

[RoutePrefix("api/books")]
public class BooksController : ApiController
{
    // GET api/books
    [Route("")]
    public IEnumerable<Book> Get() { ... }

    // GET api/books/5
    [Route("{id:int}")]
    public Book Get(int id) { ... }

    // POST api/books
    [Route("")]
    public HttpResponseMessage Post(Book book) { ... }
}

路由前綴的重寫,我們可以使用波浪符對前綴進行重寫,例如:

[RoutePrefix("api/books")]
public class BooksController : ApiController
{
    // GET /api/authors/1/books
    [Route("~/api/authors/{authorId:int}/books")]
    public IEnumerable<Book> GetByAuthor(int authorId) { ... }

    // ...
}

除此之外,還有路由約束,例如Route(“api/books/{id:int}”),表示id是一個32位整數,如果是可選的,可以在後面加"?",例如Route(“api/books/{id:int?}”)

更詳細的使用可以參考官網文檔:https://docs.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

有了屬性路由,我們甚至可以極端的拋棄約定的路由,從而實現”精準定位“,一切定位都可以牢牢的掌握在自己手中。相信這樣,大家應該不會再害怕面對路由了吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章