Swagger3 (OpenAPI3)自定义参数对象渲染设置ModelConverter

发布时间 2023-08-29 14:31:05作者: iminifly

Swagger2设置方法

以SpringDataJPA里的分页参数Pageable为例, 在使用Swagger2的时候, 可以通过自定义AlternateTypeRule, 修改参数对象的参数渲染; 如下

/**
 * 分页参数实体类参数转换, 让swagger显示正常的传参
 * 
 * @param resolver
 * @return
 */
@Bean
public AlternateTypeRuleConvention pageableConvention(final TypeResolver resolver) {
    return new AlternateTypeRuleConvention() {
        @Override
        public int getOrder() {
            return Ordered.LOWEST_PRECEDENCE;
        }

        @Override
        public List<AlternateTypeRule> rules() {
            List<AlternateTypeRule> altRules = new ArrayList<>();
            altRules.add(newRule(resolver.resolve(Pageable.class), resolver.resolve(PageableAlternate.class)));
            altRules.add(newRule(resolver.resolve(PageRequest.class), resolver.resolve(PageableAlternate.class)));
            return altRules;
        }
    };
}

/**
 * Pageable的替代别名类
 */
@Data
@ApiModel
public class PageableAlternate {
    @ApiModelProperty(value = "第几页, page从0开始计数")
    private Integer page;

    @ApiModelProperty(value = "每页数据数量")
    private Integer size;

    @ApiModelProperty(value = "按属性排序, 格式: 属性(asc|desc)。多字段排序传参eg: ?sort=id,asc&sort=name,desc")
    private List<String> sort;
}

Swagger3设置方法

在swagger3里, 已经内置了SpringDataJPA的Pageable参数的转换器, 无需自行添加了; 这里提供下如何自定义参数转换器:

  1. 自定义转换器实现io.swagger.v3.core.converter.ModelConverter接口

这里有一个GwPageRequest了封装了分页查询条件

/**
 * 分页请求参数
 * 
 * @author 吕晓飞
 * @date 2023-06-27 15:21
 * @version 1.0
 */
public class GwPageRequest implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 当前页码, 从1开始
	 */
	@Getter
	@Setter
	private int pageNumber = 1;

	/**
	 * 每页大小, 默认10条
	 */
	@Getter
	@Setter
	private int pageSize = 10;

	/**
	 * 排序信息
	 */
	@Getter
	@Setter
	private List<GwSort> sort;

	/**
	 * 搜索信息
	 */
	@Getter
	@Setter
	private GwSearch search;

}
/**
 * GwPageRequest的替代别名类
 *
 * @author 吕晓飞
 * @date 2023-06-27 15:21
 * @version 1.0
 */
@Data
@Schema(description = "分页参数")
static class GwPageRequestAlternate {

    @Schema(description = "当前页码, 从1开始", defaultValue = "1")
    private int pageNumber = 1;

    @Schema(description = "每页大小, 默认10条", defaultValue = "10")
    private int pageSize = 10;

    @Schema(description = "按属性排序. 单字段排序eg: ?sort=id|asc 多字段排序eg: ?sort=id|asc,name|desc", nullable = true)
    private List<String> sort;

    @Schema(description = "搜索信息. eg: ?search=field|keyword", nullable = true)
    private String search;
}

/**
 * GwPageRequest模型参数转换
 */
static class GwPageRequestConverter implements ModelConverter {

    /**
     * 待转换的参数类型全限定名
     */
    private static final String TO_BE_REPLACE = "cn.rsmis.gpm.core.page.GwPageRequest";

    /**
     * 替换后的类型
     */
    private static final AnnotatedType REPLACEED_TYPE = new AnnotatedType(GwPageRequestAlternate.class).resolveAsRef(true);

    /**
     * Spring doc 对象映射器提供者的类型。
     */
    private final ObjectMapperProvider springDocObjectMapper;

    /**
     * 构造方法: 实例化一个新的 GwPageRequest openApi 转换器
     *
     * @param springDocObjectMapper the spring doc object mapper
     */
    public GwPageRequestConverter(ObjectMapperProvider springDocObjectMapper) {
        this.springDocObjectMapper = springDocObjectMapper;
    }

    @Override
    public io.swagger.v3.oas.models.media.Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator<ModelConverter> chain) {
        JavaType javaType = springDocObjectMapper.jsonMapper().constructType(type.getType());
        if (javaType != null) {
            Class<?> cls = javaType.getRawClass();
            if (TO_BE_REPLACE.equals(cls.getCanonicalName())) {
                if (!type.isSchemaProperty()) {
                    type = REPLACEED_TYPE;
                } else {
                    type.name(cls.getSimpleName() + StringUtils.capitalize(type.getParent().getType()));
                }
            }
        }
        return (chain.hasNext()) ? chain.next().resolve(type, context, chain) : null;
    }
}
  1. 注册自定义参数的Converter
/**
 * 注册自定义参数的Converter
 *
 * @param objectMapperProvider
 * @return
 */
@Bean
@Lazy(false)
GwPageRequestConverter pageableOpenAPIConverter(ObjectMapperProvider objectMapperProvider) {
    // 将 参数对象 替换为目标类
    SpringDocUtils.getConfig().replaceParameterObjectWithClass(GwPageRequest.class, GwPageRequestAlternate.class);
    return new GwPageRequestConverter(objectMapperProvider);
}
  1. 在controller方法形参声明
/**
 * 分页查询
 *
 * @param pageRequest 分页条件
 * @return 分页对象
 */
@Operation(summary = "分页查询")
@GetMapping("/page")
public ResultBean<GwPageResult<JobEntity>> page(@ParameterObject GwPageRequest pageRequest) {
    return ResultBean.success(iJobService.getPage(pageRequest));
}

注意如果使用了Knife4j等增强框架, 需要添加 @ParameterObject注解, 否则无法正常渲染详见https://gitee.com/xiaoym/knife4j/issues/I6HDXO
最终效果
image.png