Skip to content

Commit

Permalink
feat: support Request decorators for HTTPController (#159)
Browse files Browse the repository at this point in the history
  • Loading branch information
412799755 authored Oct 26, 2023
1 parent 83fd550 commit 945e1eb
Show file tree
Hide file tree
Showing 19 changed files with 199 additions and 1 deletion.
3 changes: 2 additions & 1 deletion core/controller-decorator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"@eggjs/tegg-common-util": "^3.23.0",
"@eggjs/tegg-metadata": "^3.23.0",
"path-to-regexp": "^1.8.0",
"reflect-metadata": "^0.1.13"
"reflect-metadata": "^0.1.13",
"undici": "^5.26.5"
},
"devDependencies": {
"@types/mocha": "^10.0.1",
Expand Down
13 changes: 13 additions & 0 deletions core/controller-decorator/src/decorator/http/HTTPParam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,16 @@ export function HTTPParam(param?: HTTPParamParams) {
HTTPInfoUtil.setHTTPMethodParamName(name, parameterIndex, controllerClazz, methodName);
};
}

export function Request() {
return function(target: any, propertyKey: PropertyKey, parameterIndex: number) {
const [ nodeMajor ] = process.versions.node.split('.').map(v => Number(v));
assert(nodeMajor >= 16,
`[controller/${target.name}] expect node version >=16, but now is ${nodeMajor}`);
assert(typeof propertyKey === 'string',
`[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`);
const methodName = propertyKey as string;
const controllerClazz = target.constructor as EggProtoImplClass;
HTTPInfoUtil.setHTTPMethodParamType(HTTPParamType.REQUEST, parameterIndex, controllerClazz, methodName);
};
}
10 changes: 10 additions & 0 deletions core/controller-decorator/src/model/HTTPMethodMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ export abstract class ParamMeta {

abstract validate(httpPath: string);
}
export class RequestParamMeta extends ParamMeta {
type = HTTPParamType.REQUEST;

validate() {
return;
}
}

