Skip to content

Commit 1dfc814

Browse files
Merge branch 'feature/change-http-code-interceptor' of https://github.com/ToonvanStrijp/nest into ToonvanStrijp-feature/change-http-code-interceptor
2 parents 48137df + 21c22d5 commit 1dfc814

13 files changed

Lines changed: 142 additions & 40 deletions

File tree

integration/hello-world/e2e/interceptors.spec.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,36 @@ export class TransformInterceptor {
2828
}
2929
}
3030

31+
@Injectable()
32+
export class StatusInterceptor {
33+
34+
constructor(private statusCode: number){}
35+
36+
intercept(context: ExecutionContext, next: CallHandler) {
37+
const ctx = context.switchToHttp();
38+
const res = ctx.getResponse();
39+
res.status(this.statusCode);
40+
return next.handle().pipe(map(data => ({ data })));
41+
}
42+
}
43+
44+
@Injectable()
45+
export class HeaderInterceptor {
46+
47+
constructor(private headers: object){}
48+
49+
intercept(context: ExecutionContext, next: CallHandler) {
50+
const ctx = context.switchToHttp();
51+
const res = ctx.getResponse();
52+
for (const key in this.headers) {
53+
if (this.headers.hasOwnProperty(key)) {
54+
res.header(key, this.headers[key]);
55+
}
56+
}
57+
return next.handle().pipe(map(data => ({ data })));
58+
}
59+
}
60+
3161
function createTestModule(interceptor) {
3262
return Test.createTestingModule({
3363
imports: [ApplicationModule],
@@ -87,6 +117,33 @@ describe('Interceptors', () => {
87117
.expect(200, { data: 'Hello world!' });
88118
});
89119

120+
it(`should modify response status`, async () => {
121+
app = (await createTestModule(
122+
new StatusInterceptor(400),
123+
)).createNestApplication();
124+
125+
await app.init();
126+
return request(app.getHttpServer())
127+
.get('/hello')
128+
.expect(400, { data: 'Hello world!' });
129+
});
130+
131+
it(`should modify Authorization header`, async () => {
132+
const customHeaders = {
133+
Authorization: 'jwt',
134+
};
135+
136+
app = (await createTestModule(
137+
new HeaderInterceptor(customHeaders),
138+
)).createNestApplication();
139+
140+
await app.init();
141+
return request(app.getHttpServer())
142+
.get('/hello')
143+
.expect(200)
144+
.expect('Authorization', 'jwt');
145+
});
146+
90147
afterEach(async () => {
91148
await app.close();
92149
});

integration/hello-world/src/hello/hello.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { HelloService } from './hello.service';
2-
import { Controller, Get, Header, Param } from '@nestjs/common';
2+
import { Controller, Get, Header, Param, Post } from '@nestjs/common';
33
import { Observable, of } from 'rxjs';
44
import { UserByIdPipe } from './users/user-by-id.pipe';
55

packages/common/interfaces/http/http-server.interface.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ export interface HttpServer<TRequest = any, TResponse = any> {
4242
options(path: string, handler: RequestHandler<TRequest, TResponse>): any;
4343
listen(port: number | string, callback?: () => void): any;
4444
listen(port: number | string, hostname: string, callback?: () => void): any;
45-
reply(response: any, body: any, statusCode: number): any;
45+
reply(response: any, body: any, statusCode?: number): any;
46+
status(response: any, statusCode: number): any;
4647
render(response: any, view: string, options: any): any;
4748
setHeader(response: any, name: string, value: string): any;
4849
setErrorHandler?(handler: Function): any;

packages/core/adapters/http-adapter.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ export abstract class AbstractHttpAdapter<
8282
abstract setViewEngine(engine: string);
8383
abstract getRequestMethod(request);
8484
abstract getRequestUrl(request);
85-
abstract reply(response, body: any, statusCode: number);
85+
abstract status(response, statusCode: number);
86+
abstract reply(response, body: any, statusCode?: number);
8687
abstract render(response, view: string, options: any);
8788
abstract setErrorHandler(handler: Function);
8889
abstract setNotFoundHandler(handler: Function);

packages/core/router/router-execution-context.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export class RouterExecutionContext {
8383
fnHandleResponse,
8484
paramtypes,
8585
getParamsMetadata,
86-
} = this.getMetadata(instance, callback, methodName, module, requestMethod);
86+
} = this.getMetadata(instance, callback, methodName, module);
8787
const paramsOptions = this.contextUtils.mergeParamsMetatypes(
8888
getParamsMetadata(module, contextId, inquirerId),
8989
paramtypes,
@@ -131,6 +131,20 @@ export class RouterExecutionContext {
131131
const args = this.contextUtils.createNullArray(argsLength);
132132
fnCanActivate && (await fnCanActivate([req, res]));
133133

134+
const httpCode = this.reflectHttpStatusCode(callback);
135+
136+
const httpStatusCode = httpCode
137+
? httpCode
138+
: this.responseController.getStatusByMethod(requestMethod);
139+
140+
this.responseController.status(res, httpStatusCode);
141+
142+
const responseHeaders = this.reflectResponseHeaders(callback);
143+
const hasCustomHeaders = !isEmpty(responseHeaders);
144+
145+
hasCustomHeaders &&
146+
this.responseController.setHeaders(res, responseHeaders);
147+
134148
const result = await this.interceptorsConsumer.intercept(
135149
interceptors,
136150
[req, res],
@@ -146,8 +160,7 @@ export class RouterExecutionContext {
146160
instance: Controller,
147161
callback: (...args: any[]) => any,
148162
methodName: string,
149-
module: string,
150-
requestMethod: RequestMethod,
163+
module: string
151164
): HandlerMetadata {
152165
const cacheMetadata = this.handlerMetadataStorage.get(instance, methodName);
153166
if (cacheMetadata) {
@@ -165,7 +178,6 @@ export class RouterExecutionContext {
165178
instance,
166179
methodName,
167180
);
168-
const httpCode = this.reflectHttpStatusCode(callback);
169181
const getParamsMetadata = (
170182
moduleKey: string,
171183
contextId = STATIC_CONTEXT,
@@ -184,14 +196,10 @@ export class RouterExecutionContext {
184196
({ type }) =>
185197
type === RouteParamtypes.RESPONSE || type === RouteParamtypes.NEXT,
186198
);
187-
const httpStatusCode = httpCode
188-
? httpCode
189-
: this.responseController.getStatusByMethod(requestMethod);
190199

191200
const fnHandleResponse = this.createHandleResponseFn(
192201
callback,
193-
isResponseHandled,
194-
httpStatusCode,
202+
isResponseHandled
195203
);
196204
const handlerMetadata: HandlerMetadata = {
197205
argsLength,
@@ -342,23 +350,16 @@ export class RouterExecutionContext {
342350
public createHandleResponseFn(
343351
callback: (...args: any[]) => any,
344352
isResponseHandled: boolean,
345-
httpStatusCode: number,
353+
httpStatusCode?: number
346354
) {
347355
const renderTemplate = this.reflectRenderTemplate(callback);
348-
const responseHeaders = this.reflectResponseHeaders(callback);
349-
const hasCustomHeaders = !isEmpty(responseHeaders);
350356

351357
if (renderTemplate) {
352358
return async <TResult, TResponse>(result: TResult, res: TResponse) => {
353-
hasCustomHeaders &&
354-
this.responseController.setHeaders(res, responseHeaders);
355359
await this.responseController.render(result, res, renderTemplate);
356360
};
357361
}
358362
return async <TResult, TResponse>(result: TResult, res: TResponse) => {
359-
hasCustomHeaders &&
360-
this.responseController.setHeaders(res, responseHeaders);
361-
362363
result = await this.responseController.transformToResult(result);
363364
!isResponseHandled &&
364365
(await this.responseController.apply(result, res, httpStatusCode));

packages/core/router/router-response-controller.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export class RouterResponseController {
1212
public async apply<TInput = any, TResponse = any>(
1313
result: TInput,
1414
response: TResponse,
15-
httpStatusCode: number,
15+
httpStatusCode?: number,
1616
) {
1717
return this.applicationRef.reply(response, result, httpStatusCode);
1818
}
@@ -50,4 +50,11 @@ export class RouterResponseController {
5050
this.applicationRef.setHeader(response, name, value),
5151
);
5252
}
53+
54+
public status<TResponse = any>(
55+
response: TResponse,
56+
statusCode: number
57+
) {
58+
this.applicationRef.status(response, statusCode);
59+
}
5360
}

packages/core/test/exceptions/exceptions-handler.spec.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ describe('ExceptionsHandler', () => {
3333
beforeEach(() => {
3434
sinon
3535
.stub(adapter, 'reply')
36-
.callsFake((responseRef: any, body: any, statusCode: number) => {
37-
const res = responseRef.status(statusCode);
36+
.callsFake((responseRef: any, body: any, statusCode?: number) => {
37+
if (statusCode) {
38+
responseRef.status(statusCode);
39+
}
3840
if (isNil(body)) {
39-
return res.send();
41+
return responseRef.send();
4042
}
41-
return isObject(body) ? res.json(body) : res.send(String(body));
43+
return isObject(body) ? responseRef.json(body) : responseRef.send(String(body));
4244
});
4345
});
4446
it('should method send expected response status code and message when exception is unknown', () => {

packages/core/test/router/router-execution-context.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ describe('RouterExecutionContext', () => {
281281
sinon.stub(contextCreator, 'reflectResponseHeaders').returns([]);
282282
sinon.stub(contextCreator, 'reflectRenderTemplate').returns(template);
283283

284-
const handler = contextCreator.createHandleResponseFn(null, true, 100);
284+
const handler = contextCreator.createHandleResponseFn(null, true, 200);
285285
await handler(value, response);
286286

287287
expect(response.render.calledWith(template, value)).to.be.true;
@@ -295,7 +295,7 @@ describe('RouterExecutionContext', () => {
295295
sinon.stub(contextCreator, 'reflectResponseHeaders').returns([]);
296296
sinon.stub(contextCreator, 'reflectRenderTemplate').returns(undefined);
297297

298-
const handler = contextCreator.createHandleResponseFn(null, true, 100);
298+
const handler = contextCreator.createHandleResponseFn(null, true, 200);
299299
handler(result, response);
300300

301301
expect(response.render.called).to.be.false;

packages/core/test/router/router-response-controller.spec.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,20 @@ describe('RouterResponseController', () => {
2222
json: sinon.SinonSpy;
2323
};
2424
beforeEach(() => {
25-
response = { send: sinon.spy(), json: sinon.spy() };
26-
response.status = sinon.stub().returns(response);
25+
response = { send: sinon.spy(), json: sinon.spy(), status: sinon.spy() };
2726
});
2827
describe('when result is', () => {
2928
beforeEach(() => {
3029
sinon
3130
.stub(adapter, 'reply')
32-
.callsFake((responseRef: any, body: any, statusCode: number) => {
33-
const res = responseRef.status(statusCode);
31+
.callsFake((responseRef: any, body: any, statusCode?: number) => {
32+
if (statusCode) {
33+
responseRef.status(statusCode);
34+
}
3435
if (isNil(body)) {
35-
return res.send();
36+
return responseRef.send();
3637
}
37-
return isObject(body) ? res.json(body) : res.send(String(body));
38+
return isObject(body) ? responseRef.json(body) : responseRef.send(String(body));
3839
});
3940
});
4041
describe('nil', () => {
@@ -149,4 +150,22 @@ describe('RouterResponseController', () => {
149150
).to.be.true;
150151
});
151152
});
153+
154+
describe('status', () => {
155+
let statusStub: sinon.SinonStub;
156+
157+
beforeEach(() => {
158+
statusStub = sinon.stub(adapter, 'status').callsFake(() => ({}));
159+
});
160+
161+
it('should set status', () => {
162+
const response = {};
163+
const statusCode = 400;
164+
165+
routerResponseController.status(response, statusCode);
166+
expect(
167+
statusStub.calledWith(response, statusCode),
168+
).to.be.true;
169+
});
170+
});
152171
});

packages/core/test/utils/noop-adapter.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export class NoopHttpAdapter extends AbstractHttpAdapter {
1111
setViewEngine(engine: string): any {}
1212
getRequestMethod(request: any): any {}
1313
getRequestUrl(request: any): any {}
14-
reply(response: any, body: any, statusCode: number): any {}
14+
reply(response: any, body: any): any {}
15+
status(response: any, statusCode: number): any {}
1516
render(response: any, view: string, options: any): any {}
1617
setErrorHandler(handler: Function): any {}
1718
setNotFoundHandler(handler: Function): any {}

0 commit comments

Comments
 (0)