• WebApi2官网学习记录---Attribute Routing


    从WebApi 1迁移到WebAPI 2要改变配置代码如下:

    WebApi 1:

    protected void Application_Start()
    {
        // WARNING - Not compatible with attribute routing.
        WebApiConfig.Register(GlobalConfiguration.Configuration);
    }

    WebAPI 2:

    protected void Application_Start()
    {
        // Pass a delegate to the Configure method.
        GlobalConfiguration.Configure(WebApiConfig.Register);
    }

    添加一个Route Attributes

    public class OrdersController : ApiController
    {
        [Route("customers/{customerId}/orders")]
        [HttpGet]
        public IEnumerable<Order> FindOrdersByCustomer(int customerId) { ... }
    }

    可以匹配一下URL: 

    • http://localhost/customers/1/orders
    • http://localhost/customers/bob/orders
    • http://localhost/customers/1234-5678/orders

    匹配http post请求的CreateBook

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

    Route Prefixes

     对于同一个controller路由有相同的前缀, 此时可以使用 [RoutePrefix] attribute 

    [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) { ... }
    }

    可以在方法的attribute中使用(~)重写路由前缀  

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

    路由前缀中可以包含参数 

    [RoutePrefix("customers/{customerId}")]
    public class OrdersController : ApiController
    {
        // GET customers/1/orders
        [Route("orders")]
        public IEnumerable<Order> Get(int customerId) { ... }
    }

    路由约束

      路由约束用于严格匹配路由中的参数,通用语法是{参数:约束},如下:  

    [Route("users/{id:int}"]
    public User GetUserById(int id) { ... }
    
    [Route("users/{name}"]
    public User GetUserByName(string name) { ... }

           支持的约束如下表所示: 

    ConstraintDescriptionExample
    alpha Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z) {x:alpha}
    bool Matches a Boolean value. {x:bool}
    datetime Matches a DateTime value. {x:datetime}
    decimal Matches a decimal value. {x:decimal}
    double Matches a 64-bit floating-point value. {x:double}
    float Matches a 32-bit floating-point value. {x:float}
    guid Matches a GUID value. {x:guid}
    int Matches a 32-bit integer value. {x:int}
    length Matches a string with the specified length or within a specified range of lengths. {x:length(6)}
    {x:length(1,20)}
    long Matches a 64-bit integer value. {x:long}
    max Matches an integer with a maximum value. {x:max(10)}
    maxlength Matches a string with a maximum length. {x:maxlength(10)}
    min Matches an integer with a minimum value. {x:min(10)}
    minlength Matches a string with a minimum length. {x:minlength(10)}
    range Matches an integer within a range of values. {x:range(10,50)}
    regex Matches a regular expression. {x:regex(^d{3}-d{3}-d{4}$)}

       注意:带括号的约束,如"min",可以对路由中的参数应用多个约束,用冒号分割约束     

    [Route("users/{id:int:min(1)}")]
    public User GetUserById(int id) { ... }

     自定义路由约束

    public class NonZeroConstraint : IHttpRouteConstraint
    {
        public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, 
            IDictionary<string, object> values, HttpRouteDirection routeDirection)
        {
            object value;
            if (values.TryGetValue(parameterName, out value) && value != null)
            {
                long longValue;
                if (value is long)
                {
                    longValue = (long)value;
                    return longValue != 0;
                }
    
                string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
                if (Int64.TryParse(valueString, NumberStyles.Integer, 
                    CultureInfo.InvariantCulture, out longValue))
                {
                    return longValue != 0;
                }
            }
            return false;
        }
    }
    
    修改WebAPIConfig.cs
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            var constraintResolver = new DefaultInlineConstraintResolver();
            constraintResolver.ConstraintMap.Add("nonzero", typeof(NonZeroConstraint));
    
            config.MapHttpAttributeRoutes(constraintResolver);
        }
    }
    
    实际应用:
    [Route("{id:nonzero}")]
    public HttpResponseMessage GetNonZero(int id) { ... }
    View Code

    可选的URI参数和默认值

     第一种方式,默认值直接分配给了方法参数,因此参数获得的是精确的值

     第二种方式,默认值通过model-binding进行处理,默认的model-binder将字符串类型的默认值"1033"转为int形的1033,对于model-binder可能有所不同。

     1 第一种写法
     2 public class BooksController : ApiController
     3 {
     4     [Route("api/books/locale/{lcid:int?}")]
     5     public IEnumerable<Book> GetBooksByLocale(int lcid = 1033) { ... }
     6 }
     7 
     8 第二种写法
     9 public class BooksController : ApiController
    10 {
    11     [Route("api/books/locale/{lcid:int=1033}")]
    12     public IEnumerable<Book> GetBooksByLocale(int lcid) { ... }
    13 }
    View Code

    Route Names

     web api中每个路由有一个name,Route names可以用来生成links

     1 public class BooksController : ApiController
     2 {
     3     [Route("api/books/{id}", Name="GetBookById")]
     4     public BookDto GetBook(int id) 
     5     {
     6         // Implementation not shown...
     7     }
     8 
     9     [Route("api/books")]
    10     public HttpResponseMessage Post(Book book)
    11     {
    12         // Validate and add book to database (not shown)
    13 
    14         var response = Request.CreateResponse(HttpStatusCode.Created);
    15 
    16         // Generate a link to the new book and set the Location header in the response.
    17         string uri = Url.Link("GetBookById", new { id = book.BookId });
    18         response.Headers.Location = new Uri(uri);
    19         return response;
    20     }
    21 }
    View Code

    Route Order

     通过设置RouteOrder属性在route attribute上,可以指定Route的顺序,值越小先被计算,默认顺序对应的值是0

      路由顺序的选择:

    • 比较RouteOrder属性
    • 对于URI部分按如下进行排序
    1. URI的固定部分(literal segments)  
    2. 带约束的路由参数
    3. 不带约束的路由参数
    4. 带约束的通配符
    5. 不带约束的通配符
    • route的排序不区分大小写的     
    [RoutePrefix("orders")]
    public class OrdersController : ApiController
    {
        [Route("{id:int}")] // constrained parameter
        public HttpResponseMessage Get(int id) { ... }
    
        [Route("details")]  // literal
        public HttpResponseMessage GetDetails() { ... }
    
        [Route("pending", RouteOrder = 1)]
        public HttpResponseMessage GetPending() { ... }
    
        [Route("{customerName}")]  // unconstrained parameter
        public HttpResponseMessage GetByCustomer(string customerName) { ... }
    
        [Route("{*date:datetime}")]  // wildcard
        public HttpResponseMessage Get(DateTime date) { ... }
    }

     路由排序如下:

    1. orders/{details}
    2. orders/{id}
    3. orders/{customreName}
    4. orders/{*date}
    5. orders/pending

     Route Attribute的使用完整例子:  

      1 using BooksAPI.DTOs;
      2 using BooksAPI.Models;
      3 using System;
      4 using System.Data.Entity;
      5 using System.Linq;
      6 using System.Linq.Expressions;
      7 using System.Threading.Tasks;
      8 using System.Web.Http;
      9 using System.Web.Http.Description;
     10 
     11 namespace BooksAPI.Controllers
     12 {
     13     [RoutePrefix("api/books")]
     14     public class BooksController : ApiController
     15     {
     16         private BooksAPIContext db = new BooksAPIContext();
     17 
     18         // Typed lambda expression for Select() method. 
     19         private static readonly Expression<Func<Book, BookDto>> AsBookDto =
     20             x => new BookDto
     21             {
     22                 Title = x.Title,
     23                 Author = x.Author.Name,
     24                 Genre = x.Genre
     25             };
     26 
     27         // GET api/Books
     28         [Route("")]
     29         public IQueryable<BookDto> GetBooks()
     30         {
     31             return db.Books.Include(b => b.Author).Select(AsBookDto);
     32         }
     33 
     34         // GET api/Books/5
     35         [Route("{id:int}")]
     36         [ResponseType(typeof(BookDto))]
     37         public async Task<IHttpActionResult> GetBook(int id)
     38         {
     39             BookDto book = await db.Books.Include(b => b.Author)
     40                 .Where(b => b.BookId == id)
     41                 .Select(AsBookDto)
     42                 .FirstOrDefaultAsync();
     43             if (book == null)
     44             {
     45                 return NotFound();
     46             }
     47 
     48             return Ok(book);
     49         }
     50 
     51         [Route("{id:int}/details")]
     52         [ResponseType(typeof(BookDetailDto))]
     53         public async Task<IHttpActionResult> GetBookDetail(int id)
     54         {
     55             var book = await (from b in db.Books.Include(b => b.Author)
     56                               where b.AuthorId == id
     57                               select new BookDetailDto
     58                               {
     59                                   Title = b.Title,
     60                                   Genre = b.Genre,
     61                                   PublishDate = b.PublishDate,
     62                                   Price = b.Price,
     63                                   Description = b.Description,
     64                                   Author = b.Author.Name
     65                               }).FirstOrDefaultAsync();
     66 
     67             if (book == null)
     68             {
     69                 return NotFound();
     70             }
     71             return Ok(book);
     72         }
     73 
     74         [Route("{genre}")]
     75         public IQueryable<BookDto> GetBooksByGenre(string genre)
     76         {
     77             return db.Books.Include(b => b.Author)
     78                 .Where(b => b.Genre.Equals(genre, StringComparison.OrdinalIgnoreCase))
     79                 .Select(AsBookDto);
     80         }
     81 
     82         [Route("~api/authors/{authorId}/books")]
     83         public IQueryable<BookDto> GetBooksByAuthor(int authorId)
     84         {
     85             return db.Books.Include(b => b.Author)
     86                 .Where(b => b.AuthorId == authorId)
     87                 .Select(AsBookDto);
     88         }
     89 
     90         [Route("date/{pubdate:datetime:regex(\d{4}-\d{2}-\d{2})}")]
     91         [Route("date/{*pubdate:datetime:regex(\d{4}/\d{2}/\d{2})}")]
     92         public IQueryable<BookDto> GetBooks(DateTime pubdate)
     93         {
     94             return db.Books.Include(b => b.Author)
     95                 .Where(b => DbFunctions.TruncateTime(b.PublishDate)
     96                     == DbFunctions.TruncateTime(pubdate))
     97                 .Select(AsBookDto);
     98         }
     99 
    100         protected override void Dispose(bool disposing)
    101         {
    102             db.Dispose();
    103             base.Dispose(disposing);
    104         }
    105     }
    106 }
    View Code

    其中以下路由中的匹配符*的作用:

      [Route("date/{pubdate:datetime:regex(\d{4}-\d{2}-\d{2})}")]
            [Route("date/{pubdate:datetime:regex(\d{4}/\d{2}/\d{2})}")]  // new
            public IQueryable<BookDto> GetBooks2(DateTime pubdate)
            {
                /*
                 * 路由中的*匹配符的作用,是为了告诉route engine占位符{pubdate}应该匹配剩余的URI,
                 * 默认情况下,一个模板参数仅仅匹配一个URI。
                 * This tells the routing engine that {pubdate} should match the rest of the URI. By default, 
                 * a template parameter matches a single URI segment. In this case, we want {pubdate} to 
                 * span several URI segments:
                 */
               
            }

         

  • 相关阅读:
    605. Can Place Flowers
    1184. Distance Between Bus Stops
    1711. Count Good Meals
    1710. Maximum Units on a Truck
    566. Reshape the Matrix
    980. Unique Paths III
    212. Word Search II
    每日总结
    每日总结
    每日总结
  • 原文地址:https://www.cnblogs.com/goodlucklzq/p/4433552.html
Copyright © 2020-2023  润新知