Web 路由表
从 v2.8.0 开始,Midway 提供了内置的路由表能力,所有的 Web 框架都将使用这份路由表注册路由。
从 v3.4.0 开始,路由服务将作为 Midway 内置服务提供。
在应用启动,onReady 生命周期以及之后可用。
获取路由表服务
已默认实例化,可以直接注入使用。
import { Configuration, Inject, MidwayWebRouterService, MidwayServerlessFunctionService } from '@midwayjs/core';
@Configuration({
// ...
})
export class MainConfiguration {
@Inject()
webRouterService: MidwayWebRouterService;
@Inject()
serverlessFunctionService: MidwayServerlessFunctionService;
async onReady() {
// Web 路由
const routes = await this.webRouterService.getFlattenRouterTable();
// serverless 函数
const routes = await this.serverlessFunctionService.getFunctionList();
}
}
MidwayServerlessFunctionService
仅在 Serverless 场景下生效,方法和 MidwayWebRouterService
几乎相同。
路由信息定义
每个路由信息由一个 RouterInfo
定义表示,包含一些属性。
定义如下:
export interface RouterInfo {
/**
* router prefix
*/
prefix: string;
/**
* router alias name
*/
routerName: string;
/**
* router path, without prefix
*/
url: string | RegExp;
/**
* request method for http, like get/post/delete
*/
requestMethod: string;
/**
* invoke function method
*/
method: string;
description: string;
summary: string;
/**
* router handler function key,for IoC container load
*/
handlerName: string;
/**
* serverless func load key
*/
funcHandlerName: string;
/**
* controller provideId
*/
controllerId: string;
/**
* router middleware
*/
middleware: any[];
/**
* controller middleware in this router
*/
controllerMiddleware: any[];
/**
* request args metadata
*/
requestMetadata: any[];
/**
* response data metadata
*/
responseMetadata: any[];
}
属性 | 类型 | 描述 |
---|---|---|
prefix | string | 路由前缀,比如 / 或者 /api,用户写在 @Controller 装饰器上的部分 |
routerName | string | 路由名 |
url | string | 路由的去除路由前缀的部分,也是用户写在 @Get 等装饰器上的部分 |
requestMethod | string | get/post/delete/put/all 等 |
method | string | 实际调用的类上的方法名 |
description | string | 描述,路由装饰器上的参数 |
summary | string | 摘要,路由装饰器上的参数 |
handlerName | string | 等价于 controllerId.method |
funcHandlerName | string | 使用 @Func 写的 handler 名字 |
controllerId | string | controller 的依赖注入容器的 key(providerId) |
middleware | string[] | 路由中间件字符串数组 |
controllerMiddleware | string[] | 控制器中间件字符串数组 |
requestMetadata | any[] | 请求参数的元数据,@Query/@Body 等元数据 |
responseMetadata | any[] | 响应参数的元数据,@SetHeader/@ContentType 等元数据 |
路由优先级
以往我们需要关心路由的加载顺序,比如通配的 /*
比如在实际的 /abc
之后,否则会加载到错误的路由。在新版本中,我们对此种情况做了自动排序。
规则如下:
-
- 绝对路径规则优先级最高如
/ab/cb/e
- 绝对路径规则优先级最高如
-
- 星号只能出现最后且必须在/后面,如
/ab/cb/**
- 星号只能出现最后且必须在/后面,如
-
- 如果绝对路径和通配都能匹配一个路径时,绝对规则优先级高
-
- 有多个通配能匹配一个路径时,最长的规则匹配,如
/ab/**
和/ab/cd/**
在匹配/ab/cd/f
时命中/ab/cd/**
- 有多个通配能匹配一个路径时,最长的规则匹配,如
-
- 如果
/
与/*
都能匹配 / ,但/
的优先级高于/*
- 如果
此规则也与 Serverless 下函数的路由规则保持一致。
简单理解为,“明确的路由优先级最高,长的路由优先级高,通配的优先级最低”。
比如,排序完的优先级如下(高到低):
/api/invoke/abc
/api/invoke/*
/api/abc
/api/*
/abc
/*
当前匹配的路由
通过 getMatchedRouterInfo
方法,我们可以知道当前的路由,匹配到哪个路由信息(RouterInfo),从而进一步处理,这个逻辑在鉴权等场景很有用。
比如,在中间件中,我们可以在进入控制器之前提前判断。
import { Middleware, Inject, httpError, MidwayWebRouterService } from '@midwayjs/core';
@Middleware()
export class AuthMiddleware {
@Inject()
webRouterService: MidwayWebRouterService;
resolve() {
return async (ctx, next) => {
// 查询当前路由是否在路由表中注册
const routeInfo = await this.webRouterService.getMatchedRouterInfo(ctx.path, ctx.method);
if (routeInfo) {
await next();
} else {
throw new httpError.NotFoundError();
}
}
}
}
路由信息
获取扁平化路由列表
获取当前所有可注册到 HTTP 服务的路由列表(包括 @Func/@Controller,以及一切按照标准信息注册的自定义装饰器)。
会按照优先级从高到低自动排序。
定义:
async getFlattenRouterTable(): Promise<RouterInfo[]>
获取路由表 API。
const result = await this.webRouterService.getFlattenRouterTable();
输出示例:
[
{
"prefix": "/",
"routerName": "",
"url": "/set_header",
"requestMethod": "get",
"method": "homeSet",
"description": "",
"summary": "",
"handlerName": "apiController.homeSet",
"funcHandlerName": "apiController.homeSet",
"controllerId": "apiController",
"middleware": [],
"controllerMiddleware": [],
"requestMetadata": [],
"responseMetadata": [
{
"type": "web:response_header",
"setHeaders": {
"ccc": "ddd"
}
},
{
"type": "web:response_header",
"setHeaders": {
"bbb": "aaa"
}
}
],
},
{
"prefix": "/",
"routerName": "",
"url": "/ctx-body",
"requestMethod": "get",
"method": "getCtxBody",
"description": "",
"summary": "",
"handlerName": "apiController.getCtxBody",
"funcHandlerName": "apiController.getCtxBody",
"controllerId": "apiController",
"middleware": [],
"controllerMiddleware": [],
"requestMetadata": [],
"responseMetadata": [],
},
// ...
]
获取 Router 信息列表
在 Midway 中,每个 Controller 对应一个 Router 对象,每个 Router 都会有一个路由前缀(prefix),在此之中的所有路由都会按照上面的规则进行排序。
Router 本身也会按照 prefix 进行排序。
定义:
export interface RouterPriority {
prefix: string;
priority: number;
middleware: any[];
routerOptions: any;
controllerId: string;
}
async getRoutePriorityList(): Promise<RouterPriority[]>
Router 的数据相对简单。
属性 | 类型 | 描述 |
---|---|---|
prefix | string | 路由前缀,比如 / 或者 /api,用户写在 @Controller 装饰器上的部分 |
priority | number | Router 的优先级,@Priority 装饰器填写的值,/ 根 Router 默认优先级最低,为 -999 |
middleware | string[] | 控制器中间件字符串数组 |
controllerId | string | controller 的依赖注入容器的 key(providerId) |
routerOptions | any | @Controller 装饰器的 options |
获取路由表 API。
const list = await collector.getRoutePriorityList();
输出示例:
[
{
"prefix": "/case",
"priority": 0,
"middleware": [],
"routerOptions": {
"middleware": [],
"sensitive": true
},
"controllerId": "caseController"
},
{
"prefix": "/user",
"priority": 0,
"middleware": [],
"routerOptions": {
"middleware": [],
"sensitive": true
},
"controllerId": "userController"
},
{
"prefix": "/",
"priority": -999,
"middleware": [],
"routerOptions": {
"middleware": [],
"sensitive": true
},
"controllerId": "apiController"
}
]