swagger2 同一个实体用在多个不同的controller接口展示不同的字段

情景

同一个实体用在多个不同的controller接口展示不同的字段,如果用过spring Validation校验框架应该都懂它里面有个分组概念,作用就是同一个实体用在多个接口里面但是校验参数不同,但swagger并没有分组概念,swagger显然做不到类似作用,ApiModelProperty注解的有个hidden属性,但这个作用只能要么全显示要么全隐藏,那下面的方法就是针对同一个实体在一个接口的某些属性是隐藏,在另一个接口中是显示的这种情况。

解决方法

1.依赖

<!--swagger2-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.8.0</version>
</dependency>
<!--swagger2 UI-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.8.0</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>

2.自定义注解

添加ApiIgp和ApiNeed自定义注解。
ApiIgp排除实体类中不显示在swagger文档的属性
ApiNeed是只显示实体类中的某些属性

tip:这两个注解都只能操作实体的属性,如果实体中不存在的属性不会报错也不会生效

ApiIgp

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义aop注解 支持swagger的动态属性 排除属性
 */
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIgp {
    String[] value(); //对象属性值
}

ApiNeed

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义aop注解 支持swagger的动态属性 (只)需要属性
 */
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiNeed {
    //对象属性值
    String[] value();
}

3.重写swagger

添加两个配置类 MyParameterBuilderPlugin和MyOperationBuilderPlugin。一个大概针对的是json数据,另一个是非json数据

MyParameterBuilderPlugin

import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import com.honger1234.annotation.ApiIgp;
import com.honger1234.annotation.ApiNeed;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.javassist.*;
import org.apache.ibatis.javassist.bytecode.AnnotationsAttribute;
import org.apache.ibatis.javassist.bytecode.ConstPool;
import org.apache.ibatis.javassist.bytecode.annotation.Annotation;
import org.apache.ibatis.javassist.bytecode.annotation.StringMemberValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;


import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 重写 swagger2 的 ParameterBuilderPlugin 支持自定义白名单黑名单注解
 * 局限:只能对post中的json数据生效,如果要对get的拼接参数请看MyOperationBuilderPlugin
 */
@Component
@Order
@Slf4j
public class MyParameterBuilderPlugin implements ParameterBuilderPlugin {
    @Autowired
    private TypeResolver typeResolver;

