You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NestJS中根据用户角色动态调整API返回字段

在NestJS中根据用户角色动态隐藏/显示返回字段的方案

方案一:使用ClassSerializerInterceptor配合class-transformer分组功能(推荐)

这是NestJS官方推荐的序列化方式,能优雅地控制字段的显示逻辑。

步骤1:安装依赖

确保项目中已安装class-transformer和class-validator(NestJS CLI创建的项目通常默认包含):

npm install class-transformer class-validator

步骤2:定义带分组的DTO

创建产品DTO,通过@Exclude默认隐藏createdBy,再用@Expose指定仅管理员/店铺管理员角色可见:

import { Exclude, Expose } from 'class-transformer';

export class ProductDto {
  title: string;
  price: number;
  category: string;

  // 默认隐藏该字段
  @Exclude()
  createdBy: string;

  // 仅指定分组的请求能看到这个字段
  @Expose({ groups: ['admin', 'shop-admin'] })
  get adminCreatedBy() {
    return this.createdBy;
  }
}

步骤3:控制器中动态设置序列化分组

通过@SerializeOptions的回调函数,根据当前请求的用户角色返回对应分组,同时启用ClassSerializerInterceptor:

import { Controller, Get, Param, UseInterceptors, SerializeOptions, Request } from '@nestjs/common';
import { ClassSerializerInterceptor } from '@nestjs/common/serializer';
import { plainToInstance } from 'class-transformer';
import { ProductDto } from './product.dto';
import { ProductService } from './product.service';

@Controller('product')
@UseInterceptors(ClassSerializerInterceptor)
export class ProductController {
  constructor(private readonly productService: ProductService) {}

  @Get(':id')
  @SerializeOptions({
    groups: (context) => {
      // 从请求中获取已认证用户的角色信息(需先完成身份验证,比如JWT)
      const user = context.switchToHttp().getRequest().user;
      const groups = [];
      if (user?.roles?.includes('admin') || user?.roles?.includes('shop-admin')) {
        groups.push('admin', 'shop-admin');
      }
      return groups;
    },
  })
  async getProduct(@Param('id') id: string): Promise<ProductDto> {
    const product = await this.productService.getProductById(id);
    // 将数据库原始数据转换为DTO实例,触发序列化逻辑
    return plainToInstance(ProductDto, product);
  }
}

方案二:手动过滤字段(简单直接)

如果需求简单,可在控制器或服务层直接根据角色删除不需要的字段:

import { Controller, Get, Param, Request } from '@nestjs/common';
import { ProductService } from './product.service';

@Controller('product')
export class ProductController {
  constructor(private readonly productService: ProductService) {}

  @Get(':id')
  async getProduct(@Param('id') id: string, @Request() req) {
    const product = await this.productService.getProductById(id);
    const user = req.user;

    // 非管理员角色删除createdBy字段
    if (!(user?.roles?.includes('admin') || user?.roles?.includes('shop-admin'))) {
      delete product.createdBy;
    }

    return product;
  }
}

这种方式适合字段少、逻辑简单的场景,但字段增多后维护成本会上升。

方案三:自定义拦截器统一处理

创建自定义拦截器,在全局或控制器层面统一处理字段过滤逻辑:

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class DynamicFieldInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();
    const user = request.user;

    return next.handle().pipe(
      map((responseData) => {
        // 非管理员角色过滤字段
        if (!(user?.roles?.includes('admin') || user?.roles?.includes('shop-admin'))) {
          delete responseData.createdBy;
        }
        return responseData;
      }),
    );
  }
}

然后在控制器中使用该拦截器:

import { Controller, Get, Param, UseInterceptors } from '@nestjs/common';
import { DynamicFieldInterceptor } from './dynamic-field.interceptor';
import { ProductService } from './product.service';

@Controller('product')
@UseInterceptors(DynamicFieldInterceptor)
export class ProductController {
  constructor(private readonly productService: ProductService) {}

  @Get(':id')
  async getProduct(@Param('id') id: string) {
    return this.productService.getProductById(id);
  }
}

注意事项

  • 所有方案的前提是已完成用户身份验证,能从req.user中获取用户角色信息(比如通过JWT守卫解析token)。
  • 如果使用TypeORM实体,可直接在实体类上添加class-transformer装饰器,无需额外创建DTO,查询出的实体实例可直接参与序列化。

内容的提问来源于stack exchange,提问作者Night owl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 15:20:24