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

有了属性路由,我们甚至可以极端的抛弃约定的路由,从而实现”精准定位“,一切定位都可以牢牢的掌握在自己手中。相信这样,大家应该不会再害怕面对路由了吧。

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