    @Override
    public void apply(ParameterContext parameterContext) {
        Resolved
<think>好的,我现在需要帮用户解决如何在 Nest.js 中实现一个字典接口的问题。首先,我得回想一下用户之前的问题历史。之前用户问过React中setState更新后的操作,现在转向了Nest.js,可能他们正在全栈开发,前端用React,后端用Nest.js。用户可能是想了解如何构建一个基础的CRUD接口,特别是字典类的数据管理。 接下来,我需要确定用户的需求层次。用户提到“实现一个字典接口”,可能指的是创建、读取、更新、删除(CRUD)字典项的功能。但可能需要更详细的信息,比如字典的结构,是否需要分页、验证、错误处理等。不过用户没有给出具体细节,所以需要做一个通用的指南,覆盖基本实现步骤。 首先,我应该从项目结构开始。Nest.js通常使用模块化结构,所以需要创建模块、控制器、服务、实体和DTO。考虑到用户可能对Nest.js有一定了解,但可能需要一步步的指导,所以分步骤讲解比较合适。 然后,数据库部分。Nest.js常用TypeORM,所以需要配置数据库连接,创建实体类。这里可能需要选择数据库类型,比如MySQL或PostgreSQL,但为了通用性,可以先用SQLite作为示例,方便用户快速测试。 接下来是实体定义。字典通常有键、值、描述等字段,所以需要定义这些字段的类型和装饰器。同时,添加自动生成的主键ID和自动时间戳,这样用户可以看到完整的实体示例。 然后是DTO,用于数据验证。使用class-validator来添加验证规则,比如字段是否必填,长度限制等。这能帮助用户了解如何确保输入数据的正确性。 服务层负责业务逻辑和数据库交互。需要实现CRUD方法,这里要注意异步操作,使用async/await。同时,处理可能的错误,比如查找不到字典项时抛出NotFoundException,这样用户就知道如何正确返回HTTP错误。 控制器层处理HTTP请求,使用不同的装饰器处理GET、POST、PUT、DELETE等方法。需要注入服务,并在路由处理中调用服务的方法。同时,参数校验和正确的状态码返回也是重点,比如POST返回201 Created,DELETE返回204 No Content。 模块部分需要将各个部分组合起来,导入TypeOrmModule,并配置实体。这样用户能理解模块化的好处,以及如何组织代码。 最后,可能需要添加Swagger文档,方便用户测试接口使用@nestjs/swagger可以自动生成API文档,这对前端开发人员很有帮助,特别是如果用户同时负责前后端的话。 测试部分也很重要,建议用户使用Postman或curl测试接口,并提供示例请求,这样用户可以直接复制粘贴进行测试,确保接口按预期工作。 另外,用户可能对高级功能感兴趣,比如分页查询、软删除、缓存、权限控制等,但作为基础教程,可能先覆盖基础部分,再在最后提及扩展点,供用户参考。 需要确保代码示例正确,符合Nest.js和TypeORM的最佳实践,比如使用Repository模式,依赖注入等。同时,注意代码格式和注释,方便用户理解每一步的作用。 最后,总结整个实现步骤,强调模块化、分层结构和最佳实践,帮助用户建立良好的开发习惯。可能还需要提醒用户根据实际需求调整,比如数据库配置、字段定义等,保持灵活性。</think>以下是在 Nest.js 中实现字典接口的完整指南,采用模块化分层架构,包含基础CRUD功能和最佳实践: --- ### 一、项目结构 ```bash src/ ├── dictionary/ │ ├── dto/ │ │ ├── create-dictionary.dto.ts # 创建DTO │ │ └── update-dictionary.dto.ts # 更新DTO │ ├── entities/ │ │ └── dictionary.entity.ts # 实体类 │ ├── dictionary.controller.ts # 控制器 │ ├── dictionary.service.ts # 服务层 │ └── dictionary.module.ts # 模块 ``` --- ### 二、实体定义(TypeORM) ```typescript // src/dictionary/entities/dictionary.entity.ts import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class Dictionary { @PrimaryGeneratedColumn() id: number; @Column({ unique: true, comment: '字典键名' }) key: string; @Column({ type: 'json', comment: '字典值' }) value: string | object; @Column({ comment: '字典描述' }) description: string; @Column({ default: true, comment: '是否启用' }) isActive: boolean; @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }) createdAt: Date; @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP', onUpdate: 'CURRENT_TIMESTAMP' }) updatedAt: Date; } ``` --- ### 三、DTO 数据验证 ```typescript // src/dictionary/dto/create-dictionary.dto.ts import { IsNotEmpty, IsString, IsObject } from 'class-validator'; export class CreateDictionaryDto { @IsNotEmpty({ message: '键名不能为空' }) @IsString() key: string; @IsNotEmpty({ message: '值不能为空' }) value: string | object; @IsString() description: string; } // src/dictionary/dto/update-dictionary.dto.ts import { PartialType } from '@nestjs/swagger'; import { CreateDictionaryDto } from './create-dictionary.dto'; export class UpdateDictionaryDto extends PartialType(CreateDictionaryDto) {} ``` --- ### 四、服务层实现 ```typescript // src/dictionary/dictionary.service.ts import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Dictionary } from './entities/dictionary.entity'; @Injectable() export class DictionaryService { constructor( @InjectRepository(Dictionary) private readonly dictRepository: Repository<Dictionary>, ) {} // 创建字典项 async create(createDto: CreateDictionaryDto): Promise<Dictionary> { const newDict = this.dictRepository.create(createDto); return await this.dictRepository.save(newDict); } // 查询所有字典项(带分页) async findAll(page = 1, limit = 10): Promise<[Dictionary[], number]> { return await this.dictRepository.findAndCount({ skip: (page - 1) * limit, take: limit, }); } // 按key查询 async findByKey(key: string): Promise<Dictionary> { const dict = await this.dictRepository.findOne({ where: { key } }); if (!dict) throw new NotFoundException(`字典项 ${key} 不存在`); return dict; } // 更新字典项 async update(id: number, updateDto: UpdateDictionaryDto): Promise<Dictionary> { await this.dictRepository.update(id, updateDto); const updatedDict = await this.dictRepository.findOneBy({ id }); if (!updatedDict) throw new NotFoundException(`字典项 #${id} 不存在`); return updatedDict; } // 删除字典项(软删除) async remove(id: number): Promise<void> { const result = await this.dictRepository.softDelete(id); if (result.affected === 0) { throw new NotFoundException(`字典项 #${id} 不存在`); } } } ``` --- ### 五、控制器实现 ```typescript // src/dictionary/dictionary.controller.ts import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common'; import { DictionaryService } from './dictionary.service'; import { CreateDictionaryDto, UpdateDictionaryDto } from './dto'; @Controller('dictionary') export class DictionaryController { constructor(private readonly dictService: DictionaryService) {} @Post() async create(@Body() createDto: CreateDictionaryDto) { return this.dictService.create(createDto); } @Get() async findAll( @Query('page') page: number = 1, @Query('limit') limit: number = 10 ) { return this.dictService.findAll(page, limit); } @Get(':key') async findByKey(@Param('key') key: string) { return this.dictService.findByKey(key); } @Put(':id') async update( @Param('id') id: string, @Body() updateDto: UpdateDictionaryDto ) { return this.dictService.update(+id, updateDto); } @Delete(':id') async remove(@Param('id') id: string) { return this.dictService.remove(+id); } } ``` --- ### 六、模块整合 ```typescript // src/dictionary/dictionary.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Dictionary } from './entities/dictionary.entity'; import { DictionaryService } from './dictionary.service'; import { DictionaryController } from './dictionary.controller'; @Module({ imports: [TypeOrmModule.forFeature([Dictionary])], controllers: [DictionaryController], providers: [DictionaryService], }) export class DictionaryModule {} ``` --- ### 七、API 文档集成(Swagger) ```typescript // main.ts import { NestFactory } from '@nestjs/core'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); // Swagger配置 const config = new DocumentBuilder() .setTitle('Dictionary API') .setDescription('字典管理接口文档') .setVersion('1.0') .addTag('dictionary') .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api-docs', app, document); await app.listen(3000); } bootstrap(); ``` --- ### 八、测试接口 ```bash # 创建字典项 curl -X POST http://localhost:3000/dictionary \ -H "Content-Type: application/json" \ -d '{ "key": "GENDER", "value": {"1":"男","2":"女"}, "description": "性别字典" }' # 查询所有字典项(分页) curl http://localhost:3000/dictionary?page=1&limit=10 # 按key查询 curl http://localhost:3000/dictionary/GENDER # 更新字典项 curl -X PUT http://localhost:3000/dictionary/1 \ -H "Content-Type: application/json" \ -d '{"description": "更新后的性别字典"}' # 删除字典项 curl -X DELETE http://localhost:3000/dictionary/1 ``` --- ### 高级扩展建议 1. **缓存机制**:使用Redis缓存高频访问的字典数据 2. **权限控制**:集成JWT实现接口访问权限控制 3. **国际化**:添加多语言支持字段 4. **版本控制**:使用`@Version`装饰器实现API版本管理 5. **操作日志**:添加审计日志记录字典变更 6. **数据校验**:扩展自定义校验装饰器(如校验key格式) 这个实现方案遵循了Nest.js的最佳实践,采用分层架构设计,具备完整的类型检查和错误处理机制,可直接用于生产环境。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值