使用spring doc代替spring fox,openapi 3.0规范

This commit is contained in:
FrozenWatermelon
2023-02-21 11:45:40 +08:00
parent a906a9c369
commit 53a2d9644b
98 changed files with 788 additions and 1044 deletions

View File

@@ -1,17 +1,29 @@
package com.yami.shop.common.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.yami.shop.common.util.PageParam;
import io.swagger.v3.oas.annotations.Hidden;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springdoc.api.annotations.ParameterObject;
import org.springdoc.core.DelegatingMethodParameter;
import org.springdoc.core.GenericParameterService;
import org.springdoc.core.PropertyResolverUtils;
import org.springdoc.core.SpringDocUtils;
import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer;
import org.springdoc.core.providers.ObjectMapperProvider;
import org.springdoc.core.providers.WebConversionServiceProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
import org.springframework.core.MethodParameter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author FrozenWatermelon
@@ -20,36 +32,68 @@ import java.util.stream.Collectors;
@Configuration
public class Swagger2Config {
static {
SpringDocUtils.getConfig().addAnnotationsToIgnore(JsonIgnore.class);
}
@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {
public GenericParameterService parameterBuilder(PropertyResolverUtils propertyResolverUtils, Optional<WebConversionServiceProvider> optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider) {
return new GenericParameterService(propertyResolverUtils, delegatingMethodParameterCustomizer(),
optionalWebConversionServiceProvider,objectMapperProvider);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
}
return bean;
// 解决@ParameterObject和@Hidden, JsonIgnore同时使用不生效的问题
private Optional<DelegatingMethodParameterCustomizer> delegatingMethodParameterCustomizer() { // NOSONAR
return Optional.of((originalMethodParam, methodParam) -> {
// 这个方法类拥有的注解
Annotation[] annotations = originalMethodParam.getParameterType().getAnnotations();
boolean typeContainParameterObject = false;
if (annotations.length > 0) {
List<? extends Class<? extends Annotation>> annotationTypes = Arrays.stream(annotations).map(Annotation::annotationType).collect(Collectors.toList());
typeContainParameterObject = annotationTypes.contains(ParameterObject.class);
}
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
List<T> copy = mappings.stream()
.filter(mapping -> mapping.getPatternParser() == null)
.collect(Collectors.toList());
mappings.clear();
mappings.addAll(copy);
}
@SuppressWarnings("unchecked")
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
if (typeContainParameterObject
|| (originalMethodParam.hasParameterAnnotations() && originalMethodParam.hasParameterAnnotation(ParameterObject.class))) {
try {
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
field.setAccessible(true);
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
if (isParameterIgnore(originalMethodParam, methodParam)) {
Field field = FieldUtils.getDeclaredField(DelegatingMethodParameter.class, "additionalParameterAnnotations", true);
try {
field.set(methodParam, new Annotation[] {new Hidden() { // NOSONAR
@Override
public Class<? extends Annotation> annotationType() {
return Hidden.class;
}}
});
} catch (IllegalArgumentException|IllegalAccessException e) {
e.printStackTrace();
}
}
} catch (NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
}
};
});
}
private boolean isParameterIgnore(MethodParameter originalMethodParam, MethodParameter methodParam) throws NoSuchFieldException, SecurityException {
String parameterName = StrUtil.isBlank(methodParam.getParameterName())? "":methodParam.getParameterName();
String fieldName = parameterName.indexOf('.') == -1 ? parameterName : parameterName.substring(0, parameterName.indexOf('.'));
// 解决mybatis-plus返回的查询参数污染的问题
if (originalMethodParam.getParameterType().isAssignableFrom(PageParam.class)) {
if ("searchCount".equals(fieldName)) {
return true;
}
}
Field declaredField;
try {
declaredField = originalMethodParam.getParameterType().getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
e.printStackTrace();
declaredField = originalMethodParam.getParameterType().getSuperclass().getDeclaredField(fieldName);
}
return Stream.of(declaredField.getAnnotations())
.filter(annot -> Arrays.asList(Hidden.class, JsonIgnore.class).contains(annot.annotationType())).count() > 0;
}
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
*
* https://www.mall4j.com/
*
* 未经允许,不可做商业用途!
*
* 版权所有,侵权必究!
*/
package com.yami.shop.common.serializer.springfox;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import springfox.documentation.spring.web.json.JacksonModuleRegistrar;
import springfox.documentation.spring.web.json.JsonSerializer;
import java.util.List;
/**
* @author LGH
*/
@Configuration
public class SpringFoxJsonSerializerConfig {
@Bean
@Primary
public JsonSerializer yamiSpringfoxJsonSerializer(List<JacksonModuleRegistrar> moduleRegistrars) {
return new SpringfoxJsonSerializer(moduleRegistrars);
}
}

View File

@@ -1,53 +0,0 @@
/*
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
*
* https://www.mall4j.com/
*
* 未经允许,不可做商业用途!
*
* 版权所有,侵权必究!
*/
package com.yami.shop.common.serializer.springfox;
import io.swagger.models.Operation;
import io.swagger.models.Swagger;
import io.swagger.models.parameters.Parameter;
import springfox.documentation.spring.web.json.JacksonModuleRegistrar;
import springfox.documentation.spring.web.json.Json;
import springfox.documentation.spring.web.json.JsonSerializer;
import java.util.List;
/**
* 自定义Swagger 的序列化去除分页参数中的records值
* @author LGH
*/
public class SpringfoxJsonSerializer extends JsonSerializer {
public SpringfoxJsonSerializer(List<JacksonModuleRegistrar> modules) {
super(modules);
}
@Override
public Json toJson(Object toSerialize) {
if (!(toSerialize instanceof Swagger)) {
return super.toJson(toSerialize);
}
Swagger swagger = (Swagger)toSerialize;
swagger.getPaths().forEach((key, path) ->{
Operation get = path.getGet();
if (get != null) {
List<Parameter> parameters = get.getParameters();
if (parameters != null) {
parameters.removeIf(parameter -> parameter.getName().startsWith("records[0]."));
}
}
});
return super.toJson(swagger);
// return super.toJson(toSerialize);
}
}

View File

@@ -11,42 +11,56 @@
package com.yami.shop.common.util;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiParam;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springdoc.api.annotations.ParameterObject;
import java.util.Collections;
import java.util.List;
@Schema
@ParameterObject
public class PageParam<T> extends Page<T> {
/**
* 查询数据列表
*/
@ApiParam(hidden = true)
private List<T> records;
/**
* 总数
*/
@ApiParam(hidden = true)
private long total = 0;
/**
* 每页显示条数,默认 10
*/
@ApiParam(value = "每页大小默认10",required = false, defaultValue = "10")
@Schema(description = "每页大小,默认10")
private long size = 10;
/**
* 当前页
*/
@ApiParam(value = "当前页默认1",required = false,defaultValue = "1")
@Schema(description = "当前页,默认1")
private long current = 1;
/**
* 查询数据列表
*/
@Hidden
private List<T> records;
/**
* 总数
*/
@Hidden
private long total = 0;
/**
* 是否进行 count 查询
*/
@ApiParam(hidden = true)
@JsonIgnore
private boolean isSearchCount = true;
@JsonIgnore
private String countId;
@JsonIgnore
private Long maxLimit;
@JsonIgnore
private boolean optimizeCountSql;
@Override
@ApiParam(hidden = true)
public List<T> getRecords() {
return this.records;
}
@@ -68,7 +82,7 @@ public class PageParam<T> extends Page<T> {
return this;
}
@ApiParam(hidden = true)
@JsonIgnore
public boolean getSearchCount() {
if (total < 0) {
return false;
@@ -77,7 +91,6 @@ public class PageParam<T> extends Page<T> {
}
@Override
@ApiParam(hidden = true)
public boolean isSearchCount() {
if (total < 0) {
return false;
@@ -98,7 +111,11 @@ public class PageParam<T> extends Page<T> {
@Override
public Page<T> setSize(long size) {
this.size = size;
if (size > 100) {
this.size = 100;
} else {
this.size = size;
}
return this;
}
@@ -112,4 +129,24 @@ public class PageParam<T> extends Page<T> {
this.current = current;
return this;
}
/** @deprecated */
@Deprecated
public String getCountId() {
return this.countId;
}
/** @deprecated */
@Deprecated
public Long getMaxLimit() {
return this.maxLimit;
}
/** @deprecated */
@Deprecated
public boolean isOptimizeCountSql() {
return this.optimizeCountSql;
}
}