swagger-mode

swagger json request&response

License

License

Categories

Categories

Swagger Program Interface REST Frameworks
GroupId

GroupId

io.github.cyjishuang
ArtifactId

ArtifactId

swagger-mode
Last Version

Last Version

1.0
Release Date

Release Date

Type

Type

jar
Description

Description

swagger-mode
swagger json request&response
Project URL

Project URL

https://github.com/cyjishuang
Source Code Management

Source Code Management

https://github.com/cyjishuang/swagger-mode.git

Download swagger-mode

How to add to project

<!-- https://jarcasting.com/artifacts/io.github.cyjishuang/swagger-mode/ -->
<dependency>
    <groupId>io.github.cyjishuang</groupId>
    <artifactId>swagger-mode</artifactId>
    <version>1.0</version>
</dependency>
// https://jarcasting.com/artifacts/io.github.cyjishuang/swagger-mode/
implementation 'io.github.cyjishuang:swagger-mode:1.0'
// https://jarcasting.com/artifacts/io.github.cyjishuang/swagger-mode/
implementation ("io.github.cyjishuang:swagger-mode:1.0")
'io.github.cyjishuang:swagger-mode:jar:1.0'
<dependency org="io.github.cyjishuang" name="swagger-mode" rev="1.0">
  <artifact name="swagger-mode" type="jar" />
</dependency>
@Grapes(
@Grab(group='io.github.cyjishuang', module='swagger-mode', version='1.0')
)
libraryDependencies += "io.github.cyjishuang" % "swagger-mode" % "1.0"
[io.github.cyjishuang/swagger-mode "1.0"]

Dependencies

compile (21)

Group / Artifact Type Version
org.springframework : spring-aop jar 4.3.12.RELEASE
org.springframework : spring-orm jar 4.3.12.RELEASE
org.springframework : spring-jdbc jar 4.3.12.RELEASE
org.springframework : spring-instrument-tomcat jar 4.3.12.RELEASE
org.springframework : spring-instrument jar 4.3.12.RELEASE
org.springframework : spring-framework-bom jar 4.3.12.RELEASE
org.springframework : spring-expression jar 4.3.12.RELEASE
org.springframework : spring-core jar 4.3.12.RELEASE
org.springframework : spring-context-support jar 4.3.12.RELEASE
org.springframework : spring-context jar 4.3.12.RELEASE
org.springframework : spring-beans jar 4.3.12.RELEASE
org.springframework : spring-aspects jar 4.3.12.RELEASE
org.springframework : spring-test jar 4.3.12.RELEASE
org.springframework : spring-tx jar 4.3.12.RELEASE
org.springframework : spring-web jar 4.3.12.RELEASE
org.springframework : spring-webmvc jar 4.3.12.RELEASE
org.springframework : spring-webmvc-portlet jar 4.3.12.RELEASE
com.google.guava : guava jar 14.0
org.codehaus.jackson : jackson-mapper-asl jar 1.9.13
io.springfox : springfox-swagger2 jar 2.9.2
io.springfox : springfox-swagger-ui jar 2.9.2

test (1)

Group / Artifact Type Version
junit : junit jar 4.12

Project Modules

There are no modules declared in this project.

swagger-mode

springfox + swagger2自定义JsonObject/Map参数文档

说明

该改动不影响swagger原来的使用,Object/JsonObject 都可以兼容

Controller

image.png

Model

image.png

最终结果

request: image.png request model: image.png

response: image.png response model: image.png

代码实现

首先根据官方文档,写一个OperationBuilderPlugin类型的插件,这个插件用来读取接口的参数

@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类中我们定义的参数单元,用一个Map来保存
        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. 自定义的注释类里包含参数列表,我们把它合成一个请求Model和应答Model,放在ModelCache缓存里面
                ModelCache.getInstance().setFactory(factory)
                        .setParamMap(paramMap)
                        .addModel(annotation.get());
            }
        }
        return parameters;
    }
}

然后重写 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的实现

先重写 SwaggerResponseMessageReader 类

@Primary
@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 3)//一定要大一点
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参数加"-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参数",
                        "",
                        "",
                        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();
                }

这样就搞定了!!!

Map

map的话比较简单,参考这篇 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"/>

在创建文档的时候设置存放参数的类ModelCache.getInstance().setParamClass(XXX.class);

 public Docket createRestApi() {
        ModelCache.getInstance().setParamClass(GlobalString.class);
        return new Docket(DocumentationType.SWAGGER_2)...

这样配置就完成了,后面只需要对Controller和参数的类添加说明即可,示例

@RequestMapping(value = "/api/v1/manager")
@RestController
@Api(description = "管理员身份接口")
public class ManagerController {
   @ApiOperation(value = "管理员-预判是否存在",notes ="预判管理员是否存在" )
    @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 = "[email protected]")
    public static final String JSON_USER_EMAIL = "userEmail";

    @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";
}

Versions

Version
1.0