export class BodyParamMeta extends ParamMeta {
type = HTTPParamType.BODY;
Expand Down Expand Up @@ -118,6 +125,9 @@ export class ParamMetaUtil {
assert(name, 'query param must has name');
return new QueryParamMeta(name!);
}
case HTTPParamType.REQUEST: {
return new RequestParamMeta();
}
default:
assert.fail('never arrive');
}
Expand Down
4 changes: 4 additions & 0 deletions core/controller-decorator/src/model/HTTPRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import undici from 'undici';
// https://github.com/nodejs/undici/blob/main/index.js#L118
// 只有 nodejs >= 16 才支持 Request
export class HTTPRequest extends (undici.Request || Object) {}
4 changes: 4 additions & 0 deletions core/controller-decorator/src/model/HTTPResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import undici from 'undici';
// https://github.com/nodejs/undici/blob/main/index.js#L118
// 只有 nodejs >= 16 才支持 Request
export class HTTPResponse extends (undici.Response || Object) {}
2 changes: 2 additions & 0 deletions core/controller-decorator/src/model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export * from './MethodMeta';
export * from './ControllerMetadata';
export * from './HTTPMethodMeta';
export * from './HTTPControllerMeta';
export * from './HTTPRequest';
export * from './HTTPResponse';
1 change: 1 addition & 0 deletions core/controller-decorator/src/model/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ export enum HTTPParamType {
QUERIES = 'QUERIES',
BODY = 'BODY',
PARAM = 'PARAM',
REQUEST = 'REQUEST',
}
5 changes: 5 additions & 0 deletions plugin/controller/lib/impl/http/HTTPMethodRegister.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { EggPrototype } from '@eggjs/tegg-metadata';
import { RootProtoManager } from '../../RootProtoManager';
import pathToRegexp from 'path-to-regexp';
import { aclMiddlewareFactory } from './Acl';
import { HTTPRequest } from './Req';
import { RouterConflictError } from '../../errors';
import { FrameworkErrorFormater } from 'egg-errors';
import { EggRouter } from '@eggjs/router';
Expand Down Expand Up @@ -88,6 +89,10 @@ export class HTTPMethodRegister {
args[index] = ctx.queries[queryParam.name];
break;
}
case HTTPParamType.REQUEST: {
args[index] = new HTTPRequest(ctx);
break;
}
default:
assert.fail('never arrive');
}
Expand Down
13 changes: 13 additions & 0 deletions plugin/controller/lib/impl/http/Req.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Context } from 'egg';
import { HTTPRequest as BaseHTTPRequest } from '@eggjs/tegg';
export class HTTPRequest extends BaseHTTPRequest {
constructor(ctx:Context) {
const request = ctx.request;
// href: https://github.com/eggjs/koa/blob/master/src/request.ts#L90C1-L98C4
super(request.href, {
method: request.method,
headers: request.headers as Record<string, string | string[]>,
body: (ctx.request as any).rawBody,
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Context as EggContext } from 'egg';
import {
Context,
HTTPController,
HTTPMethod,
HTTPMethodEnum,
Middleware,
Request,
HTTPRequest,
} from '@eggjs/tegg';
import { countMw } from '../middleware/count_mw';

@HTTPController({
path: '/apps',
})
@Middleware(countMw)
export class AppController {

@HTTPMethod({
method: HTTPMethodEnum.POST,
path: '/testRequest',
})
async testRequest(@Context() ctx: EggContext, @Request() request: HTTPRequest) {
const traceId = await ctx.tracer.traceId;
return {
success: true,
traceId,
headers: Object.fromEntries(request.headers),
method: request.method,
requestBody: await request.text(),
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Context } from 'egg';
import { Next } from '@eggjs/tegg';

export async function callModuleCtx(ctx: Context, next: Next) {
await (ctx.module as any).multiModuleService.appService.findApp('foo');
await next();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Context } from 'egg';
import { Next } from '@eggjs/tegg';

let index = 0;

export async function countMw(ctx: Context, next: Next) {
await next();
if (ctx.body) ctx.body.count = index++;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Context } from 'egg';
import { Next } from '@eggjs/tegg';

export function logMwFactory(log: string) {
return async function logMw(ctx: Context, next: Next) {
await next();
ctx.body.log = log;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict';

module.exports = function() {
const config = {
keys: 'test key',
security: {
csrf: {
ignoreJSON: false,
}
},
};
return config;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use strict';

exports.tracer = {
package: 'egg-tracer',
enable: true,
};

exports.tegg = {
package: '@eggjs/tegg-plugin',
enable: true,
};

exports.teggConfig = {
package: '@eggjs/tegg-config',
enable: true,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "http-inject-app"
}
1 change: 1 addition & 0 deletions plugin/controller/test/http/params.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,5 @@ describe('test/params.test.ts', () => {
});
});
});

});
53 changes: 53 additions & 0 deletions plugin/controller/test/http/request.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import mm from 'egg-mock';
import path from 'path';
import assert from 'assert';

describe('test/params.test.ts', () => {
let app;

beforeEach(() => {
mm(process.env, 'EGG_TYPESCRIPT', true);
});

afterEach(() => {
mm.restore();
});

before(async () => {
mm(process.env, 'EGG_TYPESCRIPT', true);
mm(process, 'cwd', () => {
return path.join(__dirname, '../..');
});
app = mm.app({
baseDir: path.join(__dirname, '../fixtures/apps/http-inject-app'),
framework: require.resolve('egg'),
});
await app.ready();
});

after(() => {
return app.close();
});
const [ nodeMajor ] = process.versions.node.split('.').map(v => Number(v));
if (nodeMajor >= 16) {
it('Request should work', async () => {
app.mockCsrf();
const param = {
name: 'foo',
desc: 'mock-desc',
};
const headerKey = 'test-header';
await app.httpRequest()
.post('/apps/testRequest')
.send(param)
.set('test', headerKey)
.expect(200)
.expect(res => {
assert(res.body.headers.test === headerKey);
assert(res.body.method === 'POST');
assert(res.body.requestBody === JSON.stringify(param));
});
});
}

});

0 comments on commit 945e1eb

Please sign in to comment.