mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
chore: resolve merge conflicts
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { OptionsUrlencoded } from 'body-parser';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
|
||||
describe('Body Parser (Express Application)', () => {
|
||||
const moduleFixture = Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
});
|
||||
let app: NestExpressApplication;
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
describe('application/json', () => {
|
||||
const stringLimit = '{ "msg": "Hello, World" }';
|
||||
const stringOverLimit = '{ "msg": "Hello, World!" }';
|
||||
|
||||
beforeEach(async () => {
|
||||
const testFixture = await moduleFixture.compile();
|
||||
|
||||
app = testFixture
|
||||
.createNestApplication<NestExpressApplication>({
|
||||
rawBody: true,
|
||||
logger: false,
|
||||
})
|
||||
.useBodyParser('json', { limit: Buffer.from(stringLimit).byteLength });
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('should allow request with matching body limit', async () => {
|
||||
const response = await request(app.getHttpServer())
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(stringLimit)
|
||||
.expect(201);
|
||||
|
||||
expect(response.body).to.eql({
|
||||
raw: stringLimit,
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail if post body is larger than limit', async () => {
|
||||
await request(app.getHttpServer())
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(stringOverLimit)
|
||||
.expect(413);
|
||||
});
|
||||
});
|
||||
|
||||
describe('application/x-www-form-urlencoded', () => {
|
||||
const stringLimit = 'msg=Hello, World';
|
||||
const stringOverLimit = 'msg=Hello, World!';
|
||||
|
||||
beforeEach(async () => {
|
||||
const testFixture = await moduleFixture.compile();
|
||||
|
||||
app = testFixture
|
||||
.createNestApplication<NestExpressApplication>({
|
||||
rawBody: true,
|
||||
logger: false,
|
||||
})
|
||||
.useBodyParser<OptionsUrlencoded>('urlencoded', {
|
||||
limit: Buffer.from(stringLimit).byteLength,
|
||||
extended: true,
|
||||
});
|
||||
|
||||
await app.init();
|
||||
});
|
||||
it('should allow request with matching body limit', async () => {
|
||||
const response = await request(app.getHttpServer())
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send(stringLimit)
|
||||
.expect(201);
|
||||
|
||||
expect(response.body).to.eql({
|
||||
raw: stringLimit,
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail if post body is larger than limit', async () => {
|
||||
await request(app.getHttpServer())
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send(stringOverLimit)
|
||||
.expect(413);
|
||||
});
|
||||
});
|
||||
});
|
||||
106
integration/nest-application/use-body-parser/e2e/fastify.spec.ts
Normal file
106
integration/nest-application/use-body-parser/e2e/fastify.spec.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import {
|
||||
FastifyAdapter,
|
||||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { AppModule } from '../src/app.module';
|
||||
|
||||
describe('Body Parser (Fastify Application)', () => {
|
||||
const moduleFixture = Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
});
|
||||
let app: NestFastifyApplication;
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
describe('application/json', () => {
|
||||
const stringLimit = '{ "msg": "Hello, World" }';
|
||||
const stringOverLimit = '{ "msg": "Hello, World!" }';
|
||||
|
||||
beforeEach(async () => {
|
||||
const testFixture = await moduleFixture.compile();
|
||||
|
||||
app = testFixture
|
||||
.createNestApplication<NestFastifyApplication>(new FastifyAdapter(), {
|
||||
rawBody: true,
|
||||
logger: false,
|
||||
})
|
||||
.useBodyParser('application/json', {
|
||||
bodyLimit: Buffer.from(stringLimit).byteLength,
|
||||
});
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('should allow request with matching body limit', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
payload: stringLimit,
|
||||
});
|
||||
|
||||
expect(JSON.parse(response.body)).to.eql({
|
||||
raw: stringLimit,
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail if post body is larger than limit', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
payload: stringOverLimit,
|
||||
});
|
||||
|
||||
expect(response.statusCode).to.equal(413);
|
||||
});
|
||||
});
|
||||
|
||||
describe('application/x-www-form-urlencoded', () => {
|
||||
const stringLimit = 'msg=Hello, World';
|
||||
const stringOverLimit = 'msg=Hello, World!';
|
||||
|
||||
beforeEach(async () => {
|
||||
const testFixture = await moduleFixture.compile();
|
||||
|
||||
app = testFixture
|
||||
.createNestApplication<NestFastifyApplication>(new FastifyAdapter(), {
|
||||
rawBody: true,
|
||||
logger: false,
|
||||
})
|
||||
.useBodyParser('application/x-www-form-urlencoded', {
|
||||
bodyLimit: Buffer.from(stringLimit).byteLength,
|
||||
});
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('should allow request with matching body limit', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/',
|
||||
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
||||
payload: stringLimit,
|
||||
});
|
||||
|
||||
expect(JSON.parse(response.body)).to.eql({
|
||||
raw: stringLimit,
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail if post body is larger than limit', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/',
|
||||
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
||||
payload: stringOverLimit,
|
||||
});
|
||||
|
||||
expect(response.statusCode).to.equal(413);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Controller, Post, Req, RawBodyRequest } from '@nestjs/common';
|
||||
import { IncomingMessage } from 'http';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
@Post()
|
||||
index(@Req() req: RawBodyRequest<IncomingMessage>) {
|
||||
return {
|
||||
raw: req.rawBody?.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [AppController],
|
||||
})
|
||||
export class AppModule {}
|
||||
23
integration/nest-application/use-body-parser/tsconfig.json
Normal file
23
integration/nest-application/use-body-parser/tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"lib": ["dom"],
|
||||
"noLib": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "es6",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"e2e/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -30,6 +30,7 @@ export interface HttpServer<TRequest = any, TResponse = any> {
|
||||
| RequestHandler<TRequest, TResponse>
|
||||
| ErrorHandler<TRequest, TResponse>,
|
||||
): any;
|
||||
useBodyParser?(...args: any[]): any;
|
||||
get(handler: RequestHandler<TRequest, TResponse>): any;
|
||||
get(path: string, handler: RequestHandler<TRequest, TResponse>): any;
|
||||
post(handler: RequestHandler<TRequest, TResponse>): any;
|
||||
|
||||
@@ -266,6 +266,20 @@ export class NestApplication
|
||||
return this;
|
||||
}
|
||||
|
||||
public useBodyParser(...args: [any, any?]): this {
|
||||
if (!('useBodyParser' in this.httpAdapter)) {
|
||||
this.logger.warn('Your HTTP Adapter does not support `.useBodyParser`.');
|
||||
return this;
|
||||
}
|
||||
|
||||
const [parserType, ...otherArgs] = args;
|
||||
const rawBody = !!this.appOptions.rawBody;
|
||||
|
||||
this.httpAdapter.useBodyParser(...[parserType, rawBody, ...otherArgs]);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public enableCors(options?: CorsOptions | CorsOptionsDelegate<any>): void {
|
||||
this.httpAdapter.enableCors(options);
|
||||
}
|
||||
|
||||
@@ -29,11 +29,13 @@ import {
|
||||
OptionsUrlencoded,
|
||||
urlencoded as bodyParserUrlencoded,
|
||||
} from 'body-parser';
|
||||
import * as bodyparser from 'body-parser';
|
||||
import * as cors from 'cors';
|
||||
import * as express from 'express';
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import { Duplex, pipeline } from 'stream';
|
||||
import { NestExpressBodyParserOptions } from '../interfaces/nest-express-body-parser-options.interface';
|
||||
import { ServeStaticOptions } from '../interfaces/serve-static-options.interface';
|
||||
import { getBodyParserOptions } from './utils/get-body-parser-options.util';
|
||||
|
||||
@@ -235,6 +237,19 @@ export class ExpressAdapter extends AbstractHttpAdapter {
|
||||
.forEach(parserKey => this.use(parserMiddleware[parserKey]));
|
||||
}
|
||||
|
||||
public useBodyParser<Options extends bodyparser.Options = bodyparser.Options>(
|
||||
type: keyof bodyparser.BodyParser,
|
||||
rawBody: boolean,
|
||||
options?: NestExpressBodyParserOptions<Options>,
|
||||
): this {
|
||||
const parserOptions = getBodyParserOptions(rawBody, options || {});
|
||||
const parser = bodyparser[type](parserOptions);
|
||||
|
||||
this.use(parser);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public setLocal(key: string, value: any) {
|
||||
this.instance.locals[key] = value;
|
||||
return this;
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './nest-express-application.interface';
|
||||
export * from './nest-express-body-parser-options.interface';
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Server } from 'net';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import * as bodyparser from 'body-parser';
|
||||
import { NestExpressBodyParserOptions } from './nest-express-body-parser-options.interface';
|
||||
import { ServeStaticOptions } from './serve-static-options.interface';
|
||||
|
||||
/**
|
||||
@@ -73,6 +75,24 @@ export interface NestExpressApplication extends INestApplication {
|
||||
*/
|
||||
useStaticAssets(path: string, options?: ServeStaticOptions): this;
|
||||
|
||||
/**
|
||||
* Register Express body parsers on the fly. Will respect
|
||||
* the application's `rawBody` option.
|
||||
*
|
||||
* @example
|
||||
* const app = await NestFactory.create<NestExpressApplication>(
|
||||
* AppModule,
|
||||
* { rawBody: true }
|
||||
* );
|
||||
* app.useBodyParser('json', { limit: '50mb' });
|
||||
*
|
||||
* @returns {this}
|
||||
*/
|
||||
useBodyParser<Options extends bodyparser.Options = bodyparser.Options>(
|
||||
parser: keyof bodyparser.BodyParser,
|
||||
options?: NestExpressBodyParserOptions<Options>,
|
||||
): this;
|
||||
|
||||
/**
|
||||
* Sets one or multiple base directories for templates (views).
|
||||
*
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { Options } from 'body-parser';
|
||||
|
||||
export type NestExpressBodyParserOptions<T extends Options = Options> = Omit<
|
||||
T,
|
||||
'verify'
|
||||
>;
|
||||
@@ -45,6 +45,7 @@ import {
|
||||
} from 'light-my-request';
|
||||
// `querystring` is used internally in fastify for registering urlencoded body parser.
|
||||
import { parse as querystringParse } from 'querystring';
|
||||
import { NestFastifyBodyParserOptions } from '../interfaces';
|
||||
import {
|
||||
FastifyStaticOptions,
|
||||
FastifyViewOptions,
|
||||
@@ -465,6 +466,43 @@ export class FastifyAdapter<
|
||||
this._isParserRegistered = true;
|
||||
}
|
||||
|
||||
public useBodyParser(
|
||||
type: string | string[] | RegExp,
|
||||
rawBody: boolean,
|
||||
options?: NestFastifyBodyParserOptions,
|
||||
parser?: FastifyBodyParser<Buffer, TServer>,
|
||||
) {
|
||||
const parserOptions = {
|
||||
...(options || {}),
|
||||
parseAs: 'buffer' as const,
|
||||
};
|
||||
|
||||
this.getInstance().addContentTypeParser<Buffer>(
|
||||
type,
|
||||
parserOptions,
|
||||
(
|
||||
req: RawBodyRequest<FastifyRequest<unknown, TServer, TRawRequest>>,
|
||||
body: Buffer,
|
||||
done,
|
||||
) => {
|
||||
if (rawBody === true && Buffer.isBuffer(body)) {
|
||||
req.rawBody = body;
|
||||
}
|
||||
|
||||
if (parser) {
|
||||
parser(req, body, done);
|
||||
return;
|
||||
}
|
||||
|
||||
done(null, body);
|
||||
},
|
||||
);
|
||||
|
||||
// To avoid the Nest application init to override our custom
|
||||
// body parser, we mark the parsers as registered.
|
||||
this._isParserRegistered = true;
|
||||
}
|
||||
|
||||
public async createMiddlewareFactory(
|
||||
requestMethod: RequestMethod,
|
||||
): Promise<(path: string, callback: Function) => any> {
|
||||
@@ -510,20 +548,15 @@ export class FastifyAdapter<
|
||||
}
|
||||
|
||||
private registerJsonContentParser(rawBody?: boolean) {
|
||||
const contentType = 'application/json';
|
||||
const withRawBody = !!rawBody;
|
||||
const { bodyLimit } = this.getInstance().initialConfig;
|
||||
|
||||
this.getInstance().addContentTypeParser<Buffer>(
|
||||
'application/json',
|
||||
{ parseAs: 'buffer', bodyLimit },
|
||||
(
|
||||
req: RawBodyRequest<FastifyRequest<unknown, TServer, TRawRequest>>,
|
||||
body: Buffer,
|
||||
done,
|
||||
) => {
|
||||
if (rawBody === true && Buffer.isBuffer(body)) {
|
||||
req.rawBody = body;
|
||||
}
|
||||
|
||||
this.useBodyParser(
|
||||
contentType,
|
||||
withRawBody,
|
||||
{ bodyLimit },
|
||||
(req, body, done) => {
|
||||
const { onProtoPoisoning, onConstructorPoisoning } =
|
||||
this.instance.initialConfig;
|
||||
const defaultJsonParser = this.instance.getDefaultJsonParser(
|
||||
@@ -536,20 +569,15 @@ export class FastifyAdapter<
|
||||
}
|
||||
|
||||
private registerUrlencodedContentParser(rawBody?: boolean) {
|
||||
const contentType = 'application/x-www-form-urlencoded';
|
||||
const withRawBody = !!rawBody;
|
||||
const { bodyLimit } = this.getInstance().initialConfig;
|
||||
|
||||
this.getInstance().addContentTypeParser<Buffer>(
|
||||
'application/x-www-form-urlencoded',
|
||||
{ parseAs: 'buffer', bodyLimit },
|
||||
(
|
||||
req: RawBodyRequest<FastifyRequest<unknown, TServer, TRawRequest>>,
|
||||
body: Buffer,
|
||||
done,
|
||||
) => {
|
||||
if (rawBody === true && Buffer.isBuffer(body)) {
|
||||
req.rawBody = body;
|
||||
}
|
||||
|
||||
this.useBodyParser(
|
||||
contentType,
|
||||
withRawBody,
|
||||
{ bodyLimit },
|
||||
(_req, body, done) => {
|
||||
done(null, querystringParse(body.toString()));
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './nest-fastify-application.interface';
|
||||
export * from './nest-fastify-body-parser-options.interface';
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import {
|
||||
FastifyBodyParser,
|
||||
FastifyInstance,
|
||||
FastifyPluginAsync,
|
||||
FastifyPluginCallback,
|
||||
FastifyPluginOptions,
|
||||
FastifyRegisterOptions,
|
||||
RawServerBase,
|
||||
} from 'fastify';
|
||||
import {
|
||||
Chain as LightMyRequestChain,
|
||||
@@ -12,6 +14,7 @@ import {
|
||||
Response as LightMyRequestResponse,
|
||||
} from 'light-my-request';
|
||||
import { FastifyStaticOptions, FastifyViewOptions } from './external';
|
||||
import { NestFastifyBodyParserOptions } from './nest-fastify-body-parser-options.interface';
|
||||
|
||||
export interface NestFastifyApplication extends INestApplication {
|
||||
/**
|
||||
@@ -28,6 +31,27 @@ export interface NestFastifyApplication extends INestApplication {
|
||||
opts?: FastifyRegisterOptions<Options>,
|
||||
): Promise<FastifyInstance>;
|
||||
|
||||
/**
|
||||
* Register Fastify body parsers on the fly. Will respect
|
||||
* the application's `rawBody` option.
|
||||
*
|
||||
* @example
|
||||
* const app = await NestFactory.create<NestFastifyApplication>(
|
||||
* AppModule,
|
||||
* new FastifyAdapter(),
|
||||
* { rawBody: true }
|
||||
* );
|
||||
* // enable the json parser with a parser limit of 50mb
|
||||
* app.useBodyParser('application/json', { bodyLimit: 50 * 1000 * 1024 });
|
||||
*
|
||||
* @returns {this}
|
||||
*/
|
||||
useBodyParser<TServer extends RawServerBase = RawServerBase>(
|
||||
type: string | string[] | RegExp,
|
||||
options?: NestFastifyBodyParserOptions,
|
||||
parser?: FastifyBodyParser<Buffer, TServer>,
|
||||
): this;
|
||||
|
||||
/**
|
||||
* Sets a base directory for public assets.
|
||||
* Example `app.useStaticAssets({ root: 'public' })`
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { AddContentTypeParser } from 'fastify';
|
||||
|
||||
export type NestFastifyBodyParserOptions = Omit<
|
||||
Parameters<AddContentTypeParser>[1],
|
||||
'parseAs'
|
||||
>;
|
||||
Reference in New Issue
Block a user