Skip to content

Commit 4678c45

Browse files
authored
feat: add HTTPHeaders decorator (#208)
```ts async foo(@HTTPHeaders headers: IncomingHttpHeaders) { console.log(headers); } ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced handling of HTTP headers in the application, enabling methods to access and utilize header information directly. - **Bug Fixes** - Enhanced parameter handling in HTTP methods to support headers, improving the robustness and functionality of the application. - **Tests** - Added new test cases to verify the correct handling of HTTP headers parameters. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent ff491a7 commit 4678c45

File tree

10 files changed

+65
-15
lines changed

10 files changed

+65
-15
lines changed

core/controller-decorator/src/decorator/http/HTTPParam.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import assert from 'assert';
2-
import { HTTPParamType } from '../../model';
3-
import HTTPInfoUtil from '../../util/HTTPInfoUtil';
1+
import assert from 'node:assert';
42
import { EggProtoImplClass } from '@eggjs/core-decorator';
53
import { ObjectUtils } from '@eggjs/tegg-common-util';
4+
import { HTTPParamType } from '../../model';
5+
import HTTPInfoUtil from '../../util/HTTPInfoUtil';
66

77
// TODO url params
88
// /foo/:id
@@ -18,6 +18,16 @@ export function HTTPBody() {
1818
};
1919
}
2020

21+
export function HTTPHeaders() {
22+
return function(target: any, propertyKey: PropertyKey, parameterIndex: number) {
23+
assert(typeof propertyKey === 'string',
24+
`[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`);
25+
const methodName = propertyKey as string;
26+
const controllerClazz = target.constructor as EggProtoImplClass;
27+
HTTPInfoUtil.setHTTPMethodParamType(HTTPParamType.HEADERS, parameterIndex, controllerClazz, methodName);
28+
};
29+
}
30+
2131
export interface HTTPQueryParams {
2232
name?: string;
2333
}

core/controller-decorator/src/model/HTTPMethodMeta.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ export class BodyParamMeta extends ParamMeta {
2424
}
2525
}
2626

27+
export class HeadersParamMeta extends ParamMeta {
28+
type = HTTPParamType.HEADERS;
29+
30+
validate() {
31+
return;
32+
}
33+
}
34+
2735
export class QueryParamMeta extends ParamMeta {
2836
type = HTTPParamType.QUERY;
2937
name: string;
@@ -117,6 +125,9 @@ export class ParamMetaUtil {
117125
case HTTPParamType.BODY: {
118126
return new BodyParamMeta();
119127
}
128+
case HTTPParamType.HEADERS: {
129+
return new HeadersParamMeta();
130+
}
120131
case HTTPParamType.QUERIES: {
121132
assert(name, 'queries param must has name');
122133
return new QueriesParamMeta(name!);

core/controller-decorator/src/model/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { Context } from 'egg';
1+
import type { Context } from 'egg';
22

33
export type EggContext = Context;
44
export type Next = () => Promise<void>;
55
export type MiddlewareFunc = (ctx: Context, next: Next) => Promise<void>;
6+
export type { IncomingHttpHeaders } from 'node:http';
67

78
export enum ControllerType {
89
HTTP = 'HTTP',
@@ -42,4 +43,5 @@ export enum HTTPParamType {
4243
BODY = 'BODY',
4344
PARAM = 'PARAM',
4445
REQUEST = 'REQUEST',
46+
HEADERS = 'HEADERS',
4547
}

core/controller-decorator/src/util/HTTPInfoUtil.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { EggProtoImplClass, MetadataUtil } from '@eggjs/core-decorator';
2-
import { HTTPMethodEnum, HTTPParamType } from '../model';
32
import { MapUtil } from '@eggjs/tegg-common-util';
3+
import { HTTPMethodEnum, HTTPParamType } from '../model';
44

55
const CONTROLLER_HTTP_PATH = Symbol.for('EggPrototype#controller#http#path');
66
const CONTROLLER_METHOD_METHOD_MAP = Symbol.for('EggPrototype#controller#method#http#method');

core/controller-decorator/test/decorators.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import assert from 'assert';
1+
import assert from 'node:assert';
22
import { PrototypeUtil } from '@eggjs/core-decorator';
3-
43
import { FooController } from './fixtures/HTTPFooController';
54

65
describe('test/decorators.test.ts', () => {

core/controller-decorator/test/fixtures/HTTPFooController.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { HTTPController } from '../../src/decorator/http/HTTPController';
22
import { Context } from '../../src/decorator/Context';
33
import { Middleware } from '../../src/decorator/Middleware';
4-
import { EggContext, HTTPMethodEnum, Next } from '../../src/model';
5-
import { HTTPBody, HTTPParam, HTTPQueries, HTTPQuery } from '../../src/decorator/http/HTTPParam';
4+
import { EggContext, HTTPMethodEnum, Next, IncomingHttpHeaders } from '../../src/model';
5+
import {
6+
HTTPBody, HTTPParam, HTTPQueries, HTTPQuery, HTTPHeaders,
7+
} from '../../src/decorator/http/HTTPParam';
68
import { HTTPMethod } from '../../src/decorator/http/HTTPMethod';
79

810
async function middleware1(ctx: EggContext, next: Next) {
@@ -45,8 +47,9 @@ export class ControllerWithParam {
4547
path: '/bar/:id',
4648
method: HTTPMethodEnum.GET,
4749
})
48-
async bar(@Context() ctx: EggContext, @HTTPParam() id: string, @HTTPParam() fooId: string) {
49-
console.log(ctx, id, fooId);
50+
async bar(@Context() ctx: EggContext, @HTTPParam() id: string, @HTTPParam() fooId: string,
51+
@HTTPHeaders() headers: IncomingHttpHeaders) {
52+
console.log(ctx, id, fooId, headers);
5053
}
5154
}
5255

core/controller-decorator/test/http/HTTPMeta.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ import {
1212
BodyParamMeta,
1313
ControllerMetaBuilderFactory,
1414
ParamMeta,
15+
HeadersParamMeta,
1516
PathParamMeta,
1617
QueriesParamMeta,
1718
QueryParamMeta,
1819
} from '../..';
1920
import { ControllerType, HTTPControllerMeta, HTTPMethodEnum } from '../../src/model';
2021
import { PriorityController, TooLongController } from '../fixtures/HTTPPriorityController';
2122

22-
describe('test/http/HTTPMeta.test.ts', () => {
23+
describe('core/controller-decorator/test/http/HTTPMeta.test.ts', () => {
2324
it('should work', () => {
2425
const builder = ControllerMetaBuilderFactory.createControllerMetaBuilder(FooController, ControllerType.HTTP)!;
2526
const fooControllerMetaData = builder.build()! as HTTPControllerMeta;
@@ -82,6 +83,7 @@ describe('test/http/HTTPMeta.test.ts', () => {
8283
const controllerMeta = builder.build()! as HTTPControllerMeta;
8384
const methodMeta = controllerMeta.methods[0];
8485
const expectParamTypeMap = new Map<number, ParamMeta>([
86+
[ 3, new HeadersParamMeta() ],
8587
[ 2, new PathParamMeta('fooId') ],
8688
[ 1, new PathParamMeta('id') ],
8789
]);

plugin/controller/lib/impl/http/HTTPMethodRegister.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ export class HTTPMethodRegister {
8989
args[index] = ctx.queries[queryParam.name];
9090
break;
9191
}
92+
case HTTPParamType.HEADERS: {
93+
args[index] = ctx.request.headers;
94+
break;
95+
}
9296
case HTTPParamType.REQUEST: {
9397
args[index] = new HTTPRequest(ctx);
9498
break;

plugin/controller/test/fixtures/apps/controller-app/app/controller/AppController.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
HTTPMethodEnum,
88
HTTPParam,
99
HTTPQuery,
10+
HTTPHeaders, IncomingHttpHeaders,
1011
Middleware,
1112
Inject,
1213
} from '@eggjs/tegg';
@@ -52,12 +53,13 @@ export class AppController {
5253
method: HTTPMethodEnum.POST,
5354
path: '',
5455
})
55-
async save(@Context() ctx: EggContext, @HTTPBody() app: App) {
56+
async save(@Context() ctx: EggContext, @HTTPBody() app: App, @HTTPHeaders() headers: IncomingHttpHeaders) {
5657
const traceId = await ctx.tracer.traceId;
5758
await this.appService.save(app);
5859
return {
5960
success: true,
6061
traceId,
62+
sessionId: headers['x-session-id'],
6163
};
6264
}
6365
}

plugin/controller/test/http/params.test.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import path from 'node:path';
2+
import { strict as assert } from 'node:assert';
13
import mm from 'egg-mock';
2-
import path from 'path';
3-
import assert from 'assert';
44

55
describe('plugin/controller/test/http/params.test.ts', () => {
66
let app;
@@ -44,6 +44,23 @@ describe('plugin/controller/test/http/params.test.ts', () => {
4444
});
4545
});
4646

47+
it('headers param should work', async () => {
48+
app.mockCsrf();
49+
await app.httpRequest()
50+
.post('/apps')
51+
.set('x-session-id', 'mock-session-id')
52+
.send({
53+
name: 'foo',
54+
desc: 'mock-desc',
55+
})
56+
.expect(200)
57+
.expect(res => {
58+
assert.equal(res.body.success, true);
59+
assert(res.body.traceId);
60+
assert.equal(res.body.sessionId, 'mock-session-id');
61+
});
62+
});
63+
4764
it('query param should work', async () => {
4865
app.mockCsrf();
4966
await app.httpRequest()

0 commit comments

Comments
 (0)