如何使用 NestJS 将纯文本作为我的请求正文传递?

Posted

技术标签:

【中文标题】如何使用 NestJS 将纯文本作为我的请求正文传递?【英文标题】:How do I pass plain text as my request body using NestJS? 【发布时间】:2019-02-16 10:02:24 【问题描述】:

我的 NestJS 应用程序中的一个控制器方法应该将纯文本作为其主体,但每当我尝试发出请求时,参数都会作为空对象接收。这甚至可能吗,还是我必须创建某种 DTO 来传递该单个字符串?

例子:

@Post()
  myFunction(@Body() id: string) 
    // do something here
  

【问题讨论】:

您传递的内容类型是否有效? 我在 Postman 中尝试过 "text" 和 "text/plain",但它们都没有被 Nest 作为字符串拾取。 【参考方案1】:

如果您希望避免额外的第 3 方依赖项,您还可以在此处使用 built-in nodejs 方法:

function readPost(req: IncomingMessage) 
  return new Promise<string>((resolve, reject) => 
    let body = '';
    req.on('data', (data: string) => (body += data));
    req.on('error', (error: unknown) => reject(error));
    req.on('end', () => resolve(body));
  );

用法:

import  Post, Req  from '@nestjs/common';
import  IncomingMessage  from 'http';
...
@Post()
myFunction(@Req() req: IncomingMessage) 
  const bodyStr = await readPost(req);
  console.log('request body:', bodyStr);

【讨论】:

【参考方案2】:

这是我对在 NestJS 的处理程序中获取原始(文本)正文的看法:

    使用preserveRawBodyInRequest 配置应用程序,如 JSDoc 示例所示 在处理程序中使用 RawBody 装饰器来检索原始(文本)正文

原始请求.decorator.ts:

import  createParamDecorator, ExecutionContext  from '@nestjs/common';
import  NestExpressApplication  from "@nestjs/platform-express";

import  json, urlencoded  from "express";
import type  Request  from "express";
import type http from "http";

export const HTTP_REQUEST_RAW_BODY = "rawBody";

/**
 * make sure you configure the nest app with <code>preserveRawBodyInRequest</code>
 * @example
 * webhook(@RawBody() rawBody: string): Record<string, unknown> 
 *   return  received: true ;
 * 
 * @see preserveRawBodyInRequest
 */
export const RawBody = createParamDecorator(
  async (data: unknown, context: ExecutionContext) => 
    const request = context
      .switchToHttp()
      .getRequest<Request>()
    ;

    if (!(HTTP_REQUEST_RAW_BODY in request)) 
      throw new Error(
        `RawBody not preserved for request in handler: $context.getClass().name::$context.getHandler().name`,
      );
    

    const rawBody = request[HTTP_REQUEST_RAW_BODY];

    return rawBody;
  ,
);

/**
 * @example
 * const app = await NestFactory.create<NestExpressApplication>(
 *   AppModule,
 *   
 *     bodyParser: false, // it is prerequisite to disable nest's default body parser
 *   ,
 * );
 * preserveRawBodyInRequest(
 *   app,
 *   "signature-header",
 * );
 * @param app
 * @param ifRequestContainsHeader
 */
export function preserveRawBodyInRequest(
  app: NestExpressApplication,
  ...ifRequestContainsHeader: string[]
): void 
  const rawBodyBuffer = (
    req: http.IncomingMessage,
    res: http.ServerResponse,
    buf: Buffer,
  ): void => 
    if (
      buf?.length
      && (ifRequestContainsHeader.length === 0
        || ifRequestContainsHeader.some(filterHeader => req.headers[filterHeader])
      )
    ) 
      req[HTTP_REQUEST_RAW_BODY] = buf.toString("utf8");
    
  ;

  app.use(
    urlencoded(
      
        verify: rawBodyBuffer,
        extended: true,
      ,
    ),
  );
  app.use(
    json(
      
        verify: rawBodyBuffer,
      ,
    ),
  );

【讨论】:

【参考方案3】:

老问题,但以上都没有对我有用,但以下对我有用:

上述装饰器或控制器方法方法对我不起作用,因为请求正文缓冲区始终已被读取。

我能够使用以下中间件使其正常工作。 (请注意,在我的情况下,我需要验证 Xero webhook,因此该示例适用于此)

cache-raw-body-on-request.ts:

import  json  from 'body-parser';
import * as cloneBuffer from 'clone-buffer';

