Skip to content

Commit b83357e

Browse files
Merge branch 'ToonvanStrijp-feature/change-http-code-interceptor'
2 parents 48137df + 0712346 commit b83357e

14 files changed

Lines changed: 143 additions & 36 deletions

File tree

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

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

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

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

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

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

66
@Controller('hello')

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/helpers/handler-metadata-storage.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ export const HANDLER_METADATA_SYMBOL = Symbol.for('handler_metadata:cache');
77
export interface HandlerMetadata {
88
argsLength: number;
99
paramtypes: any[];
10+
httpStatusCode: number;
11+
responseHeaders: any[];
12+
hasCustomHeaders: boolean;
1013
getParamsMetadata: (
1114
moduleKey: string,
1215
contextId?: ContextId,

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

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,11 @@ export class RouterExecutionContext {
8383
fnHandleResponse,
8484
paramtypes,
8585
getParamsMetadata,
86+
httpStatusCode,
87+
responseHeaders,
88+
hasCustomHeaders,
8689
} = this.getMetadata(instance, callback, methodName, module, requestMethod);
90+
8791
const paramsOptions = this.contextUtils.mergeParamsMetatypes(
8892
getParamsMetadata(module, contextId, inquirerId),
8993
paramtypes,
@@ -131,6 +135,10 @@ export class RouterExecutionContext {
131135
const args = this.contextUtils.createNullArray(argsLength);
132136
fnCanActivate && (await fnCanActivate([req, res]));
133137

138+
this.responseController.setStatus(res, httpStatusCode);
139+
hasCustomHeaders &&
140+
this.responseController.setHeaders(res, responseHeaders);
141+
134142
const result = await this.interceptorsConsumer.intercept(
135143
interceptors,
136144
[req, res],
@@ -165,7 +173,6 @@ export class RouterExecutionContext {
165173
instance,
166174
methodName,
167175
);
168-
const httpCode = this.reflectHttpStatusCode(callback);
169176
const getParamsMetadata = (
170177
moduleKey: string,
171178
contextId = STATIC_CONTEXT,
@@ -184,20 +191,28 @@ export class RouterExecutionContext {
184191
({ type }) =>
185192
type === RouteParamtypes.RESPONSE || type === RouteParamtypes.NEXT,
186193
);
187-
const httpStatusCode = httpCode
188-
? httpCode
189-
: this.responseController.getStatusByMethod(requestMethod);
190194

191195
const fnHandleResponse = this.createHandleResponseFn(
192196
callback,
193197
isResponseHandled,
194-
httpStatusCode,
195198
);
199+
200+
const httpCode = this.reflectHttpStatusCode(callback);
201+
const httpStatusCode = httpCode
202+
? httpCode
203+
: this.responseController.getStatusByMethod(requestMethod);
204+
205+
const responseHeaders = this.reflectResponseHeaders(callback);
206+
const hasCustomHeaders = !isEmpty(responseHeaders);
207+
196208
const handlerMetadata: HandlerMetadata = {
197209
argsLength,
198210
fnHandleResponse,
199211
paramtypes,
200212
getParamsMetadata,
213+
httpStatusCode,
214+
hasCustomHeaders,
215+
responseHeaders,
201216
};
202217
this.handlerMetadataStorage.set(instance, methodName, handlerMetadata);
203218
return handlerMetadata;
@@ -342,23 +357,16 @@ export class RouterExecutionContext {
342357
public createHandleResponseFn(
343358
callback: (...args: any[]) => any,
344359
isResponseHandled: boolean,
345-
httpStatusCode: number,
360+
httpStatusCode?: number,
346361
) {
347362
const renderTemplate = this.reflectRenderTemplate(callback);
348-
const responseHeaders = this.reflectResponseHeaders(callback);
349-
const hasCustomHeaders = !isEmpty(responseHeaders);
350363

351364
if (renderTemplate) {
352365
return async <TResult, TResponse>(result: TResult, res: TResponse) => {
353-
hasCustomHeaders &&
354-
this.responseController.setHeaders(res, responseHeaders);
355366
await this.responseController.render(result, res, renderTemplate);
356367
};
357368
}
358369
return async <TResult, TResponse>(result: TResult, res: TResponse) => {
359-
hasCustomHeaders &&
360-
this.responseController.setHeaders(res, responseHeaders);
361-
362370
result = await this.responseController.transformToResult(result);
363371
!isResponseHandled &&
364372
(await this.responseController.apply(result, res, httpStatusCode));

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

Lines changed: 5 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,8 @@ export class RouterResponseController {
5050
this.applicationRef.setHeader(response, name, value),
5151
);
5252
}
53+
54+
public setStatus<TResponse = any>(response: TResponse, statusCode: number) {
55+
this.applicationRef.status(response, statusCode);
56+
}
5357
}

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,22 @@ 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)
39+
? responseRef.json(body)
40+
: responseRef.send(String(body));
3841
});
3942
});
4043
describe('nil', () => {
@@ -149,4 +152,20 @@ describe('RouterResponseController', () => {
149152
).to.be.true;
150153
});
151154
});
155+
156+
describe('status', () => {
157+
let statusStub: sinon.SinonStub;
158+
159+
beforeEach(() => {
160+
statusStub = sinon.stub(adapter, 'status').callsFake(() => ({}));
161+
});
162+
163+
it('should set status', () => {
164+
const response = {};
165+
const statusCode = 400;
166+
167+
routerResponseController.setStatus(response, statusCode);
168+
expect(statusStub.calledWith(response, statusCode)).to.be.true;
169+
});
170+
});
152171
});

0 commit comments

Comments
 (0)