nestjs / nest

A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications with TypeScript/JavaScript 🚀
https://nestjs.com
MIT License
66.9k stars 7.56k forks source link

RawBody with Binary #10587

Closed UncleFirefox closed 1 year ago

UncleFirefox commented 1 year ago

Is there an existing issue for this?

Current behavior

Hi, I'm trying to use the rawBody feature with binary in Postman but it does not work.

image

I thought passing binary would have the buffer value field on req.rawBody but it does not, is this intended to work with binary? NOTE: I configured the feature correctly and if I pass a raw json I see a buffer there so I guess rawBody works but not for this case?

Some people created this gist but I find it rather ugly: https://gist.github.com/jonilsonds9/efc228e34a298fa461d378f48ef67836

Minimum reproduction code

-

Steps to reproduce

No response

Expected behavior

req.rawBody has the buffer of the attached file

Package

Other package

No response

NestJS version

No response

Packages versions

"@nestjs/common": "^9.0.8",
    "@nestjs/config": "^2.2.0",
    "@nestjs/core": "^9.0.8",
    "@nestjs/platform-express": "^9.0.8",

Node.js version

No response

In which operating systems have you tested?

Other

No response

micalevisk commented 1 year ago

why reproductions are required

jmcdo29 commented 1 year ago

Nest does not add req.rawBody based on the raw income, rather it modifies the body-parser signature for application/json and application/x-www-form-urlencoded to take the original raw body and set it to req.rawBody. This means that if you send a data type that does not match, then req.rawBody will not be set.

https://github.com/nestjs/nest/blob/master/packages/platform-express/adapters/utils/get-body-parser-options.util.ts

micalevisk commented 1 year ago

@UncleFirefox feel free to update the docs on raw body to mention the behavior described above.

UncleFirefox commented 1 year ago

@jmcdo29 I was thinking about being able to include application/octet-stream along with the others you mentioned, I believe it would be useful for other people would it be so hard to do so? I could contribute if you tell me where the code resides in nest.

Cheers!

micalevisk commented 1 year ago

@UncleFirefox

clintonb commented 9 months ago

@micalevisk can you say more, please? It's a year later, and I'm encountering this issue but there are zero docs. You've provided two links, but it's unclear what someone needs to do to receive other content types.

micalevisk commented 9 months ago

@clintonb please use our discord for support

I don't recall neither

clintonb commented 9 months ago

I adapted https://stackoverflow.com/a/61416426/592820. This requires installing raw-body. I have rawBody: true on the NestFactory.create() call, but have no clue if it's required to get the raw body to the controller. I need it to handle AWS signature verification for the S3 proxy I'm building.

I created a decorator to apply to a controller route's arguments.

import {
  BadRequestException,
  ExecutionContext,
  createParamDecorator,
} from '@nestjs/common';
import getRawBody from 'raw-body';

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

    return getRawBody(req);
  }
);

Here it is in action:

// controller.ts

@Put('*')
@Header('Content-Type', 'application/xml')
async put(@Req() request, @Param() params, @RawBody() body) {
  const name = request.params[0];
  return this.s3ProxyService.putObject({
    bucketName: BUCKET,
    name,
    content: body,
  });
}
micalevisk commented 9 months ago

this should be enough: https://stackoverflow.com/a/75642036 (as per https://docs.nestjs.com/faq/raw-body)

clintonb commented 9 months ago

I tried that. It doesn't seem to work with binary data. body is set to an empty object ({}) and rawBody is undefined.

I also tried setting app.useBodyParser('raw').

clintonb commented 9 months ago

Note: I suspect the reason none of the other solutions work in my case is because the S3 client does not set a Content-Type header automatically, but expects the caller to do so. The parsers aren't triggered because the non-existent content type does not match expectations.

Using something like this sets rawBody, but causes other issues with my signature verification. I'll share here as I learn more.

app.useBodyParser('raw', {
  type: (req) => {
    // Apply to *all* requests, regardless of content type.
    return true;
  },
});