export const cachedRawBodyRequestKey = 'rawBodyBuffer';

/**
 * Clones the request buffer and stores it on the request object for reading later 
 */
export const cacheRawBodyOnRequest = json(
  verify: (req: any, res, buf, encoding) => 

    // only clone the buffer if we're receiving a Xero webhook request
    if (req.headers['x-xero-signature'] && Buffer.isBuffer(buf)) 
      req[cachedRawBodyRequestKey] = cloneBuffer(buf);
    
    return true;
  ,
);

main.ts:

app.use(cacheRawBodyOnRequest);

控制器:

const textBody = req[cachedRawBodyRequestKey].toString('utf-8');

【讨论】:

【参考方案4】:

添加@yumaa's post above

这是 NestJS v7.0.8 的工作装饰器:

import  createParamDecorator, ExecutionContext, BadRequestException  from '@nestjs/common';
import * as rawBody from "raw-body";

export const PlainBody = createParamDecorator(async (_, context: ExecutionContext) => 
    const req = context.switchToHttp().getRequest<import("express").Request>();
    if (!req.readable)  throw new BadRequestException("Invalid body"); 

    const body = (await rawBody(req)).toString("utf8").trim();
    return body;
)

【讨论】:

【参考方案5】:

我看到这个问题很老了,但它在谷歌中排名第一,所以我想在这里添加答案。

如果您不想添加 body-parser 中间件(例如,您只希望在单个控制器方法中使用纯文本),您可以使用 raw-body(您的 node_modules 中已经存在),如下所示:

import * as rawbody from 'raw-body';
import  Controller, Post, Body, Req  from '@nestjs/common';

@Controller('/')
export class IndexController 

  @Post()
  async index(@Body() data, @Req() req) 

    // we have to check req.readable because of raw-body issue #57
    // https://github.com/stream-utils/raw-body/issues/57
    if (req.readable) 
      // body is ignored by NestJS -> get raw body from request
      const raw = await rawbody(req);
      const text = raw.toString().trim();
      console.log('body:', text);

     else 
      // body is parsed by NestJS
      console.log('data:', data);
    

    // ...
  


你也可以创建新的参数装饰器

import * as rawbody from 'raw-body';
import  createParamDecorator, HttpException, HttpStatus  from '@nestjs/common';

export const PlainBody = createParamDecorator(async (data, req) => 
  if (req.readable) 
    return (await rawbody(req)).toString().trim();
  
  throw new HttpException('Body aint text/plain', HttpStatus.INTERNAL_SERVER_ERROR);
);

并像使用它

@Post()
async index(@PlainBody() text: string) 
  // ...

(我没有检查装饰器代码,在评论中写在这里)

【讨论】:

虽然这行得通(我测试过),但唯一的问题是你不能严格控制类型,因为 rawbody 要求解析到其中的任何内容都是可流式的,而 @Req 实际上不是的 Request 类型那。因此,您必须关闭严格输入才能使其正常工作,例如,如果您还想检查内容长度(您应该这样做)但是可以,所以谢谢! @yumaa 你让我的开发者度过了一个愉快的夜晚! :)【参考方案6】:

Nest 与纯文本不兼容,您必须将 bodyparser 传递给您的 express 应用程序。试试这样的:

import * as bodyParser from 'body-parser';


async function bootstrap() 
  const app = await NestFactory.create(AppModule);
  app.use(bodyparser( ...options )) // for plain/text bodies
  await app.listen(3000)

bootstrap();

其中 options 是从 https://www.npmjs.com/package/body-parser

创建的

【讨论】:

【参考方案7】:

发布请求的语义由指示内容类型的标头确定。尝试确保请求标头的类型为“text/plain”并查看是否有帮助。

https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST

【讨论】:

我在 Postman 中尝试过 "text" 和 "text/plain",但它们都没有被 Nest 作为字符串拾取。

以上是关于如何使用 NestJS 将纯文本作为我的请求正文传递?的主要内容,如果未能解决你的问题,请参考以下文章

AFNetworking 2.0:是不是可以将纯 json 放入 POST 请求的正文中?

如何将纯文本发布到 WCF 服务,即不包含在 XML 标记中?

如何在 NestJS 控制器处理程序的请求中获取“已验证的正文”和“已验证的用户”?

如何在 swagger nestjs 中手动添加摘要和正文

请求正文中的布尔参数在 NestJS api 中始终为真

如何在 RestSharp 中向请求正文添加文本