NestJS参数解析黑科技:5种装饰器搞定全场景HTTP请求处理

在构建现代Web应用时,处理HTTP请求参数是每个后端开发者必须掌握的核心技能。NestJS作为一款渐进式Node.js框架,通过一系列优雅的装饰器让参数解析变得异常简单。本文将深入探讨如何利用NestJS的5种核心装饰器处理各种HTTP请求场景,并通过电商API实例展示最佳实践。

1. 初识NestJS参数装饰器体系

NestJS的参数装饰器是框架最精妙的设计之一,它们像瑞士军刀一样可以处理各种HTTP请求参数。与传统Express/Koa手动解析req对象不同,NestJS通过装饰器自动完成参数提取和类型转换,让代码更加简洁和类型安全。

核心装饰器对比表

装饰器 对应Express对象 典型应用场景 是否支持直接取值
@Query() req.query 获取URL查询参数
@Body() req.body 获取POST/PUT请求体
@Param() req.params 获取路由参数
@Headers() req.headers 获取请求头信息
@Ip() req.ip 获取客户端IP地址

这些装饰器可以单独使用,也可以组合使用。比如在电商API中,我们可能同时需要获取商品ID(路由参数)、分页信息(查询参数)和认证令牌(请求头):

@Get('products/:id')
getProduct(
  @Param('id') id: string,
  @Query() pagination: PaginationDto,
  @Headers('authorization') token: string
) {
  // 业务逻辑
}

提示:使用@Query()@Param()时,NestJS会自动将字符串参数转换为装饰器参数指定的类型。例如@Param('id') id: number会将路由参数自动转为数字类型。

2. 查询参数解析:@Query的进阶用法

@Query装饰器是处理GET请求的利器。在电商系统中,商品列表接口通常需要支持复杂查询条件:

@Get('products')
searchProducts(
  @Query() params: {
    keyword?: string;
    minPrice?: number;
    maxPrice?: number;
    category?: string;
    sortBy?: 'price' | 'rating';
    page?: number;
    limit?: number;
  }
) {
  // 构建查询条件
  const { keyword, minPrice, maxPrice, category, sortBy, page = 1, limit = 10 } = params;
  
  return this.productService.search({
    keyword,
    priceRange: { min: minPrice, max: maxPrice },
    category,
    sortBy,
    pagination: { page, limit }
  });
}

查询参数处理技巧

  • 默认值处理:使用解构赋值设置默认值,如page = 1
  • 参数验证:结合class-validator创建DTO类进行自动验证
  • 类型转换:NestJS会自动将字符串查询参数转为指定的类型

对于复杂查询条件,推荐使用查询参数DTO模式:

class ProductQueryDto {
  @IsOptional()
  @IsString()
  keyword?: string;

  @IsOptional()
  @IsNumber()
  @Min(0)
  minPrice?: number;

  // 其他参数...
}

@Get('products')
searchProducts(@Query() query: ProductQueryDto) {
  // 业务逻辑
}

3. 请求体处理:@Body与DTO的完美结合

POST/PUT请求通常携带复杂的JSON数据,@Body装饰器让请求体处理变得简单。在电商系统中,创建订单是典型场景:

@Post('orders')
createOrder(
  @Body() orderData: {
    userId: string;
    items: Array<{
      productId: string;
      quantity: number;
      price: number;
    }>;
    shippingAddress: {
      street: string;
      city: string;
      zipCode: string;
    };
    paymentMethod: string;
  }
) {
  return this.orderService.create(orderData);
}

请求体处理最佳实践

  1. 使用DTO类:创建专门的DTO类定义请求体结构
  2. 自动验证:配合class-validator进行数据验证
  3. 类型转换:自动将JSON数据转换为TypeScript类型

更规范的DTO实现示例:

class OrderItemDto {
  @IsString()
  productId: string;

  @IsNumber()
  @Min(1)
  quantity: number;

  @IsNumber()
  @Min(0)
  price: number;
}

class CreateOrderDto {
  @IsString()
  userId: string;

  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => OrderItemDto)
  items: OrderItemDto[];

  @IsObject()
  @ValidateNested()
  @Type(() => ShippingAddressDto)
  shippingAddress: ShippingAddressDto;

  @IsString()
  paymentMethod: string;
}

@Post('orders')
createOrder(@Body() orderData: CreateOrderDto) {
  // 业务逻辑
}

注意:使用DTO时需要在main.ts中全局启用验证管道:app.useGlobalPipes(new ValidationPipe());

4. 路由参数与请求头处理技巧

动态路由和请求头处理是Web API的常见需求。@Param@Headers装饰器让这些操作变得直观。

动态路由参数处理

@Get('products/:id')
getProduct(@Param('id') productId: string) {
  return this.productService.findById(productId);
}

@Get('categories/:category/products')
getProductsByCategory(
  @Param('category') category: string,
  @Query() pagination: PaginationDto
) {
  return this.productService.findByCategory(category, pagination);
}

请求头处理示例

@Get('profile')
getUserProfile(@Headers('authorization') token: string) {
  if (!token) {
    throw new UnauthorizedException('Missing authentication token');
  }
  
  return this.userService.getProfile(token);
}

实用技巧

  • 路由参数可以整体获取或单独获取:@Param() vs @Param('id')
  • 请求头名称不区分大小写:@Headers('Authorization')@Headers('authorization')等效
  • 可以创建自定义装饰器组合常用头信息处理逻辑

5. 高级技巧:装饰器组合与自定义管道

NestJS的参数处理能力可以通过自定义装饰器和管道进一步扩展。

自定义用户装饰器

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const CurrentUser = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  }
);

@Get('profile')
getProfile(@CurrentUser() user: UserEntity) {
  // 直接获取已认证的用户对象
}

自定义参数管道

import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';

@Injectable()
export class ParseObjectIdPipe implements PipeTransform<string, string> {
  transform(value: string): string {
    if (!/^[0-9a-fA-F]{24}$/.test(value)) {
      throw new BadRequestException('Invalid ObjectId');
    }
    return value;
  }
}

@Get('products/:id')
getProduct(@Param('id', ParseObjectIdPipe) id: string) {
  // id保证是合法的MongoDB ObjectId
}

装饰器组合示例

function AuthToken() {
  return applyDecorators(
    Headers('authorization'),
    ApiHeader({ name: 'authorization', description: 'Bearer token' })
  );
}

@Get('protected')
protectedRoute(@AuthToken() token: string) {
  // 组合了Headers装饰器和Swagger文档
}

在实际项目中,合理运用这些高级技巧可以大幅提升代码的可读性和可维护性。比如电商系统中的支付回调接口:

@Post('payments/callback')
handlePaymentCallback(
  @Body() data: PaymentCallbackDto,
  @Headers('x-signature') signature: string,
  @CurrentUser() user: UserEntity
) {
  return this.paymentService.handleCallback({
    data,
    signature,
    userId: user.id
  });
}
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