Как мне аннотировать конечную точку в NestJS для OpenAPI, которая принимает данные многокомпонентной формы

#file-upload #swagger #nestjs #openapi #nestjs-swagger

#загрузка файла #развязность #nestjs #openapi #nestjs-развязность

Вопрос:

На моем сервере NestJS есть конечная точка, которая принимает файлы, а также дополнительные данные формы, например, я передаю файл и идентификатор пользователя создателя файла в форме.

введите описание изображения здесь

NestJS Swagger необходимо явно указать, что body содержит файл и что конечная точка использует multipart/form-data это не задокументировано в документах NestJS https://docs.nestjs.com/openapi/types-and-parameters#types-and-parameters .

Ответ №1:

К счастью, некоторые ошибки привели к обсуждению того, как справиться с этим вариантом использования

глядя на эти два обсуждения https://github.com/nestjs/swagger/issues/167
https://github.com/nestjs/swagger/issues/417 Мне удалось собрать воедино следующее

Я добавил аннотацию с использованием DTO: две критические части:

в DTO добавьте

   @ApiProperty({
    type: 'file',
    properties: {
      file: {
        type: 'string',
        format: 'binary',
      },
    },
  })
  public readonly file: any;

  @IsString()
  public readonly user_id: string;
 

в контроллере добавьте

 @ApiConsumes('multipart/form-data')
 

это дает мне рабочую конечную точку

и этот OpenAPI Json

 {
   "/users/files":{
      "post":{
         "operationId":"UsersController_addPrivateFile",
         "summary":"...",
         "parameters":[
            
         ],
         "requestBody":{
            "required":true,
            "content":{
               "multipart/form-data":{
                  "schema":{
                     "$ref":"#/components/schemas/UploadFileDto"
                  }
               }
            }
         }
      }
   }
}
 

 {
   "UploadFileDto":{
      "type":"object",
      "properties":{
         "file":{
            "type":"file",
            "properties":{
               "file":{
                  "type":"string",
                  "format":"binary"
               }
            },
            "description":"...",
            "example":"'file': <any-kind-of-binary-file>"
         },
         "user_id":{
            "type":"string",
            "description":"...",
            "example":"cus_IPqRS333voIGbS"
         }
      },
      "required":[
         "file",
         "user_id"
      ]
   }
}
 

введите описание изображения здесь

Комментарии:

1. Ваш ответ не читается, я бы хотел, чтобы вы предоставили полный пример, то, что я вижу на изображениях, — это то, что мне нужно, но я не мог понять, куда поместить предоставленный вами код.

Ответ №2:

Вот что я нахожу более чистым подходом:

 @Injectable()
class FileToBodyInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const ctx = context.switchToHttp();
    const req = ctx.getRequest();
    if(req.body amp;amp; req.file?.fieldname) {
      const { fieldname } = req.file;
      if(!req.body[fieldname]) {
        req.body[fieldname] = req.file;
      }
    }

    return next
      .handle();
  }
}

const ApiFile = (options?: ApiPropertyOptions): PropertyDecorator => (
  target: Object, propertyKey: string | symbol
) => {
  ApiProperty({
    type: 'file',
    properties: {
      [propertyKey]: {
        type: 'string',
        format: 'binary',
      },
    },
  })(target, propertyKey);
};

class UserImageDTO {
  @ApiFile()
  file: Express.Multer.File;  // you can name it something else like image or photo
  
  @ApiProperty()
  user_id: string;
}


@Controller('users')
export class UsersController {

  @ApiBody({ type: UserImageDTO })
  // @ApiResponse( { type: ... } ) // some dto to annotate the response
  @Post('files')
  @ApiConsumes('multipart/form-data')
  @UseInterceptors(
    FileInterceptor('file'),  //this should match the file property name
    FileToBodyInterceptor,  // this is to inject the file into the body object
  ) 
  async addFile(@Body() userImage: UserImageDTO): Promise<void> { // if you return something to the client put it here
    console.log({modelImage}); // all the fields and the file
    console.log(userImage.file); // the file is here

    // ... your logic
  }
}

 

FileToBodyInterceptor и ApiFile , как правило, я желаю, чтобы они были в NestJS

Вероятно, вам нужно установить @types/multer , чтобы Express.Multer.File