說(shuō)明
該改動(dòng)不影響swagger原來(lái)的使用,Object/JsonObject 都可以兼容
Controller
Model
最終結(jié)果
request:
request model:
response:
response model:
代碼實(shí)現(xiàn)
首先根據(jù)官方文檔穗泵,寫(xiě)一個(gè)OperationBuilderPlugin類型的插件养晋,這個(gè)插件用來(lái)讀取接口的參數(shù)
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ParametersReader implements OperationBuilderPlugin {
...
@Override
public void apply(OperationContext context) {
context.operationBuilder().parameters(readParameters(context));
}
private List<Parameter> readParameters(OperationContext context) {
List<Parameter> parameters = Lists.newArrayList();
List<ResolvedMethodParameter> methodParameters = context.getParameters();
//1. 先讀取GlobalString類中我們定義的參數(shù)單元亲怠,用一個(gè)Map來(lái)保存
Map<String, ApiSingleParam> paramMap = new HashMap<>();
Field[] fields = GlobalString.class.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) {
}
}
}
//遍歷controller中的方法
for (ResolvedMethodParameter methodParameter : methodParameters) {
ParameterContext parameterContext = new ParameterContext(methodParameter,
new ParameterBuilder(),
context.getDocumentationContext(),
context.getGenericsNamingStrategy(),
context);
Function<ResolvedType, ? extends ModelReference> factory = createModelRefFactory(parameterContext);
//讀取自定義的注釋類
Optional<ApiJsonObject> annotation = context.findAnnotation(ApiJsonObject.class);
if (annotation.isPresent()) {
//2. 自定義的注釋類里包含參數(shù)列表,我們把它合成一個(gè)請(qǐng)求Model和應(yīng)答Model缘琅,放在ModelCache緩存里面
ModelCache.getInstance().setFactory(factory)
.setParamMap(paramMap)
.addModel(annotation.get());
}
}
return parameters;
}
}
然后重寫(xiě) ApiListingScanner 類,將我們的Model加入到swagger的Model列表中
@Component
@Primary
public class ApiListingPluginsScanner extends ApiListingScanner {
...
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));
}
//加入自己的Model
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;
}
}
這樣request的部分就完成了褒颈,下面是response的實(shí)現(xiàn)
先重寫(xiě) SwaggerResponseMessageReader 類
@Primary
@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 3)//一定要大一點(diǎn)
public class ResponseMessageReader extends SwaggerResponseMessageReader {
private final TypeNameExtractor typeNameExtractor;
private final TypeResolver typeResolver;
public ResponseMessageReader(TypeNameExtractor typeNameExtractor, TypeResolver typeResolver) {
super(typeNameExtractor, typeResolver);
this.typeNameExtractor = typeNameExtractor;
this.typeResolver = typeResolver;
}
@Override
protected Set<ResponseMessage> read(OperationContext context) {
ResolvedType defaultResponse = context.getReturnType();
Optional<ApiOperation> operationAnnotation = context.findAnnotation(ApiOperation.class);
Optional<ResolvedType> operationResponse =
operationAnnotation.transform(resolvedTypeFromOperation(typeResolver, defaultResponse));
Optional<ResponseHeader[]> defaultResponseHeaders = operationAnnotation.transform(responseHeaders());
Map<String, Header> defaultHeaders = newHashMap();
if (defaultResponseHeaders.isPresent()) {
defaultHeaders.putAll(headers(defaultResponseHeaders.get()));
}
List<ApiResponses> allApiResponses = context.findAllAnnotations(ApiResponses.class);
Set<ResponseMessage> responseMessages = newHashSet();
Map<Integer, ApiResponse> seenResponsesByCode = newHashMap();
for (ApiResponses apiResponses : allApiResponses) {
ApiResponse[] apiResponseAnnotations = apiResponses.value();
for (ApiResponse apiResponse : apiResponseAnnotations) {
if (!seenResponsesByCode.containsKey(apiResponse.code())) {
seenResponsesByCode.put(apiResponse.code(), apiResponse);
java.util.Optional<ModelReference> responseModel = java.util.Optional.empty();
java.util.Optional<ResolvedType> type = resolvedType(null, apiResponse);
if (isSuccessful(apiResponse.code())) {
type = java.util.Optional.ofNullable(type.orElseGet(operationResponse::get));
}
if (type.isPresent()) {
//將返回的模型ID修改成自定義的,這里我取@apiResponse中的reference參數(shù)加"-result"組合
responseModel = java.util.Optional.of(new ModelRef(apiResponse.reference()+"-result"));
}
Map<String, Header> headers = newHashMap(defaultHeaders);
headers.putAll(headers(apiResponse.responseHeaders()));
responseMessages.add(new ResponseMessageBuilder()
.code(apiResponse.code())
.message(apiResponse.message())
.responseModel(responseModel.orElse(null))
.headersWithDescription(headers)
.build());
}
}
}
if (operationResponse.isPresent()) {
ModelContext modelContext = returnValue(
context.getGroupName(),
operationResponse.get(),
context.getDocumentationType(),
context.getAlternateTypeProvider(),
context.getGenericsNamingStrategy(),
context.getIgnorableParameterTypes());
ResolvedType resolvedType = context.alternateFor(operationResponse.get());
ModelReference responseModel = modelRefFactory(modelContext, typeNameExtractor).apply(resolvedType);
context.operationBuilder().responseModel(responseModel);
ResponseMessage defaultMessage = new ResponseMessageBuilder()
.code(httpStatusCode(context))
.message(message(context))
.responseModel(responseModel)
.build();
if (!responseMessages.contains(defaultMessage) && !"void".equals(responseModel.getType())) {
responseMessages.add(defaultMessage);
}
}
return responseMessages;
}
...
}
ModelCache生成Model
public ModelCache addModel(ApiJsonObject jsonObj) {
String modelName =jsonObj.name();
knownModels.put(modelName,
new Model(modelName,
modelName,
new TypeResolver().resolve(String.class),
"xin.bee.model.entity.BusinessUser",
toPropertyMap(jsonObj.value()),
"POST參數(shù)",
"",
"",
newArrayList(), null, null
));
String resultName = jsonObj.name() + "-" + "result";
knownModels.put(resultName,
new Model(resultName,
resultName,
new TypeResolver().resolve(String.class),
"xin.bee.model.entity.BusinessUser",
toResultMap(jsonObj.result(), resultName),
"返回模型",
"",
"",
newArrayList(), null, null
));
return ModelCacheSub.instance;
}
ModelProperty的制作
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();
}
這樣就搞定了M堑邸C着觥!
Map
map的話比較簡(jiǎn)單购城,參考這篇
https://blog.csdn.net/hellopeng1/article/details/82227942
git:https://github.com/cyjishuang/swagger-mode
jar:
build.gradle:
allprojects {
repositories {
//maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
maven{ url "https://oss.sonatype.org/content/groups/staging"}
//mavenCentral()
}
}
dependencies {
compile group: 'io.github.cyjishuang' ,name: 'swagger-mode', version: '1.0'
}
spring-context.xml 添加掃描
<context:component-scan base-package="io.github.cyjishuang"/>
在創(chuàng)建文檔的時(shí)候設(shè)置存放參數(shù)的類ModelCache.getInstance().setParamClass(XXX.class);
public Docket createRestApi() {
ModelCache.getInstance().setParamClass(GlobalString.class);
return new Docket(DocumentationType.SWAGGER_2)...
這樣配置就完成了吕座,后面只需要對(duì)Controller和參數(shù)的類添加說(shuō)明即可,示例
@RequestMapping(value = "/api/v1/manager")
@RestController
@Api(description = "管理員身份接口")
public class ManagerController {
@ApiOperation(value = "管理員-預(yù)判是否存在",notes ="預(yù)判管理員是否存在" )
@ApiJsonObject(name = "manager-checkManager", value = {
@ApiJsonProperty(name = JSON_USER_NAME),
@ApiJsonProperty(name = JSON_USER_EMAIL)},
result = @ApiJsonResult({}))
@ApiImplicitParam(name = "params", required = true, dataType = "manager-checkManager")
@ApiResponses({@ApiResponse(code = 200, message = "OK", reference = "manager-checkManager")})
@RequestMapping(value = "/checkManager", method = RequestMethod.POST, consumes = MSG_FORMAT_JSON_UTF8, produces = MSG_FORMAT_JSON_UTF8)
public String checkManager(@RequestBody String params) {
return new ControllerCallBack()
.addCommonService(managerService::checkUser)
.build(params);
}
}
public class GlobalString {
@ApiSingleParam(value = "用戶姓名", example = "test1")
public static final String JSON_USER_NAME = "userName";
@ApiSingleParam(value = "用戶郵箱", example = "17721026877@qq.com")
public static final String JSON_USER_EMAIL = "userEmail";
@ApiSingleParam(value = "錯(cuò)誤碼", example = "0", type = Integer.class)
public static final String JSON_ERROR_CODE = "errorCode";
@ApiSingleParam(value = "錯(cuò)誤信息", example = "OK")
public static final String JSON_ERROR_MSG = "errorMsg";
}