Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

希望 extends继承时可以继承装饰器 #2364

Open
yuntian001 opened this issue Oct 9, 2022 · 8 comments
Open

希望 extends继承时可以继承装饰器 #2364

yuntian001 opened this issue Oct 9, 2022 · 8 comments

Comments

@yuntian001
Copy link
Contributor

比如

import { Controller } from '@midwayjs/decorator';
@Controller('/api')
export class APIController {
 
}
import {  Get, Query } from '@midwayjs/decorator';
import { APIController } from './api.controller';
export class UserController extends APIController {
  @Get('/user')
  async getUser(@Query('uid') uid) {
    return { success: true, message: 'OK', data: {aa:1} };
  }
}

这时候路由/api/user并不存在。
经测试在nestjs中这样写是可以访问/api/user的。

@waitingsong
Copy link
Member

基类上不建议添加装饰器

@yuntian001
Copy link
Contributor Author

yuntian001 commented Oct 9, 2022

基类上不建议添加装饰器

我主要想实现分模块,
比如v1、v2两个版本的api,只要继承不同的基类,在基类上写上前缀和定义好模块的公共中间件, 就可以作用到模块下的所有控制器。

V1模块

import { Controller } from '@midwayjs/decorator';
import { ReportMiddlewareV1 } from '../middleware/report.v1.middlweare';
@Controller('/v1', { middleware: [ ReportMiddlewareV1 ] })
export class APIController {
 }
import {  Get, Query } from '@midwayjs/decorator';
import { APIController } from './api.controller';
import { ReportMiddlewareV1User } from '../middleware/report.v1.uer.middlweare';
@Controller('/user',{middleware: [ ReportMiddlewareV1User ]});
export class UserController extends APIController {
  @Get('/user')
  async getUser(@Query('uid') uid) {
    return { success: true, message: 'OK', data: {aa:1} };
  }
}

期望的访问地址为:/v1/user,期望应用的中间为["ReportMiddlewareV1","ReportMiddlewareV1User"]

V2模块

import { Controller } from '@midwayjs/decorator';
import { ReportMiddlewareV2 } from '../middleware/report.v2.middlweare';
@Controller('/v2', { middleware: [ ReportMiddlewareV2 ] })
export class APIController {
 }
import {  Get, Query } from '@midwayjs/decorator';
import { APIController } from './api.controller';
import { ReportMiddlewareV2User } from '../middleware/report.v2.user.middlweare';

@Controller('/user',{middleware: [ ReportMiddlewareV2User ]});
export class UserController extends APIController {
  @Get('/user')
  async getUser(@Query('uid') uid) {
    return { success: true, message: 'OK', data: {aa:1} };
  }
}

期望的访问地址为:/v2/user,期望应用的中间为["ReportMiddlewareV2","ReportMiddlewareV2User"]

思路

我看官方的 packages\core\src\service\webRouterService.ts下的路由注册部分

  protected analyzeController() {
    const controllerModules = listModule(CONTROLLER_KEY);

    for (const module of controllerModules) {
      const controllerOption: ControllerOption = getClassMetadata(
        CONTROLLER_KEY,
        module
      );
      this.addController(
        module,
        controllerOption,
        this.options.includeFunctionRouter
      );
    }
  }

改为获取到controllerOption后递归拿下原型的controllerOption,然后加个@controller入参控制下是否合并祖先的属性,进行controllerOption合并后再注册,应该可以实现。

不知道官方有没有更好的实现方式和建议

@waitingsong
Copy link
Member

可以试试把不同版本的中间件合并为一个入口,通过 ctx.path 来调用不同版本(中间件)逻辑

@yuntian001
Copy link
Contributor Author

可以试试把不同版本的中间件合并为一个入口,通过 ctx.path 来调用不同版本(中间件)逻辑

那样只解决了中间件的问题,前缀问题依然没有解决,而且每次请求都会进行判断,不像注册时只会执行一次。我还在自己封个装饰器,动态注册路由吧。

@czy88840616
Copy link
Member

import {
  Provide,
  Get,
  Configuration,
  App,
  Inject,
  Middleware,
  Init,
} from '@midwayjs/decorator';
import * as koa from '@midwayjs/koa';
import { join } from 'path';
import { MidwayWebRouterService } from '@midwayjs/core';

@Provide()
export class HomeController {
  @Inject()
  ctx;

  @Get('/')
  async home(): Promise<string> {
    return 'Hello Midwayjs!' + this.ctx.user;
  }
}

@Middleware()
export class CustomMiddleware {
  resolve() {
    return async (ctx, next) => {
      ctx.user = 'v1';
      await next();
    };
  }
}

@Middleware()
export class CustomMiddleware2 {
  resolve() {
    return async (ctx, next) => {
      ctx.user = 'v2';
      await next();
    };
  }
}

@Configuration({
  imports: [koa],
  importConfigs: [join(__dirname, './config')],
})
export class ContainerLifeCycle {
  @App()
  app: koa.Application;

  @Inject()
  webRouterService: MidwayWebRouterService;

  @Init()
  async init() {
    this.webRouterService.addController(HomeController, {
      prefix: '/v1',
      routerOptions: {
        middleware: [CustomMiddleware],
      },
    });
    this.webRouterService.addController(HomeController, {
      prefix: '/v2',
      routerOptions: {
        middleware: [CustomMiddleware2],
      },
    });
  }
}

@yuntian001
Copy link
Contributor Author

yuntian001 commented Oct 9, 2022

//packages\core\src\service\webRouterService.ts 
protected mergeControllerOption(controllerOption: ControllerOption, controllerClz: Object) {
    if(controllerOption.mergeAncestor){
      const prototype = Object.getPrototypeOf(controllerClz);
      const parentOption: ControllerOption = getClassMetadata(
        CONTROLLER_KEY,
        prototype
      )
      if(parentOption){
        controllerOption.prefix = (controllerOption.prefix+parentOption.prefix).replace(/\/\//g,'/');
        controllerOption.routerOptions = merge(controllerOption.routerOptions,parentOption.routerOptions);
        return this.mergeControllerOption(controllerOption,prototype)
      }
    }
    return controllerOption;
  }

  protected analyzeController() {
    const controllerModules = listModule(CONTROLLER_KEY);

    for (const module of controllerModules) {
      const controllerOption: ControllerOption = this.mergeControllerOption(getClassMetadata(
        CONTROLLER_KEY,
        module
      ),module);
      this.addController(
        module,
        controllerOption,
        this.options.includeFunctionRouter
      );
    }
  }

@czy88840616 这种方式是否合适,如果感觉合适我可以完善下提个PR

@czy88840616
Copy link
Member

暂时不建议动原型链,原型链会有不少悖论和延展的问题,比如重名的装饰器是组合还是继承,配置如何处理,swagger 扩展等等。

@yuntian001
Copy link
Contributor Author

暂时不建议动原型链,原型链会有不少悖论和延展的问题,比如重名的装饰器是组合还是继承,配置如何处理,swagger 扩展等等。

好的,希望官方能规划出更方便的分模化方案。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants