引言
原文出处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@Primarypublic 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@Componentpublic 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;@Datapublic 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")@CrossOriginpublic 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到了吗!