Fork me on GitHub

Spring boot集成swagger

前面一片文章在介绍时有提到过swagger,这篇文章就重点介绍一下Spring boot集成swagger的方法与代码实践。

引入依赖

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>

 如截图所示,springfox-boot-starter依赖提供了swagger与swagger-ui以及一些必要非必要的依赖。

yml文件

spring:
  # 新增该配置处理程序启动swagger路径查找报错
  mvc:
    path-match:
      matching-strategy: ant_path_matcher

server:
  port: 8080

swagger:
  enable: true
  application-name: ${spring.application.name}
  application-version: 1.0
  application-description: ***平台接口文档
  try-host: http://localhost:${server.port}
Swagger配置文件
@Data
@Component
@ConfigurationProperties("swagger")
public class SwaggerProperties {
    /**
     * 是否开启swagger,生产环境一般关闭,所以这里定义一个变量
     */
    private Boolean enable;

    /**
     * 项目应用名
     */
    private String applicationName;

    /**
     * 项目版本信息
     */
    private String applicationVersion;

    /**
     * 项目描述信息
     */
    private String applicationDescription;

    /**
     * 接口调试地址
     */
    private String tryHost;
}

swagger配置

@EnableOpenApi
@Configuration
public class SwaggerConfig implements WebMvcConfigurer {
    private final SwaggerProperties swaggerProperties;
    private final RepeatSubmitInterceptor repeatSubmitInterceptor;

    /**
     * 首页地址
     */
    @Value("${shiro.user.indexUrl}")
    private String indexUrl;

    public SwaggerConfig(SwaggerProperties swaggerProperties,
                         RepeatSubmitInterceptor repeatSubmitInterceptor) {
        this.swaggerProperties = swaggerProperties;
        this.repeatSubmitInterceptor = repeatSubmitInterceptor;
    }


    @Override
    public void addViewControllers(ViewControllerRegistry registry)
    {
        registry.addViewController("/").setViewName("forward:" + indexUrl);
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //swagger配置
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.OAS_30).pathMapping("/")

                // 定义是否开启swagger,false为关闭,可以通过变量控制
                .enable(swaggerProperties.getEnable())

                // 将api的元信息设置为包含在json ResourceListing响应中。
                .apiInfo(apiInfo())

                // 接口调试地址
                .host(swaggerProperties.getTryHost())

