路由和控制器

路由和控制器

midway 使用 koa-router 作为路由的承载者,同时在 ts 的语法上做了一些简化,我们将路由和控制器放在了一起,使用装饰器来标注路由。

路由装饰器

在新的 ts 体系中,我们的控制器目录为 app/controller ,我们在其中编写 *.ts 文件。例如下面的 userController.ts ,我们提供了一个获取用户的接口。

@provide()
@controller('/user')
export class UserController {

  @inject('userService')
  service: IUserService;

  @get('/:id')
  async getUser(ctx): Promise<void> {
    const id: number = ctx.params.id;
    const user: IUserResult = await this.service.getUser({id});
    ctx.body = {success: true, message: 'OK', data: user};
  }
}

我们创建了 [@controller](https://github.com/controller) "@controller") 装饰器用来定义这个类为 Controller,同时,提供了方法装饰器用于标注请求的类型。

小贴士

便于大家理解,[@controller](https://github.com/controller) "@controller") 的参数为字符串 pattern,我们会将这个值传入 router.prefix(prefix) 的参数中。

midway 针对 web 请求,提供了和 koa-router 对应的方法装饰器,列表如下。

  • @get “@get”)
  • @post “@post”)
  • @del “@del”)
  • @put “@put”)
  • @patch “@patch”)
  • @options “@options”)
  • @head “@head”)
  • @all “@all”) 这几个装饰器用于修饰不同的异步方法,同时对应到了 koa-router 的相应的方法。和原有提供的控制器一样,每个控制器都为异步方法,参数为 koa 上下文。
@get('/:id')
async getUser(ctx, next): Promise<void> {
    // TODO ctx...
}

路由绑定

在以往框架的写法中,提供的 app/router 文件,虽然可以直接使用,但是由于控制器被 IoC 管控的关系,会有一些区别。

和以往的写法不同的是,我们需要从容器中拿到对应的控制器实例,并绑定到路由的 pattern 上 。

假如我们有一个控制器,同时没有提供 [@controller](https://github.com/controller) "@controller") 装饰器,表明他不是一个控制器,在应用初始化时不会自动绑定到某个路由上,但是由于有 [@provide](https://github.com/provide) "@provide") 装饰器,他会被 IoC 容器所加载。

// app/controller/api.ts

@provide()
export class BaseApi {
  async index(ctx) {
    ctx.body = 'index';
  }
}

假如我们希望这个控制器可以被外部的路由使用。

// app/router.ts

module.exports = function(app) {
  app.get('/api/index', app.generateController('baseApi.index'));
};

midway 扩展了一个 app.generateController 的方法来简化绑定的这个步骤,参数为 ClassName.methodName 的字符串形式。

路由优先级

在单页应用下,经常会出现 /* 这种路由,在原本的路由文件中,我们可以通过调整代码的顺序,来使的路由的匹配顺序发生变化。而由于使用了装饰器的关系,在新题的体系无法控制文件扫描和加载顺序,这就使得路由匹配的顺序不可控。

midway 提供了 [@priority](https://github.com/priority) "@priority")(priority: number) 装饰器,用于修饰 class,定义路由的优先级,默认的路由优先级为 0,可以设置负数让优先级降低。

@provide()
@priority(-1)
@controller('/')
export class HomeController {

  @get('/hello')
  async index(ctx) {
    ctx.body = 'hello';
  }

  @get('/*')
  async all(ctx) {
    ctx.body = 'world';
  }
}