.NET Core开发实战课程备忘(17) -- 路由与终结点:如何规划好你的Web API

路由注册方式

  • 路由模板的方式
  • RouteAttribute方式

路由约束

  • 类型约束
  • 范围约束
  • 正则表达式
  • 是否必选
  • 自定义IRouteConstraint

Url生成

  • LinkGenerator
  • IUrlHelper

代码实现

创建项目

创建名字为RoutingDemoASP.NET Core项目,类型为API,为了方便演示,这里配置了swagger的组件,需要引入以下的包

1
Swashbuckle.AspNetCore

然后右键项目名称->属性->生成->输出,勾选上XML文档文件

回到Startup.ConfigureServices方法,在services.AddControllers();后面新增以下代码:

1
2
3
4
5
6
7
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo() {Title = "My API", Version = "v1"});
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});

Startup.Configureif(env.IsDevelopment()){...}这个if判断后面,新增以下代码:

1
2
app.UseSwagger();
app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); });

添加测试控制器

新增控制器OrderController,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;

namespace RoutingDemo.Controllers
{
/// <summary>
/// 订单控制器
/// </summary>
[Route("api/[controller]/[action]")]
[ApiController]
public class OrderController : ControllerBase
{
/// <summary>
/// 订单是否存在
/// </summary>
/// <param name="id">必须可以转为long</param>
/// <returns></returns>
//[HttpGet("{id:MyRouteConstraint}")]
[HttpGet("{id:isLong}")]
public bool OrderExist([FromRoute]string id)
{
return true;
}

/// <summary>
/// 订单最大数量
/// </summary>
/// <param name="id">最大20</param>
/// <param name="linkGenerator"></param>
/// <returns></returns>
[HttpGet("{id:max(20)}")]
public bool Max(long id, [FromServices] LinkGenerator linkGenerator)
{
var a = linkGenerator.GetPathByAction("Reque", "Order");
return true;
}

/// <summary>
///
/// </summary>
/// <para name="ss">必填</para>
/// <returns></returns>
[HttpGet("{name:required}")]
[Obsolete]
public bool Reque(string name)
{
return true;
}

/// <summary>
///
/// </summary>
/// <param name="number">以三个数字开始</param>
/// <returns></returns>
[HttpGet("{number:regex(^\\d{{3}}$)}")]
public bool Number(string number)
{
return true;
}
}
}

自定义路由约束

在项目根目录新增Constraint文件夹,在文件夹里新增MyRouteConstraint.cs,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;

namespace RoutingDemo.Constraint
{
public class MyRouteConstraint:IRouteConstraint
{
public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values,
RouteDirection routeDirection)
{
if (RouteDirection.IncomingRequest == routeDirection)
{
var v = values[routeKey];
if (long.TryParse(v.ToString(), out var value))
{
return true;
}
}

return false;
}
}
}

将自定义路由约束注册到框架中,在Startup.ConfigureServices方法里追加以下代码:

1
2
3
4
services.AddRouting(options =>
{
options.ConstraintMap.Add("isLong", typeof(MyRouteConstraint));
});

运行代码,访问/swagger,即可以看到路由配置的情况

总结

  • Restful不是必须的
  • 约定好API的表达契约
  • 将API约束在特定的目录下,如/api/
  • 使用ObsoleteAttribute标记即将废弃的API