共计 30747 个字符,预计需要花费 77 分钟才能阅读完成。
引言
原文出处 Swagger2 对于 JSONObject 参数在 API 文档中展现具体参数以及参数阐明
相似的 Swagger2 对于 Map 参数在 API 文档中展现具体参数以及参数阐明
原理就不再做阐明,读者能够查看该文章即可。作者稍作了些扭转,也是花了很多工夫整顿,接下来咱们只负责搬砖就好,话不多说间接看代码。
代码实现
自定义注解类 ApiJsonObject2
package com.mybatis.plus.swagger;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author wulongbo
* @Date 2020/9/14 17:20
* @Version 1.0
*/
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonObject2 {ApiJsonProperty2[] value(); // 对象属性值
ApiJsonResult result() default @ApiJsonResult({});
String name() default "";}
自定义注解类ApiJsonProperty2
package com.mybatis.plus.swagger;
import io.swagger.annotations.Example;
import io.swagger.annotations.ExampleProperty;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author wulongbo
* @Date 2020/9/14 17:23
* @Version 1.0
*/
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonProperty2 {String name();
String defaultValue() default "";
String description() default "";
String allowableValues() default "";
boolean required() default false;
String access() default "";
boolean allowMultiple() default false;
Class<?> type() default String.class ;
String paramType() default "";
String example() default "";
Example examples() default @Example(value = @ExampleProperty(mediaType = "", value =""));
String format() default "";
boolean readOnly() default false;
String collectionFormat() default "";}
自定义注解类 ApiJsonResult
package com.mybatis.plus.swagger;
/**
* Created by yueh on 2018/9/7.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonResult {String[] value();
String name() default "";
String type() default CommonData.RESULT_TYPE_NORMAL_FINAL;}
ApiListingJsonScanner
package com.mybatis.plus.swagger;
/**
* Created by yueh on 2018/9/12. */
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.*;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import springfox.documentation.PathProvider;
import springfox.documentation.builders.ApiListingBuilder;
import springfox.documentation.schema.Model;
import springfox.documentation.service.*;
import springfox.documentation.spi.service.contexts.ApiListingContext;
import springfox.documentation.spi.service.contexts.DocumentationContext;
import springfox.documentation.spi.service.contexts.RequestMappingContext;
import springfox.documentation.spring.web.paths.PathMappingAdjuster;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.spring.web.plugins.DocumentationPluginsManager;
import springfox.documentation.spring.web.scanners.ApiDescriptionReader;
import springfox.documentation.spring.web.scanners.ApiListingScanner;
import springfox.documentation.spring.web.scanners.ApiListingScanningContext;
import springfox.documentation.spring.web.scanners.ApiModelReader;
import java.util.*;
import static com.google.common.base.Predicates.and;
import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newHashSet;
import static java.util.stream.Collectors.toList;
import static springfox.documentation.builders.BuilderDefaults.nullToEmptyList;
import static springfox.documentation.spi.service.contexts.Orderings.methodComparator;
import static springfox.documentation.spi.service.contexts.Orderings.resourceGroupComparator;
@Component
@Primary
public class ApiListingJsonScanner extends ApiListingScanner {
private final ApiDescriptionReader apiDescriptionReader;
private final ApiModelReader apiModelReader;
private final DocumentationPluginsManager pluginsManager;
public static final String ROOT = "/";
@Autowired
public ApiListingJsonScanner(
ApiDescriptionReader apiDescriptionReader,
ApiModelReader apiModelReader,
DocumentationPluginsManager pluginsManager) {super(apiDescriptionReader,apiModelReader,pluginsManager);
this.apiDescriptionReader = apiDescriptionReader;
this.apiModelReader = apiModelReader;
this.pluginsManager = pluginsManager;
}
static Optional<String> longestCommonPath(List<ApiDescription> apiDescriptions) {List<String> commons = newArrayList();
if (null == apiDescriptions || apiDescriptions.isEmpty()) {return Optional.absent();
}
List<String> firstWords = urlParts(apiDescriptions.get(0));
for (int position = 0; position < firstWords.size(); position++) {String word = firstWords.get(position);
boolean allContain = true;
for (int i = 1; i < apiDescriptions.size(); i++) {List<String> words = urlParts(apiDescriptions.get(i));
if (words.size() < position + 1 || !words.get(position).equals(word)) {
allContain = false;
break; }
}
if (allContain) {commons.add(word);
}
}
Joiner joiner = Joiner.on("/").skipNulls();
return Optional.of("/" + joiner.join(commons));
}
static List<String> urlParts(ApiDescription apiDescription) {return Splitter.on('/')
.omitEmptyStrings()
.trimResults()
.splitToList(apiDescription.getPath());
}
public Multimap<String, ApiListing> scan(ApiListingScanningContext context) {final Multimap<String, ApiListing> apiListingMap = LinkedListMultimap.create();
int position = 0;
Map<ResourceGroup, List<RequestMappingContext>> requestMappingsByResourceGroup
= context.getRequestMappingsByResourceGroup();
Collection<ApiDescription> additionalListings = pluginsManager.additionalListings(context);
Set<ResourceGroup> allResourceGroups = FluentIterable.from(collectResourceGroups(additionalListings))
.append(requestMappingsByResourceGroup.keySet())
.toSet();
List<SecurityReference> securityReferences = newArrayList();
for (final ResourceGroup resourceGroup : sortedByName(allResourceGroups)) {DocumentationContext documentationContext = context.getDocumentationContext();
Set<String> produces = new LinkedHashSet<String>(documentationContext.getProduces());
Set<String> consumes = new LinkedHashSet<String>(documentationContext.getConsumes());
String host = documentationContext.getHost();
Set<String> protocols = new LinkedHashSet<String>(documentationContext.getProtocols());
Set<ApiDescription> apiDescriptions = newHashSet();
Map<String, Model> models = new LinkedHashMap<String, Model>();
List<RequestMappingContext> requestMappings = nullToEmptyList(requestMappingsByResourceGroup.get(resourceGroup));
for (RequestMappingContext each : sortedByMethods(requestMappings)) {//url
Map<String, Model> knownModels = new HashMap<>();
models.putAll(apiModelReader.read(each.withKnownModels(models)));
apiDescriptions.addAll(apiDescriptionReader.read(each));
}
//
models.putAll(ModelCache.getInstance().getKnownModels());
List<ApiDescription> additional = from(additionalListings)
.filter(and(belongsTo(resourceGroup.getGroupName()),
onlySelectedApis(documentationContext)))
.toList();
apiDescriptions.addAll(additional);
List<ApiDescription> sortedApis = FluentIterable.from(apiDescriptions)
.toSortedList(documentationContext.getApiDescriptionOrdering());
Optional<String> o = longestCommonPath(sortedApis);
String resourcePath = new ResourcePathProvider(resourceGroup)
.resourcePath()
.or(o)
.orNull();
PathProvider pathProvider = documentationContext.getPathProvider();
String basePath = pathProvider.getApplicationBasePath();
PathAdjuster adjuster = new PathMappingAdjuster(documentationContext);
ApiListingBuilder apiListingBuilder = new ApiListingBuilder(context.apiDescriptionOrdering())
.apiVersion(documentationContext.getApiInfo().getVersion())
.basePath(adjuster.adjustedPath(basePath))
.resourcePath(resourcePath)
.produces(produces)
.consumes(consumes)
.host(host)
.protocols(protocols)
.securityReferences(securityReferences)
.apis(sortedApis)
.models(models)
.position(position++)
.availableTags(documentationContext.getTags());
ApiListingContext apiListingContext = new ApiListingContext(context.getDocumentationType(),
resourceGroup,
apiListingBuilder);
apiListingMap.put(resourceGroup.getGroupName(), pluginsManager.apiListing(apiListingContext));
}
return apiListingMap;
}
private Predicate<ApiDescription> onlySelectedApis(final DocumentationContext context) {return new Predicate<ApiDescription>() {
@Override
public boolean apply(ApiDescription input) {return context.getApiSelector().getPathSelector().apply(input.getPath());
}
};
}
private Iterable<RequestMappingContext> sortedByMethods(List<RequestMappingContext> contexts) {return contexts.stream().sorted(methodComparator()).collect(toList());
}
static Iterable<ResourceGroup> collectResourceGroups(Collection<ApiDescription> apiDescriptions) {return from(apiDescriptions)
.transform(toResourceGroups());
}
static Iterable<ResourceGroup> sortedByName(Set<ResourceGroup> resourceGroups) {return from(resourceGroups).toSortedList(resourceGroupComparator());
}
static Predicate<ApiDescription> belongsTo(final String groupName) {return new Predicate<ApiDescription>() {
@Override
public boolean apply(ApiDescription input) {return !input.getGroupName().isPresent()
|| groupName.equals(input.getGroupName().get());
}
};
}
private static Function<ApiDescription, ResourceGroup> toResourceGroups() {return new Function<ApiDescription, ResourceGroup>() {
@Override
public ResourceGroup apply(ApiDescription input) {
return new ResourceGroup(input.getGroupName().or(Docket.DEFAULT_GROUP_NAME),
null);
}
};
}
class ResourcePathProvider {
private final ResourceGroup resourceGroup;
ResourcePathProvider(ResourceGroup resourceGroup) {this.resourceGroup = resourceGroup;}
public Optional<String> resourcePath() {
return Optional.fromNullable(Strings.emptyToNull(controllerClass()
.transform(resourcePathExtractor())
.or("")));
}
private Function<Class<?>, String> resourcePathExtractor() {return new Function<Class<?>, String>() {
@Override
public String apply(Class<?> input) {String path = Iterables.getFirst(Arrays.asList(paths(input)), "");
if (Strings.isNullOrEmpty(path)) {return "";}
if (path.startsWith("/")) {return path;}
return "/" + path;
}
};
}
@VisibleForTesting
String[] paths(Class<?> controller) {
RequestMapping annotation
= AnnotationUtils.findAnnotation(controller, RequestMapping.class);
if (annotation != null) {return annotation.path();
}
return new String[]{};
}
private Optional<? extends Class<?>> controllerClass() {return resourceGroup.getControllerClass();
}
}
}
自定义注解类 ApiSingleParam
package com.mybatis.plus.swagger;
/**
* Created by yueh on 2018/9/7.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiSingleParam {Sring name() default "";
String value() default "";
Class<?> type() default String.class;
String example() default "";
boolean allowMultiple() default false;}
CommonData
package com.mybatis.plus.swagger;
/**
* Created by yueh on 2018/9/18.
*/
public class CommonData {
private static String RESULT_TYPE_NORMAL = "normal";
private static String RESULT_TYPE_PAGE = "page";
private static String RESULT_TYPE_LIST = "list";
private static String RESULT_TYPE_OTHER = "other";
private static String JSON_ERROR_CODE = "errorCode";
private static String JSON_ERROR_MSG = "errorMsg";
private static String JSON_START_PAGE_NUM = "startPageNum";
private static String JSON_PAGE_SIZE = "pageSize";
private static String JSON_PAGE_COUNT = "pageCount";
private static String JSON_TOTAL_COUNT = "totalCount";
public static final String RESULT_TYPE_NORMAL_FINAL = "normal";
public static String getResultTypeNormal() {return RESULT_TYPE_NORMAL;}
public static void setResultTypeNormal(String resultTypeNormal) {RESULT_TYPE_NORMAL = resultTypeNormal;}
public static String getResultTypePage() {return RESULT_TYPE_PAGE;}
public static void setResultTypePage(String resultTypePage) {RESULT_TYPE_PAGE = resultTypePage;}
public static String getResultTypeList() {return RESULT_TYPE_LIST;}
public static void setResultTypeList(String resultTypeList) {RESULT_TYPE_LIST = resultTypeList;}
public static String getResultTypeOther() {return RESULT_TYPE_OTHER;}
public static void setResultTypeOther(String resultTypeOther) {RESULT_TYPE_OTHER = resultTypeOther;}
public static String getJsonErrorCode() {return JSON_ERROR_CODE;}
public static void setJsonErrorCode(String jsonErrorCode) {JSON_ERROR_CODE = jsonErrorCode;}
public static String getJsonErrorMsg() {return JSON_ERROR_MSG;}
public static void setJsonErrorMsg(String jsonErrorMsg) {JSON_ERROR_MSG = jsonErrorMsg;}
public static String getJsonStartPageNum() {return JSON_START_PAGE_NUM;}
public static void setJsonStartPageNum(String jsonStartPageNum) {JSON_START_PAGE_NUM = jsonStartPageNum;}
public static String getJsonPageSize() {return JSON_PAGE_SIZE;}
public static void setJsonPageSize(String jsonPageSize) {JSON_PAGE_SIZE = jsonPageSize;}
public static String getJsonPageCount() {return JSON_PAGE_COUNT;}
public static void setJsonPageCount(String jsonPageCount) {JSON_PAGE_COUNT = jsonPageCount;}
public static String getJsonTotalCount() {return JSON_TOTAL_COUNT;}
public static void setJsonTotalCount(String jsonTotalCount) {JSON_TOTAL_COUNT = jsonTotalCount;}
}
全局字符串 GlobalString
package com.mybatis.plus.swagger;
/**
* @Author wulongbo
* @Date 2020/9/14 17:34
* @Version 1.0
*/
public class GlobalString {@ApiSingleParam(value = "姓名", example = "isWulongbo")
public static final String JSON_NAME = "name";
@ApiSingleParam(value = "年龄", example = "25")
public static final String JSON_AGE = "age";
@ApiSingleParam(value = "邮箱", example = "1191935532@qq.com")
public static final String JSON_EMAIL = "email";
@ApiSingleParam(value = "错误码", example = "0", type = Integer.class)
public static final String JSON_ERROR_CODE = "errorCode";
@ApiSingleParam(value = "错误信息", example = "OK")
public static final String JSON_ERROR_MSG = "errorMsg";
}
ModelCache
package com.mybatis.plus.swagger;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Function;
import org.springframework.plugin.core.OrderAwarePluginRegistry;
import org.springframework.plugin.core.PluginRegistry;
import springfox.documentation.schema.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.TypeNameProviderPlugin;
import springfox.documentation.spi.schema.contexts.ModelContext;
import springfox.documentation.spi.service.contexts.DocumentationContext;
import java.lang.reflect.Field;
import java.util.*;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.Lists.newArrayList;
import static com.mybatis.plus.swagger.CommonData.*;
import static org.springframework.util.ObjectUtils.isEmpty;
import static springfox.documentation.schema.Collections.collectionElementType;
import static springfox.documentation.spi.schema.contexts.ModelContext.inputParam;
/**
* Created by yueh on 2018/9/13. */public class ModelCache {private Map<String, Model> knownModels = new HashMap<>();
private DocumentationContext context;
private Function<ResolvedType, ? extends ModelReference> factory;
private TypeResolver typeResolver = new TypeResolver();
private Map<String, ApiSingleParam> paramMap = new HashMap<>();
private Class<?> cls;
private ModelCache() {}
public static ModelCache getInstance() {return ModelCacheSub.instance;}
public ModelCache setParamMap(Map<String, ApiSingleParam> paramMap) {
this.paramMap = paramMap;
return getInstance();}
public ModelCache setParamClass(Class<?> cls) {
this.cls = cls;
return getInstance();}
public Class<?> getParamClass() {return cls;}
public ModelCache setFactory(Function<ResolvedType, ? extends ModelReference> factory) {
this.factory = factory;
return getInstance();}
public void setContext(DocumentationContext context) {this.context = context;}
public DocumentationContext getContext() {return context;}
public Map<String, Model> getKnownModels() {return knownModels;}
public ModelCache addModel(ApiJsonObject2 jsonObj) {String modelName = jsonObj.name();
knownModels.put(modelName,
new Model(modelName,
modelName,
new TypeResolver().resolve(String.class),
"com.mybatis.plus.swagger.CommonData",
toPropertyMap(jsonObj.value()),
"POST 参数",
"","",
newArrayList(), null, null));
String resultName = jsonObj.name() + "-" + "result";
knownModels.put(resultName,
new Model(resultName,
resultName,
new TypeResolver().resolve(String.class),
"com.mybatis.plus.swagger.CommonData",
toResultMap(jsonObj.result(), resultName),
"返回模型",
"","",
newArrayList(), null, null));
return ModelCacheSub.instance;
}
public Map<String, ModelProperty> toResultMap(ApiJsonResult jsonResult, String groupName) {// System.out.println("--- toResultMap ---");
List<String> values = Arrays.asList(jsonResult.value());
List<String> outer = new ArrayList<>();
if (!getResultTypeOther().equals(jsonResult.type())) {outer.add(getJsonErrorCode());
outer.add(getJsonErrorMsg());
if (!getResultTypeNormal().equals(jsonResult.type())) {
//model
String subModelName = groupName + "-" + jsonResult.name();
knownModels.put(subModelName,
new Model(subModelName,
subModelName,
new TypeResolver().resolve(String.class),
"com.mybatis.plus.swagger.CommonData",
transResultMap(values),
"返回模型",
"","",
newArrayList(), null, null));
//prop
Map<String, ModelProperty> propertyMap = new HashMap<>();
// outer.add(jsonResult.name());
ResolvedType type = new TypeResolver().resolve(List.class);
ModelProperty mp = new ModelProperty(jsonResult.name(),
type,
"",
0,
false, false, true, false, "",
null, "",
null, "",
null, newArrayList());// new AllowableRangeValues("1", "2000"),//.allowableValues(new AllowableListValues(["ABC", "ONE", "TWO"], "string"))
mp.updateModelRef(getModelRef());
ResolvedType collectionElementType = collectionElementType(type);
try {Field f = ModelProperty.class.getDeclaredField("modelRef");
f.setAccessible(true);
f.set(mp, new ModelRef("List", new ModelRef(subModelName)));
} catch (Exception e) {e.printStackTrace();
}
propertyMap.put(jsonResult.name(), mp);
if (getResultTypePage().equals(jsonResult.type())) {outer.add(getJsonStartPageNum());
outer.add(getJsonPageSize());
outer.add(getJsonTotalCount());
}
propertyMap.putAll(transResultMap(outer));
return propertyMap;
}
outer.addAll(values);
return transResultMap(outer);
}
return transResultMap(values);
}
public Map<String, ModelProperty> transResultMap(List<String> values) {Map<String, ModelProperty> propertyMap = new HashMap<>();
for (String resultName : values) {ApiSingleParam param = paramMap.get(resultName);
if (isEmpty(param)) {continue;}
Class<?> type = param.type();
if (!isEmpty(param)) {type = param.type();
} else if (isEmpty(type)) {type = String.class;}
boolean allowMultiple = param.allowMultiple();
if (!isEmpty(param)) {allowMultiple = param.allowMultiple();
}
ResolvedType resolvedType = null;
if (allowMultiple) {resolvedType = new TypeResolver().resolve(List.class, type);
} else {resolvedType = new TypeResolver().resolve(type);
}
ModelProperty mp = new ModelProperty(
resultName,
resolvedType,
param.type().getName(),
0,
false, false, true, false, param.value(),
null, param.example(),
null, "",
null, newArrayList());// new AllowableRangeValues("1", "2000"),//.allowableValues(new AllowableListValues(["ABC", "ONE", "TWO"], "string"))
mp.updateModelRef(getModelRef());
propertyMap.put(resultName, mp);
}
return propertyMap;
}
public Map<String, ModelProperty> toPropertyMap(ApiJsonProperty2[] jsonProp) {// System.out.println("--- toPropertyMap ---");
Map<String, ModelProperty> propertyMap = new HashMap<String, ModelProperty>();
for (ApiJsonProperty2 property : jsonProp) {String propertyName = property.name();
ApiSingleParam param = paramMap.get(propertyName);
String description = property.description();
if (isNullOrEmpty(description) && !isEmpty(param)) {description = param.value();
}
String example = property.description();
if (isNullOrEmpty(example) && !isEmpty(param)) {example = param.example();
}
Class<?> type = property.type();
if (!isEmpty(param)) {type = param.type();
} else if (isEmpty(type)) {type = String.class;}
boolean allowMultiple = property.allowMultiple();
if (!isEmpty(param)) {allowMultiple = param.allowMultiple();
}
ResolvedType resolvedType = null;
if (allowMultiple) {resolvedType = new TypeResolver().resolve(List.class, type);
} else {resolvedType = new TypeResolver().resolve(type);
}
// System.out.println("----- example:" + example);
// System.out.println("----- description:" + description);
ModelProperty mp = new ModelProperty(
propertyName,
resolvedType,
type.toString(),
0,
property.required(),
false, property.readOnly(),
null, description,
null, example,
null, property.defaultValue(),
null, newArrayList());// new AllowableRangeValues("1", "2000"),//.allowableValues(new AllowableListValues(["ABC", "ONE", "TWO"], "string"))
mp.updateModelRef(getModelRef());
propertyMap.put(property.name(), mp);
}
return propertyMap;
}
private static class ModelCacheSub {private static ModelCache instance = new ModelCache();
}
private Function<ResolvedType, ? extends ModelReference> getModelRef() {Function<ResolvedType, ? extends ModelReference> factory = getFactory();
// ModelReference stringModel = factory.apply(typeResolver.resolve(List.class, String.class));
return factory;
}
public Function<ResolvedType, ? extends ModelReference> getFactory() {if (factory == null) {List<DefaultTypeNameProvider> providers = newArrayList();
providers.add(new DefaultTypeNameProvider());
PluginRegistry<TypeNameProviderPlugin, DocumentationType> modelNameRegistry =
OrderAwarePluginRegistry.create(providers);
TypeNameExtractor typeNameExtractor = new TypeNameExtractor(
typeResolver,
modelNameRegistry,
new JacksonEnumTypeDeterminer());
ModelContext modelContext = inputParam(context.getGroupName(),
String.class,
context.getDocumentationType(),
context.getAlternateTypeProvider(),
context.getGenericsNamingStrategy(),
context.getIgnorableParameterTypes());
factory = ResolvedTypes.modelRefFactory(modelContext, typeNameExtractor);
}
return factory;
}
}
留神:在 ModelCache 类中 CommonData 的门路肯定要改为你实在的我的项目门路
ParametersReader
package com.mybatis.plus.swagger;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.schema.ModelReference;
import springfox.documentation.schema.ResolvedTypes;
import springfox.documentation.schema.TypeNameExtractor;
import springfox.documentation.service.Parameter;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.contexts.ModelContext;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.spi.service.contexts.ParameterContext;
import springfox.documentation.spring.web.DescriptionResolver;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static springfox.documentation.spi.schema.contexts.ModelContext.inputParam;
import static springfox.documentation.swagger.common.SwaggerPluginSupport.pluginDoesApply;
/**
* Created by yueh on 2018/9/10.
*/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ParametersReader implements OperationBuilderPlugin {
private final DescriptionResolver descriptions;
private final TypeNameExtractor nameExtractor;
private final TypeResolver resolver;
@Autowired
public ParametersReader(DescriptionResolver descriptions, TypeNameExtractor nameExtractor, TypeResolver resolver) {
this.nameExtractor = nameExtractor;
this.resolver = resolver;
this.descriptions = descriptions;
}
@Override
public void apply(OperationContext context) {context.operationBuilder().parameters(readParameters(context));
}
@Override
public boolean supports(DocumentationType delimiter) {return pluginDoesApply(delimiter);
}
private List<Parameter> readParameters(OperationContext context) {List<Parameter> parameters = Lists.newArrayList();
List<ResolvedMethodParameter> methodParameters = context.getParameters();
Map<String, ApiSingleParam> paramMap = new HashMap<>();
Field[] fields = ModelCache.getInstance().getParamClass().getDeclaredFields();
String type = new String();
for (Field field : fields) {if (field.isAnnotationPresent(ApiSingleParam.class)) {ApiSingleParam param = field.getAnnotation(ApiSingleParam.class);
try {String name = (String) field.get(type);
paramMap.put(name, param);
} catch (Exception e) {}}
}
for (ResolvedMethodParameter methodParameter : methodParameters) {
ParameterContext parameterContext = new ParameterContext(methodParameter,
new ParameterBuilder(),
context.getDocumentationContext(),
context.getGenericsNamingStrategy(),
context);
Function<ResolvedType, ? extends ModelReference> factory = createModelRefFactory(parameterContext);
Optional<ApiJsonObject2> annotation = context.findAnnotation(ApiJsonObject2.class);
if (annotation.isPresent()) {ModelCache.getInstance().setFactory(factory)
.setParamMap(paramMap)
.addModel(annotation.get());
}
}
return parameters;
}
private Function<ResolvedType, ? extends ModelReference> createModelRefFactory(ParameterContext context) {
ModelContext modelContext = inputParam(context.getGroupName(),
context.resolvedMethodParameter().getParameterType(),
context.getDocumentationType(),
context.getAlternateTypeProvider(),
context.getGenericNamingStrategy(),
context.getIgnorableParameterTypes());
return ResolvedTypes.modelRefFactory(modelContext, nameExtractor);
}
}
OK!贴完代码后的目录构造如下:
因为咱们之前 mybatis-plus
我的项目没有引入 json 依赖
在 pom.xml
文件引入fastjson
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
封装返回值
新建一个 base
文件夹,贴入罕用的封装类BaseApiService
package com.mybatis.plus.base;
import lombok.Data;
import org.springframework.stereotype.Component;
@Data
@Component
public class BaseApiService<T> {public BaseResponse<T> setResultError(Integer code, String msg) {return setResult(code, msg, null);
}
// 返回谬误,能够传 msg
public BaseResponse<T> setResultError(String msg) {return setResult(PageCodeEnum.HTTP_RES_500.getCode(), msg, null);
}
// 返回胜利,能够传 data 值
public BaseResponse<T> setResultSuccess(T data) {return setResult(PageCodeEnum.HTTP_RES_200.getCode(), PageCodeEnum.HTTP_RES_200.getMsg(), data);
}
// 返回胜利,沒有 data 值
public BaseResponse<T> setResultSuccess() {return setResult(PageCodeEnum.HTTP_RES_200.getCode(), PageCodeEnum.HTTP_RES_200.getMsg(), null);
}
// 返回胜利,沒有 data 值
public BaseResponse<T> setResultSuccess(String msg) {return setResult(PageCodeEnum.HTTP_RES_200.getCode(), msg, null);
}
// 通用封装
public BaseResponse<T> setResult(Integer code, String msg, T data) {return new BaseResponse<T>(code, msg, data);
}
// 调用数据库封装
public Boolean toDaoResult(int result){return result>0?true:false;}
}
BaseResponse
package com.mybatis.plus.base;
import lombok.Data;
@Data
public class BaseResponse<T> {
/**
* 返回码
*/
private Integer code;
/**
* 音讯(最好用简写,不占用宽带)
*/ private String msg;
/**
* 数据
*/
private T data;
public BaseResponse() {}
public BaseResponse(Integer code, String msg, T data) {super();
this.code = code;
this.msg = msg;
this.data = data;
}
/**
* 未登录返回后果
*/
public static <T> BaseResponse<T> unauthorized(T data) {return new BaseResponse<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);
}
/**
* 未受权返回后果
*/
public static <T> BaseResponse<T> forbidden(T data) {return new BaseResponse<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);
}
}
IErrorCode
package com.mybatis.plus.base;
/**
* 封装 API 的错误码
*/
public interface IErrorCode {Integer getCode();
String getMessage();}
PageCodeEnum
package com.mybatis.plus.base;
/**
* @Author wulongbo
* @Date 2020/9/14 15:38
* @Version 1.0
*/public enum PageCodeEnum {
// 公共
HTTP_RES_200(200,"success"),
HTTP_RES_500(500,"fail"),
SUCCESS(200, "胜利"),
UNKNOWN_ERROR(9998,"未知异样"),
SYSTEM_ERROR(9999, "零碎异样"),
INSUFFICIENT_PERMISSION(4003, "权限有余"),
WARN(9000, "失败"),
REQUEST_PARAMETER_ERROR(1002, "申请参数谬误"),
// 分页
PAGE_CURRENTPAGE_EMPTY(5000, "以后页码不能为空!"),
PAGE_PAGESIZE_EMPTY(5001, "每页显示的条数不能为空!");
private Integer code;
private String msg;
// 构造方法,留神:构造方法不能为 public,因为 enum 并不能够被实例化
private PageCodeEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
// 不须要定义 set 办法,避免批改
public Integer getCode() {return code;}
public String getMsg() {return msg;}
}
ResultCode
package com.mybatis.plus.base;
/**
* 枚举了一些罕用 API 操作码
*/
public enum ResultCode implements IErrorCode {SUCCESS(200, "操作胜利"),
FAILED(500, "操作失败"),
VALIDATE_FAILED(404, "参数检验失败"),
UNAUTHORIZED(401, "暂未登录或 token 曾经过期"),
FORBIDDEN(403, "没有相干权限");
private Integer code;
private String message;
private ResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {return code;}
public String getMessage() {return message;}
}
OK!目录构造如下:
当初咱们在 UserController
中退出咱们自定义的注解
package com.mybatis.plus.controller;
import com.alibaba.fastjson.JSONObject;
import com.mybatis.plus.base.BaseApiService;
import com.mybatis.plus.base.BaseResponse;
import com.mybatis.plus.base.PageCodeEnum;
import com.mybatis.plus.entity.User;
import com.mybatis.plus.service.IUserService;
import com.mybatis.plus.swagger.ApiJsonObject2;
import com.mybatis.plus.swagger.ApiJsonProperty2;
import com.mybatis.plus.swagger.ApiJsonResult;
import com.mybatis.plus.swagger.GlobalString;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* <p>
* 前端控制器
* </p>
*
* @author wulongbo
* @since 2020-11-09
*/
@RestController
@RequestMapping("/user")
@CrossOrigin
public class UserController extends BaseApiService {
@Autowired
private IUserService iUserService;
@ApiOperation(value="增加用户信息", notes="增加用户信息")
@ApiImplicitParams({@ApiImplicitParam(paramType = "query", name = "name", dataType = "String", required = true, value = "姓名"),
@ApiImplicitParam(paramType = "query", name = "age", dataType = "int", required = true, value = "年龄"),
@ApiImplicitParam(paramType = "query", name = "email", dataType = "String", required = true, value = "邮箱") })
@PostMapping("/addUser")
public boolean addUser(String name,Integer age,String email) {
try {User user=new User();
user.setName(name);
user.setAge(age);
user.setEmail(email);
iUserService.recordJob(user);
}catch (Exception e){return false;}
return true;
}
@ApiOperation(value="删除用户信息", notes="删除用户信息")
@ApiImplicitParams({@ApiImplicitParam(paramType = "query", name = "id", dataType = "long", required = true, value = "用户 id")})
@PostMapping("/delUser")
public boolean delUser(Long id) {
try {User user=new User();
user.setId(id);
iUserService.delJob(user);
}catch (Exception e){return false;}
return true;
}
/**
* JSONObject 参数增加用户信息
* @param json
* @return
*/
@ApiOperation(value="JSONObject 参数增加用户信息", notes="JSONObject 参数增加用户信息")
@ApiJsonObject2(name = "addUserByJson", value = {@ApiJsonProperty2(name = GlobalString.JSON_NAME),
@ApiJsonProperty2(name = GlobalString.JSON_AGE),
@ApiJsonProperty2(name = GlobalString.JSON_EMAIL)},
result = @ApiJsonResult({}))
@ApiImplicitParam(name = "json", required = true, dataType = "addUserByJson")
@ApiResponses({@ApiResponse(code = 200, message = "OK", reference = "addUserByJson")})
@RequestMapping(value = "/addUserByJson", method = RequestMethod.POST, consumes ="application/json", produces = "application/json")
public BaseResponse<JSONObject> addUserByJson(@RequestBody String json){JSONObject jsonObject=JSONObject.parseObject(json);
String name=jsonObject.getString("name"); // 姓名
Integer age=jsonObject.getInteger("age");// 年龄
String email=jsonObject.getString("email");// 邮箱
try {User user=new User();
user.setName(name);
user.setAge(age);
user.setEmail(email);
iUserService.recordJob(user);
}catch (Exception e){return setResultError(PageCodeEnum.HTTP_RES_500.getMsg());
}
return setResultSuccess(PageCodeEnum.HTTP_RES_200);
}
}
启动 MybatisPlusApplication
并拜访 http://localhost:8080/swagger-ui.html
好!接下来咱们看看 swagger 文档会有什么不同!GlobalString
中的参数主动读取到了 swagger
中,咱们间接执行接口拜访:
并查看数据库是否有新增记录:
完满!至此咱们 swagger 再也不必手填参数数值了,间接丢给前端,你 get 到了吗!