@ditsmod/body-parser
У цьому модулі зроблена інтеграція з @ts-stack/body-parser (що є форком широковідомого пакета ExpressJS) та @ts-stack/multer (що є також форком широковідомого пакета ExpressJS). По-дефолту, підтримуються наступні формати даних:
application/json
application/x-www-form-urlencoded
text/plain
application/octet-stream
multipart/form-data
За перші чотири формати із цього списку відповідає пакет @ts-stack/body-parser
, за останій - @ts-stack/multer
, який використовується для завантаження файлів. І оскільки налаштування для завантаження файлів може сильно відрізнятись від роута до роута, відповідно - для завантаження файлів Ditsmod надає сервіс, що спрощує роботу з файлами, замість гото вих результатів.
Для парсингу перших чотирьох форматів, цей модуль додає інтерсептор до усіх роутів, що мають HTTP-методи вказані у bodyParserConfig.acceptMethods
, по-дефолту це:
POST
PUT
PATCH
Готовий приклад використання @ditsmod/body-parser
можете проглянути в репозиторії Ditsmod.
Встановлення
npm i @ditsmod/body-parser
Підключення
Щоб глобально підключити @ditsmod/body-parser
, потрібно імпортувати та експортувати BodyParserModule
в кореневому модулі:
import { rootModule } from '@ditsmod/core';
import { BodyParserModule } from '@ditsmod/body-parser';
@rootModule({
imports: [
BodyParserModule,
// ...
],
exports: [BodyParserModule]
})
export class AppModule {}
В такому разі будуть працювати дефолтні налаштування. Якщо ж вам потрібно змінити деякі опції, можете це зробити наступним чином:
import { rootModule } from '@ditsmod/core';
import { BodyParserModule } from '@ditsmod/body-parser';
const moduleWithBodyParserConfig = BodyParserModule.withParams({
acceptMethods: ['POST'],
jsonOptions: { limit: '500kb', strict: false },
urlencodedOptions: { extended: true },
});
@rootModule({
imports: [
moduleWithBodyParserConfig,
// ...
],
exports: [moduleWithBodyParserConfig],
})
export class AppModule {}
Ще один варіант передачі конфігурації:
import { rootModule, Providers } from '@ditsmod/core';
import { BodyParserModule, BodyParserConfig } from '@ditsmod/body-parser';
@rootModule({
imports: [
BodyParserModule,
// ...
],
providersPerApp: new Providers()
.useValue<BodyParserConfig>(BodyParserConfig, { acceptMethods: ['POST'] }),
exports: [BodyParserModule]
})
export class AppModule {}
Отримання тіла запиту
В залежності від того, чи є контролер одинаком чи ні, результат роботи інтерсептора можна отримати двома способами:
- Якщо контролер не є одинаком, результат можна отримати за допомогою токена
HTTP_BODY
:
import { controller, Res, route, inject } from '@ditsmod/core';
import { HTTP_BODY } from '@ditsmod/body-parser';
interface Body {
one: number;
}
@controller()
export class SomeController {
@route('POST')
ok(@inject(HTTP_BODY) body: Body, res: Res) {
res.sendJson(body);
}
}
- Якщо контролер є одинаком, результат можна отримати з контексту:
import { controller, route, SingletonRequestContext } from '@ditsmod/core';
@controller({ isSingleton: true })
export class SomeController {
@route('POST')
ok(ctx: SingletonRequestContext) {
ctx.sendJson(ctx.body);
}
}
Завантаження файлів
В залежності від того, чи є контролер одинаком чи ні, спосіб отримання парсера, та сигнатури його методів трохи відрізняються:
- Якщо контролер не є одинаком, через DI необхідно запитати
MulterParser
, після чого можете користуватись його методами:
import { createWriteStream } from 'node:fs';
import { controller, Res, route } from '@ditsmod/core';
import { MulterParsedForm, MulterParser } from '@ditsmod/body-parser';
@controller()
export class SomeController {
@route('POST', 'file-upload')
async downloadFile(res: Res, parse: MulterParser) {
const parsedForm = await parse.array('fieldName', 5);
await this.saveFiles(parsedForm);
// ...
res.send('ok');
}
protected saveFiles(parsedForm: MulterParsedForm) {
const promises: Promise<void>[] = [];
parsedForm.files.forEach((file) => {
const promise = new Promise<void>((resolve, reject) => {
const path = `uploaded-files/${file.originalName}`;
const writableStream = createWriteStream(path).on('error', reject).on('finish', resolve);
file.stream.pipe(writableStream);
});
promises.push(promise);
});
return Promise.all(promises);
}
}
- Якщо контролер є одинаком, через DI необхідно запитати
MulterSingletonParser
, після чого можете користуватись його методами:
import { createWriteStream } from 'node:fs';
import { controller, route, SingletonRequestContext } from '@ditsmod/core';
import { MulterParsedForm, MulterSingletonParser } from '@ditsmod/body-parser';
@controller({ isSingleton: true })
export class SomeController {
constructor(protected parse: MulterSingletonParser) {}
@route('POST', 'file-upload')
async downloadFile(ctx: SingletonRequestContext) {
const parsedForm = await this.parse.array(ctx, 'fieldName', 5);
await this.saveFiles(parsedForm);
// ...
ctx.nodeRes.end('ok');
}
protected saveFiles(parsedForm: MulterParsedForm) {
const promises: Promise<void>[] = [];
parsedForm.files.forEach((file) => {
const promise = new Promise<void>((resolve, reject) => {
const path = `uploaded-files/${file.originalName}`;
const writableStream = createWriteStream(path).on('error', reject).on('finish', resolve);
file.stream.pipe(writableStream);
});
promises.push(promise);
});
return Promise.all(promises);
}
}