                // 选择哪些接口作为swagger的doc发布
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()
                .globalRequestParameters(
                        singletonList(new springfox.documentation.builders.RequestParameterBuilder()
                                .name("Authentication")
                                .description("token")
                                .in(ParameterType.HEADER)
                                .required(true)
                                .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
                                .build()))
                // 支持的通讯协议集合
                .protocols(newHashSet("https", "http"));
    }

    /**
     * API 页面上半部分展示信息
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title(swaggerProperties.getApplicationName() + " Api Doc")
                .description(swaggerProperties.getApplicationDescription())
                .version("Application Version: " + swaggerProperties.getApplicationVersion() + ", Spring Boot Version: " + SpringBootVersion.getVersion())
                .build();

    }

    @SafeVarargs
    private <T> Set<T> newHashSet(T... ts) {
        if (ts.length > 0) {
            return new LinkedHashSet<>(Arrays.asList(ts));
        }
        return Collections.emptySet();
    }

    /**
     * 通用拦截器排除swagger设置,所有拦截器都会自动加swagger相关的资源排除信息
     */
    @SuppressWarnings("unchecked")
    @Override
    public void addInterceptors(@NonNull InterceptorRegistry registry) {
        try {
            registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
            Field registrationsField = FieldUtils.getField(InterceptorRegistry.class, "registrations", true);
            List<InterceptorRegistration> registrations = (List<InterceptorRegistration>) ReflectionUtils.getField(registrationsField, registry);
            if (registrations != null) {
                for (InterceptorRegistration interceptorRegistration : registrations) {
                    interceptorRegistration
                            .excludePathPatterns("/**/swagger**/**")
                            .excludePathPatterns("/**/webjars/**")
                            .excludePathPatterns("/**/v3/**")
                            .excludePathPatterns("/**/doc.html");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

启动类上加上注解@EnableSwagger2

swagger核心的注解就@ApiModel、@ApiModelProperty、@Api、@ApiOperation,@ApiParam() 、@ApiIgnore()这几个,下面详细介绍如何使用
Domain层

@Data
@ApiModel(value = "property", description = "模型属性")
//apiModel说明该domain的整体数据类型/内容
@TableName(value = "t_property", autoResultMap = true)
//autoResultMap = true主要针对数据库内json/jsonb格式数据做映射关系,防止相应数据查询时返回null,此处不配置默认false.
//也可以再xml中自定义resultMap
public class Property implements Serializable {
    /**
     * 主键
     */
    @TableId
    @JsonFormat(shape = JsonFormat.Shape.STRING)
    //pgsql数据库使用的内置主键策略,类型为long,但前后端交互过程会出现精度丢失,
    //故(反)序列化是转换成string
    private Long id;

    /**
     * 属性key
     */
    @ApiModelProperty(value = "属性key", name = "key", example = "key1", required = true)
    //ApiModelProperty主要作用于参数上 
    // value为字段的中文含义 name为对应key(可忽略)
    // example为该字段的value示例 required标识该字段必传
    @NotNull(message = "属性KEY不能为空")
    //个人习惯针对api创建不同的domain,故针对必传字段使用@NotNull配合controller层的@Validated注解进行判空。
    //还有一种方案是 在业务层进行非空判断。 根据个人开发情况而定吧
    private String key;

    /**
     * 数据类型
     */
    @ApiModelProperty(value = "数据类型", name = "dataType", example = "enum", required = true)
    @NotNull(message = "属性数据类型不能为空")
    private String dataType;

    /**
     * 属性名称
     */
    @ApiModelProperty(value = "数据类型", name = "dataType", example = "name1", required = true)
    @NotNull(message = "属性名称不能为空")
    private String name;

    /**
     * 数据描述
     */
    @TableField(typeHandler = FastjsonTypeHandler.class)
    //针对数据库内json/josnb格式数据需要进行类型处理,这里直接使用的第三方提供的handler,也可以自定义handler
    private JSONObject dataInfo;

    /**
     * 标签
     */
    @TableField(typeHandler = FastjsonTypeHandler.class)
    private JSONArray tag;

    /**
     * 创建时间
     */
    @ApiModelProperty(hidden = true)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    //针对时间类型个人偏好java8新增的LocalDateTime,涉及国际化时间问题换成java8的ZonedDateTime即可,ZonedDateTime+ZoneId处理时区。
    private LocalDateTime createTime;

    /**
     * 创建人
     */
    @ApiModelProperty(hidden = true)
    //hidden表示该字段不在文档中展示
    private String createBy;

    /**
     * 更新人
     */
    @ApiModelProperty(hidden = true)
    private String updateBy;

    /**
     * 更新时间
     */
    @ApiModelProperty(hidden = true)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private LocalDateTime updateTime;

    /**
     * 单位
     */
    @NotNull(message = "属性单位不能为空")
    private String unit;

    /**状态 1-启用 0-禁用**/
    @ApiModelProperty(value = "属性状态(1-启用 0-禁用)", name = "status", example = "1")
    private Integer status;

    /**读写类型 1-只读 2-只写 3-读写**/
    private Integer rwType;

    /**
     * 操作人
     **/
    @TableField(exist = false)
    //该注解表示在数据库不存在,不会进行持久化,但可以参与序列化,相似的注解还有@Transient
    private String username;

    private static final long serialVersionUID = 1L;
}

controller层

@Api(value="模型管理")
//介绍整个controler整体的业务范围
@RestController
@RequestMapping("/module/detail")
public class ModuleDetailController {

    @Resource
    ModuleService moduleService;

    /**
     * 模型新增
     * @param module 实体
     * @return com.hd.uws.result.Res<?>
     * @author Jackpot
     * @date 2022/5/27 09:15
     */
    @ApiOperation(value = "新增模型")
   //@ApiIgnore() 用于类或者方法上,可以不被swagger显示在页面上
//介绍接口的用途 @PostMapping("add") //postMapping表示该接口为post请求 public Res<?> add(@Validated @RequestBody Module module) { //@Validated结合domain内的判空、数据格式化做校验 @RequestBody表示该接口为application/json格式,请求参数放在body内。 //RequestBody 接收的是请求体里面的数据;而RequestParam接收的是key-value  里面的参数 return Boolean.TRUE.equals(moduleService.add(module)) ? Res.ok() : Res.code(ResCode.OPERATION_FAILED); } }

启动程序,最终效果,地址格式也很简单http://ip:port/swagger-ui/index.html。

注:如果程序中有使用shiro等权限控制,需要对swagger-UI及api-docs路径进行anon

 

 当然如果你觉得swagger页面看起来不友好,也可以配合着apifox使用,数据源URL就是swagger UI页面上的**/v3/api-docs

 

 

 

 下面是apifox导入swagger api的效果。图1为所有的接口及数据模型(DTO/VO/ENUM等),图2为接口的详细信息,可以直接输入参数进行模拟请求(但目前还没发现可进行压力测试的入口,如果想要进行压测,可以使用jmeter或者其他第三方(个人推荐locust))

 

posted @ 2022-08-24 10:18  JackpotHan  阅读(1865)  评论(0编辑  收藏  举报