随着前后端代码的分离, 以及 Restful API 风格的流行, 后端程序员既需要编写代码, 又需要编写接口文档, 无形之中加重了后端程序员的工作强度, 同时后端程序员往往是不怎么喜欢编写文档的, 此时一款自动生成接口文档的工具就显得非常不可缺少. Swagger 的诞生, 一定程度上拯救了程序员, 它能自动生成接口文档, 但却并未彻底拯救, 因为格式不是我们想要的, 但是程序员站在 Swagger 的基础上就可以完全实现自我救赎.
一, 方法:
Swagger 是一个可以自动生成 API 接口文档的工具, 使用它, 开发者可以节约文档编写的时间. 要想使用它生成 API 文档需要先引入 Swagger 的 jar 包, 本文通过结合 idea 编译器和 java 代码来演示.
1.1 引入 Swagger
首先我们需要先新建一个 Springboot 工程 (新建的时候可以不选择依赖项), 新建完毕后, 我们在 pom 文件中添加 swagger 相关的依赖项和插件, 用于引入 Swagger. 依赖项和插件如下所示:
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-Web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.spring4all</groupId>
- <artifactId>swagger-spring-boot-starter</artifactId>
- <version>1.7.1.RELEASE</version>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <version>1.16.16</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>io.GitHub.swagger2markup</groupId>
- <artifactId>swagger2markup</artifactId>
- <version>1.3.1</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>fastjson</artifactId>
- <version>1.2.51</version>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- <plugin>
- <groupId>io.GitHub.swagger2markup</groupId>
- <artifactId>swagger2markup-maven-plugin</artifactId>
- <version>1.3.1</version>
- <configuration>
- <swaggerInput>http://localhost:8080/v2/API-docs</swaggerInput>
- <outputDir>src/docs/asciidoc/generated</outputDir>
- <config>
- <swagger2markup.markupLanguage>ASCIIDOC</swagger2markup.markupLanguage>
- </config>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.asciidoctor</groupId>
- <artifactId>asciidoctor-maven-plugin</artifactId>
- <version>1.5.6</version>
- <configuration>
- <sourceDirectory>src/docs/asciidoc/generated</sourceDirectory>
- <outputDirectory>src/docs/asciidoc/HTML</outputDirectory>
- <backend>HTML</backend>
- <sourceHighlighter>coderay</sourceHighlighter>
- <attributes>
- <toc>left</toc>
- </attributes>
- </configuration>
- </plugin>
- </plugins>
- </build>
- <repositories>
- <repository>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- <id>jcenter-releases</id>
- <name>jcenter</name>
- <url>http://jcenter.bintray.com</url>
- </repository>
- </repositories>
1.2 添加注释项
在引入 Swagger 之后需要在对外提供的接口上添加注释, 如下所示:
- package com.wcf.swagger.apihtml.controller;
- import com.wcf.swagger.apihtml.model.EvaluationInfo;
- import com.wcf.swagger.apihtml.model.TestModel;
- import com.wcf.swagger.apihtml.response.TestResult;
- import io.swagger.annotations.API;
- import io.swagger.annotations.ApiImplicitParam;
- import io.swagger.annotations.ApiImplicitParams;
- import io.swagger.annotations.ApiOperation;
- import org.springframework.Web.bind.annotation.PostMapping;
- import org.springframework.Web.bind.annotation.RequestBody;
- import org.springframework.Web.bind.annotation.RequestMapping;
- import org.springframework.Web.bind.annotation.RestController;
- /**
- *@author wangcanfeng
- *@note description
- *@note Created in13:59-2018/11/28
- */
- @RestController
- @RequestMapping("/test")
- @API(description= "主要用于展示一些测试接口信息", value = "测试接口集合", produces = "application/json")
- public class TestController {
- @PostMapping("/submit")
- @ApiOperation("提交测试结果")
- @ApiImplicitParams({@ApiImplicitParam(type= "body",
- dataType = "EvaluationInfo", name = "planCode", value = "考评结果", required = true)})
- public TestResultsubmitEvaluation(@RequestBodyTestModel info) {
- return new TestResult("0", true);
- }
- }
- package com.wcf.swagger.apihtml.model;
- import io.swagger.annotations.ApiModel;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
- /**
- *@author wangcanfeng
- *@note description
- *@note Created in 14:05-2018/11/28
- */
- @Data
- @ApiModel("人员信息")
- @NoArgsConstructor
- @AllArgsConstructor
- public class TestModel {
- @ApiModelProperty("名称信息")
- private Stringname;
- @ApiModelProperty("年龄")
- private Integerage;
- }
- package com.wcf.swagger.apihtml.response;
- import io.swagger.annotations.ApiModel;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
- /**
- *@author wangcanfeng
- *@note description
- *@note Created in10:45-2018/11/24
- */
- @Data
- @ApiModel("返回结果")
- public class TestResult<T> {
- @ApiModelProperty("状态码")
- private String code;
- @ApiModelProperty("数据实体")
- private T data;
- @ApiModelProperty("状态信息")
- private Stringmsg;
- public TestResult(String code, T data){
- this.code=code;
- this .data=data;
- }
- }
1.2 生成 HTML 文档
本文先通过 swagger 生成了 ascii 文档, 然后再生成 HTML 文档, 步骤如下所示:
(1) 在生成文档前, 要保证程序运行没有 bug, 否则不能生成文档. 可以编写一个 test 类, 然后再使用 test 方法生成 ascii:
- @Test
- public void generateAsciiDocsToFile() throwsException {
- // 输出 Ascii 到单文件
- Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
- .withMarkupLanguage(MarkupLanguage. ASCIIDOC)
- .withOutputLanguage(Language. ZH) // 选择语言为中文
- .withGeneratedExamples() // 选择需要输出入参和返回值样例
- .build();
- //localhost 和 8080 可以改成指定的 ip 和端口号
- Swagger2MarkupConverter.from(new URL("http://localhost:8080/v2/api-docs"))
- .withConfig(config)
- .build()
- // 这里是输出文档的位置
- .toFile(Paths.get("src/docs/asciidoc/generated/all"));
- }
(2) 配置 plugin 插件, 设置输入输出文件位置:
- <plugin>
- <groupId>org.asciidoctor</groupId>
- <artifactId>asciidoctor-maven-plugin</artifactId>
- <version>1.5.6</version>
- <configuration>
- <sourceDirectory>src/docs/asciidoc/generated</sourceDirectory>
- <outputDirectory>src/docs/asciidoc/HTML</outputDirectory>
- <backend>HTML</backend>
- <sourceHighlighter>coderay</sourceHighlighter>
- <attributes>
- <toc>left</toc>
- </attributes>
- </configuration>
- </plugin>
在 sourceDirectory 配置项中设置 ascii 文件的位置, 在 outputDirectory 配置项中设置输出文件的位置.
(3) 运行插件, 输出 HTML
点击 idea 的侧边栏中的 Maven Projects 菜单, 然后打开 Plugins, 然后点击 asciidoctor:process-asciidoc, 如下所示:
image.PNG
到这里就生成了 HTML 文档了, 但是格式并不是我们想要的, 并不能完全提高效率, 提高生产率.
image.PNG
image.PNG
1.3 自定义格式
(1)Swagger 提供的配置项, 并没有设置修改格式的, 所以需要修改源码, 源码可以在 GitHub 或者通过 idea 直接下载, 然后自行创建一个 maven 工程去修改 Swagger. 因为我们不满意的地方是入参表, 出参表, 以及结构体定义表. 打开源码, 主要需要修改的部分为:
image.PNG
其中 PropertiesTableComponent 结构体定义的参数表对象类, ParameterTableComponent 为入参表的对象类, 然后 ResponseComponent 这个是返回参数对象类. 修改它们的 apply 方法, 可以调整表格中各个列的顺序, 以及各个列中数据的值. 具体的源码量较大, 参见附录, 附录贴了这三个主要修改类的的源码. 然后修改 lang 文件夹中的 labels_zh.properties, 可以修改表头等地方的中文字样.
将这个修改过的源码打包, install 到我们的 maven 目录中, 然后再通过自己新建的 maven 工程配置的 groupId 和 artifacId 以及 version 信息引入修改过的 swagger, 替换源码:
- <dependency>
- <groupId>com.test.swagger</groupId>
- <artifactId>wcf-swagger</artifactId>
- <version>1.0-SNAPSHOT</version>
- </dependency>
替换成
- <dependency>
- <groupId>io.GitHub.swagger2markup</groupId>
- <artifactId>swagger2markup</artifactId>
- <version>1.3.1</version>
- </dependency>
(2) 修改后的展示效果:
image.PNG
image.PNG
image.PNG
到这里表格的格式已经基本符合我们的要求了, 效果不错, 如果还有别的要求可以继续修改源码.
二, 总结:
Swagger 源码生成的 API 文档虽然可以直接使用, 但是它的格式在不同程度上影响了我们的使用, 通常我们需要对它生成文档进行各种编辑或者只是复制出部分数据到我们已经编辑好格式的文档中, 这么做不仅麻烦, 还有可能出错. 如果需要让它彻底的适应自身的文档格式需求, 可以修改源码使得生成我们想要的文档格式.
通过修改 Swagger 源码, 生成的自身所需求的文档格式, 极大程度上减少了文档格式修改的时间, 降低了开发成本, 减少了开发环节, 提升程序员的开发体验和舒适度.
三, 附录:
3.1 ParameterTableComponent 类源码:
- /*
- * Copyright 2017 Robert Winkler
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package io.GitHub.swagger2markup.internal.component;
- import ch.netzwerg.paleo.StringColumn;
- import io.GitHub.swagger2markup.Swagger2MarkupConverter;
- import io.GitHub.swagger2markup.internal.adapter.ParameterAdapter;
- import io.GitHub.swagger2markup.internal.resolver.DocumentResolver;
- import io.GitHub.swagger2markup.internal.type.ObjectType;
- import io.GitHub.swagger2markup.markup.builder.MarkupDocBuilder;
- import io.GitHub.swagger2markup.model.PathOperation;
- import io.GitHub.swagger2markup.spi.MarkupComponent;
- import io.GitHub.swagger2markup.spi.PathsDocumentExtension;
- import io.swagger.models.parameters.Parameter;
- import org.apache.commons.collections4.CollectionUtils;
- import org.apache.commons.lang3.StringUtils;
- import org.apache.commons.lang3.Validate;
- import java.util.List;
- import java.util.stream.Collectors;
- import static ch.netzwerg.paleo.ColumnIds.StringColumnId;
- import static io.GitHub.swagger2markup.Labels.*;
- import static io.GitHub.swagger2markup.internal.utils.MarkupDocBuilderUtils.copyMarkupDocBuilder;
- import static org.apache.commons.lang3.StringUtils.isNotBlank;
- public class ParameterTableComponent extends MarkupComponent<ParameterTableComponent.Parameters> {
- private final DocumentResolver definitionDocumentResolver;
- private final TableComponent tableComponent;
- ParameterTableComponent(Swagger2MarkupConverter.Context context,
- DocumentResolver definitionDocumentResolver) {
- super(context);
- this.definitionDocumentResolver = Validate.notNull(definitionDocumentResolver, "DocumentResolver must not be null");
- this.tableComponent = new TableComponent(context);
- }
- public static Parameters parameters(PathOperation operation,
- List<ObjectType> inlineDefinitions,
- int titleLevel) {
- return new Parameters(operation, inlineDefinitions, titleLevel);
- }
- @Override
- public MarkupDocBuilder apply(MarkupDocBuilder markupDocBuilder, Parameters params) {
- PathOperation operation = params.operation;
- List<ObjectType> inlineDefinitions = params.inlineDefinitions;
- List<Parameter> parameters = operation.getOperation().getParameters();
- if (config.getParameterOrdering() != null)
- parameters.sort(config.getParameterOrdering());
- // Filter parameters to display in parameters section
- List<Parameter> filteredParameters = parameters.stream()
- .filter(this::filterParameter).collect(Collectors.toList());
- MarkupDocBuilder parametersBuilder = copyMarkupDocBuilder(markupDocBuilder);
- applyPathsDocumentExtension(new PathsDocumentExtension.Context(PathsDocumentExtension.Position.OPERATION_PARAMETERS_BEGIN, parametersBuilder, operation));
- if (CollectionUtils.isNotEmpty(filteredParameters)) {
- // StringColumn.Builder typeColumnBuilder = StringColumn.builder(StringColumnId.of(labels.getLabel(TYPE_COLUMN)))
- // .putMetaData(TableComponent.WIDTH_RATIO, "2");
- StringColumn.Builder nameColumnBuilder = StringColumn.builder(StringColumnId.of(labels.getLabel(NAME_COLUMN)))
- .putMetaData(TableComponent.WIDTH_RATIO, "5");
- StringColumn.Builder descriptionColumnBuilder = StringColumn.builder(StringColumnId.of(labels.getLabel(DESCRIPTION_COLUMN)))
- .putMetaData(TableComponent.WIDTH_RATIO, "9")
- .putMetaData(TableComponent.HEADER_COLUMN, "true");
- StringColumn.Builder schemaColumnBuilder = StringColumn.builder(StringColumnId.of(labels.getLabel(SCHEMA_COLUMN)))
- .putMetaData(TableComponent.WIDTH_RATIO, "3")
- .putMetaData(TableComponent.HEADER_COLUMN, "true");
- // StringColumn.Builder defaultColumnBuilder = StringColumn.builder(StringColumnId.of(labels.getLabel(DEFAULT_COLUMN)))
- // .putMetaData(TableComponent.WIDTH_RATIO, "2")
- // .putMetaData(TableComponent.HEADER_COLUMN, "true");
- // 是否必填的列
- StringColumn.Builder requireColumnBuilder = StringColumn.builder(StringColumnId.of(labels.getLabel(REQUIRE)))
- .putMetaData(TableComponent.WIDTH_RATIO, "3");
- for (Parameter parameter : filteredParameters) {
- ParameterAdapter parameterAdapter = new ParameterAdapter(context,
- operation, parameter, definitionDocumentResolver);
- inlineDefinitions.addAll(parameterAdapter.getInlineDefinitions());
- requireColumnBuilder.add(getParameterRequireColumnContent(markupDocBuilder,parameterAdapter));
- // typeColumnBuilder.add(parameterAdapter.displayType(markupDocBuilder));
- nameColumnBuilder.add(getParameterNameColumnContent(markupDocBuilder, parameterAdapter));
- schemaColumnBuilder.add(parameterAdapter.displaySchema(markupDocBuilder));
- // defaultColumnBuilder.add(parameterAdapter.displayDefaultValue(markupDocBuilder));
- descriptionColumnBuilder.add(parameterAdapter.displayDescription(markupDocBuilder));
- }
- // 调整这里的参数顺序可以修改最后生成的表格的列的顺序
- parametersBuilder = tableComponent.apply(parametersBuilder, TableComponent.parameters(
- // typeColumnBuilder.build(),
- nameColumnBuilder.build(),
- schemaColumnBuilder.build(),
- requireColumnBuilder.build(),
- descriptionColumnBuilder.build()
- // defaultColumnBuilder.build()
- ));
- }
- applyPathsDocumentExtension(new PathsDocumentExtension.Context(PathsDocumentExtension.Position.OPERATION_PARAMETERS_END, parametersBuilder, operation));
- String parametersContent = parametersBuilder.toString();
- applyPathsDocumentExtension(new PathsDocumentExtension.Context(PathsDocumentExtension.Position.OPERATION_PARAMETERS_BEFORE, markupDocBuilder, operation));
- if (isNotBlank(parametersContent)) {
- markupDocBuilder.sectionTitleLevel(params.titleLevel, labels.getLabel(PARAMETERS));
- markupDocBuilder.text(parametersContent);
- }
- applyPathsDocumentExtension(new PathsDocumentExtension.Context(PathsDocumentExtension.Position.OPERATION_PARAMETERS_AFTER, markupDocBuilder, operation));
- return markupDocBuilder;
- }
- private String getParameterNameColumnContent(MarkupDocBuilder markupDocBuilder, ParameterAdapter parameter) {
- MarkupDocBuilder parameterNameContent = copyMarkupDocBuilder(markupDocBuilder);
- parameterNameContent.boldTextLine(parameter.getName(), true);
- // 是否必填的信息都存在单独列里面
- // 生成的名称列不需要有是否必填
- // if (parameter.getRequired())
- // parameterNameContent.italicText(labels.getLabel(FLAGS_REQUIRED).toLowerCase());
- // else
- // parameterNameContent.italicText(labels.getLabel(FLAGS_OPTIONAL).toLowerCase());
- return parameterNameContent.toString();
- }
- /**
- * 功能描述:
- * @param markupDocBuilder 文件编辑器
- * @param parameter 参数对象
- * @return: 不用管
- * @since: v2.2
- * @Author:wangcanfeng
- * @Date: 2018/11/23 14:32
- */
- private String getParameterRequireColumnContent(MarkupDocBuilder markupDocBuilder, ParameterAdapter parameter) {
- MarkupDocBuilder parameterReuqireContent = copyMarkupDocBuilder(markupDocBuilder);
- // 是否必填的信息都存在单独列里面
- if (parameter.getRequired())
- parameterReuqireContent.italicText(labels.getLabel(FLAGS_REQUIRED).toLowerCase());
- else
- parameterReuqireContent.italicText(labels.getLabel(FLAGS_OPTIONAL).toLowerCase());
- return parameterReuqireContent.toString();
- }
- /**
- * Filter parameters to display in parameters section
- *
- * @param parameter parameter to filter
- * @return true if parameter can be displayed
- */
- private boolean filterParameter(Parameter parameter) {
- return (!config.isFlatBodyEnabled() || !StringUtils.equals(parameter.getIn(), "body"));
- }
- /**
- * Apply extension context to all OperationsContentExtension.
- *
- * @param context context
- */
- private void applyPathsDocumentExtension(PathsDocumentExtension.Context context) {
- extensionRegistry.getPathsDocumentExtensions().forEach(extension -> extension.apply(context));
- }
- public static class Parameters {
- private final PathOperation operation;
- private final int titleLevel;
- private final List<ObjectType> inlineDefinitions;
- public Parameters(PathOperation operation,
- List<ObjectType> inlineDefinitions,
- int titleLevel) {
- this.operation = Validate.notNull(operation, "PathOperation must not be null");
- this.inlineDefinitions = Validate.notNull(inlineDefinitions, "InlineDefinitions must not be null");
- this.titleLevel = titleLevel;
- }
- }
- }
3.2 PropertiesTableComponent 源码:
- /*
- * Copyright 2017 Robert Winkler
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package io.GitHub.swagger2markup.internal.component;
- import ch.netzwerg.paleo.ColumnIds;
- import ch.netzwerg.paleo.StringColumn;
- import io.GitHub.swagger2markup.Swagger2MarkupConverter;
- import io.GitHub.swagger2markup.internal.adapter.ParameterAdapter;
- import io.GitHub.swagger2markup.internal.adapter.PropertyAdapter;
- import io.GitHub.swagger2markup.internal.resolver.DocumentResolver;
- import io.GitHub.swagger2markup.internal.type.ObjectType;
- import io.GitHub.swagger2markup.internal.type.Type;
- import io.GitHub.swagger2markup.markup.builder.MarkupDocBuilder;
- import io.GitHub.swagger2markup.spi.MarkupComponent;
- import io.swagger.models.properties.Property;
- import io.swagger.util.JSON;
- import org.apache.commons.collections4.MapUtils;
- import org.apache.commons.lang3.Validate;
- import java.math.BigDecimal;
- import java.text.DecimalFormat;
- import java.text.DecimalFormatSymbols;
- import java.util.List;
- import java.util.Map;
- import java.util.Optional;
- import static io.GitHub.swagger2markup.Labels.*;
- import static io.GitHub.swagger2markup.internal.utils.InlineSchemaUtils.createInlineType;
- import static io.GitHub.swagger2markup.internal.utils.MapUtils.toSortedMap;
- import static io.GitHub.swagger2markup.internal.utils.MarkupDocBuilderUtils.copyMarkupDocBuilder;
- import static io.GitHub.swagger2markup.internal.utils.MarkupDocBuilderUtils.markupDescription;
- import static org.apache.commons.lang3.StringUtils.isNotBlank;
- public class PropertiesTableComponent extends MarkupComponent<PropertiesTableComponent.Parameters> {
- private final DocumentResolver definitionDocumentResolver;
- private final TableComponent tableComponent;
- /**
- * Build a generic property table
- *
- * @param definitionDocumentResolver definition document resolver to apply to property type cross-reference
- */
- PropertiesTableComponent(Swagger2MarkupConverter.Context context,
- DocumentResolver definitionDocumentResolver) {
- super(context);
- this.definitionDocumentResolver = definitionDocumentResolver;
- this.tableComponent = new TableComponent(context);
- }
- public static Parameters parameters(Map<String, Property> properties,
- String parameterName,
- List<ObjectType> inlineDefinitions) {
- return new Parameters(properties, parameterName, inlineDefinitions);
- }
- public MarkupDocBuilder apply(MarkupDocBuilder markupDocBuilder, Parameters params) {
- //TODO: This method is too complex, split it up in smaller methods to increase readability
- // 展示属性名, 类型, 是否必填, 描述列表
- // 名称
- StringColumn.Builder nameColumnBuilder = StringColumn.builder(
- ColumnIds.StringColumnId.of(labels.getLabel(NAME_COLUMN)))
- .putMetaData(TableComponent.WIDTH_RATIO, "5");
- // 描述
- StringColumn.Builder descriptionColumnBuilder = StringColumn.builder(
- ColumnIds.StringColumnId.of(
- labels.getLabel(DESCRIPTION_COLUMN)))
- .putMetaData(TableComponent.WIDTH_RATIO, "9")
- .putMetaData(TableComponent.HEADER_COLUMN, "true");
- // 内容
- StringColumn.Builder schemaColumnBuilder = StringColumn.builder(
- ColumnIds.StringColumnId.of(labels.getLabel(SCHEMA_COLUMN)))
- .putMetaData(TableComponent.WIDTH_RATIO, "3")
- .putMetaData(TableComponent.HEADER_COLUMN, "true");
- // 是否必填的列
- StringColumn.Builder requireColumnBuilder = StringColumn.builder(
- ColumnIds.StringColumnId.of(labels.getLabel(REQUIRE)))
- .putMetaData(TableComponent.WIDTH_RATIO, "3");
- Map<String, Property> properties = params.properties;
- if (MapUtils.isNotEmpty(properties)) {
- Map<String, Property> sortedProperties = toSortedMap(properties, config.getPropertyOrdering());
- sortedProperties.forEach((String propertyName, Property property) -> {
- PropertyAdapter propertyAdapter = new PropertyAdapter(property);
- Type propertyType = propertyAdapter.getType(definitionDocumentResolver);
- if (config.isInlineSchemaEnabled()) {
- propertyType = createInlineType(propertyType,
- propertyName, params.parameterName + " " + propertyName, params.inlineDefinitions);
- }
- Optional<Object> optionalExample = propertyAdapter.getExample(
- config.isGeneratedExamplesEnabled(), markupDocBuilder);
- Optional<Object> optionalDefaultValue = propertyAdapter.getDefaultValue();
- Optional<Integer> optionalMaxLength = propertyAdapter.getMaxlength();
- Optional<Integer> optionalMinLength = propertyAdapter.getMinlength();
- Optional<String> optionalPattern = propertyAdapter.getPattern();
- Optional<BigDecimal> optionalMinValue = propertyAdapter.getMin();
- boolean exclusiveMin = propertyAdapter.getExclusiveMin();
- Optional<BigDecimal> optionalMaxValue = propertyAdapter.getMax();
- boolean exclusiveMax = propertyAdapter.getExclusiveMax();
- MarkupDocBuilder propertyNameContent = copyMarkupDocBuilder(markupDocBuilder);
- propertyNameContent.boldTextLine(propertyName, true);
- // 不展示
- // if (propertyAdapter.getReadOnly()) {
- // propertyNameContent.newLine(true);
- // propertyNameContent.italicText(labels.getLabel(FLAGS_READ_ONLY).toLowerCase());
- // }
- MarkupDocBuilder descriptionContent = copyMarkupDocBuilder(markupDocBuilder);
- String description = markupDescription(config.getSwaggerMarkupLanguage(),
- markupDocBuilder, property.getDescription());
- if (isNotBlank(description))
- descriptionContent.text(description);
- if (optionalDefaultValue.isPresent()) {
- if (isNotBlank(descriptionContent.toString())) {
- descriptionContent.newLine(true);
- }
- descriptionContent.boldText(labels.getLabel(DEFAULT_COLUMN))
- .text(COLON).literalText(JSON.pretty(optionalDefaultValue.get()));
- }
- if (optionalMinLength.isPresent() && optionalMaxLength.isPresent()) {
- // combination of minlength/maxlength
- Integer minLength = optionalMinLength.get();
- Integer maxLength = optionalMaxLength.get();
- if (isNotBlank(descriptionContent.toString())) {
- descriptionContent.newLine(true);
- }
- String lengthRange = minLength + "-" + maxLength;
- if (minLength.equals(maxLength)) {
- lengthRange = minLength.toString();
- }
- descriptionContent.boldText(labels.getLabel(LENGTH_COLUMN))
- .text(COLON).literalText(lengthRange);
- } else {
- if (optionalMinLength.isPresent()) {
- if (isNotBlank(descriptionContent.toString())) {
- descriptionContent.newLine(true);
- }
- descriptionContent.boldText(labels.getLabel(MINLENGTH_COLUMN)).
- text(COLON).literalText(optionalMinLength.get().toString());
- }
- if (optionalMaxLength.isPresent()) {
- if (isNotBlank(descriptionContent.toString())) {
- descriptionContent.newLine(true);
- }
- descriptionContent.boldText(labels.getLabel(MAXLENGTH_COLUMN)).
- text(COLON).literalText(optionalMaxLength.get().toString());
- }
- }
- if (optionalPattern.isPresent()) {
- if (isNotBlank(descriptionContent.toString())) {
- descriptionContent.newLine(true);
- }
- descriptionContent.boldText(labels.getLabel(PATTERN_COLUMN)).
- text(COLON).literalText(JSON.pretty(optionalPattern.get()));
- }
- DecimalFormat numberFormatter = new DecimalFormat("#.##",
- DecimalFormatSymbols.getInstance(config.getOutputLanguage().toLocale()));
- if (optionalMinValue.isPresent()) {
- if (isNotBlank(descriptionContent.toString())) {
- descriptionContent.newLine(true);
- }
- String minValueColumn = exclusiveMin ? labels.
- getLabel(MINVALUE_EXCLUSIVE_COLUMN) : labels.getLabel(MINVALUE_COLUMN);
- descriptionContent.boldText(minValueColumn).text(COLON)
- .literalText(numberFormatter.format(optionalMinValue.get()));
- }
- if (optionalMaxValue.isPresent()) {
- if (isNotBlank(descriptionContent.toString())) {
- descriptionContent.newLine(true);
- }
- String maxValueColumn = exclusiveMax ? labels
- .getLabel(MAXVALUE_EXCLUSIVE_COLUMN) : labels.getLabel(MAXVALUE_COLUMN);
- descriptionContent.boldText(maxValueColumn).text(COLON)
- .literalText(numberFormatter.format(optionalMaxValue.get()));
- }
- if (optionalExample.isPresent()) {
- if (isNotBlank(description) || optionalDefaultValue.isPresent()) {
- descriptionContent.newLine(true);
- }
- // descriptionContent.boldText(labels.getLabel(EXAMPLE_COLUMN))
- // .text(COLON).literalText(JSON.pretty(optionalExample.get()));
- }
- nameColumnBuilder.add(propertyNameContent.toString());
- schemaColumnBuilder.add(propertyType.displaySchema(markupDocBuilder));
- requireColumnBuilder.add(getParameterRequireColumnContent(markupDocBuilder,property));
- descriptionColumnBuilder.add(descriptionContent.toString());
- });
- }
- return tableComponent.apply(markupDocBuilder, TableComponent.parameters(
- nameColumnBuilder.build(),
- schemaColumnBuilder.build(),
- requireColumnBuilder.build(),
- descriptionColumnBuilder.build()
- ));
- }
- public static class Parameters {
- private final Map<String, Property> properties;
- private final String parameterName;
- private final List<ObjectType> inlineDefinitions;
- public Parameters(Map<String, Property> properties,
- String parameterName,
- List<ObjectType> inlineDefinitions) {
- this.properties = Validate.notNull(properties, "Properties must not be null");
- this.parameterName = Validate.notBlank(parameterName, "ParameterName must not be blank");
- this.inlineDefinitions = Validate.notNull(inlineDefinitions, "InlineDefinitions must not be null");
- }
- }
- /**
- * 功能描述:
- *
- * @param markupDocBuilder 文件编辑器
- * @return: 不用管
- * @since: v2.2
- * @Author:wangcanfeng
- * @Date: 2018/11/23 14:32
- */
- private String getParameterRequireColumnContent(MarkupDocBuilder markupDocBuilder,Property property) {
- MarkupDocBuilder parameterRequireContent = copyMarkupDocBuilder(markupDocBuilder);
- // 是否必填的信息都存在单独列里面
- if (property.getRequired())
- parameterRequireContent.italicText(labels.getLabel(FLAGS_REQUIRED).toLowerCase());
- else
- parameterRequireContent.italicText(labels.getLabel(FLAGS_OPTIONAL).toLowerCase());
- // parameterRequireContent.italicText("是");
- return parameterRequireContent.toString();
- }
- }
3.3 ResponseComponent 源码:
- /*
- * Copyright 2017 Robert Winkler
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package io.GitHub.swagger2markup.internal.component;
- import ch.netzwerg.paleo.StringColumn;
- import io.GitHub.swagger2markup.Swagger2MarkupConverter;
- import io.GitHub.swagger2markup.internal.adapter.ParameterAdapter;
- import io.GitHub.swagger2markup.internal.adapter.PropertyAdapter;
- import io.GitHub.swagger2markup.internal.resolver.DocumentResolver;
- import io.GitHub.swagger2markup.internal.type.ObjectType;
- import io.GitHub.swagger2markup.internal.type.Type;
- import io.GitHub.swagger2markup.markup.builder.MarkupDocBuilder;
- import io.GitHub.swagger2markup.model.PathOperation;
- import io.GitHub.swagger2markup.spi.MarkupComponent;
- import io.GitHub.swagger2markup.spi.PathsDocumentExtension;
- import io.swagger.models.Response;
- import io.swagger.models.properties.Property;
- import io.swagger.util.JSON;
- import org.apache.commons.collections4.MapUtils;
- import org.apache.commons.lang3.Validate;
- import java.util.List;
- import java.util.Map;
- import java.util.Optional;
- import static ch.netzwerg.paleo.ColumnIds.StringColumnId;
- import static io.GitHub.swagger2markup.Labels.*;
- import static io.GitHub.swagger2markup.internal.utils.InlineSchemaUtils.createInlineType;
- import static io.GitHub.swagger2markup.internal.utils.MapUtils.toSortedMap;
- import static io.GitHub.swagger2markup.internal.utils.MarkupDocBuilderUtils.*;
- import static org.apache.commons.lang3.StringUtils.isNotBlank;
- public class ResponseComponent extends MarkupComponent<ResponseComponent.Parameters> {
- private final TableComponent tableComponent;
- private final DocumentResolver definitionDocumentResolver;
- ResponseComponent(Swagger2MarkupConverter.Context context,
- DocumentResolver definitionDocumentResolver) {
- super(context);
- this.definitionDocumentResolver = Validate.notNull(definitionDocumentResolver,
- "DocumentResolver must not be null");
- this.tableComponent = new TableComponent(context);
- }
- public static Parameters parameters(PathOperation operation,
- int titleLevel,
- List<ObjectType> inlineDefinitions) {
- return new Parameters(operation, titleLevel, inlineDefinitions);
- }
- @Override
- public MarkupDocBuilder apply(MarkupDocBuilder markupDocBuilder, Parameters params) {
- PathOperation operation = params.operation;
- Map<String, Response> responses = operation.getOperation().getResponses();
- MarkupDocBuilder responsesBuilder = copyMarkupDocBuilder(markupDocBuilder);
- applyPathsDocumentExtension(new PathsDocumentExtension.Context(
- PathsDocumentExtension.Position.OPERATION_RESPONSES_BEGIN, responsesBuilder, operation));
- if (MapUtils.isNotEmpty(responses)) {
- // 返回参数表也需要按照属性名, 参数类型, 是否必填, 描述来展示
- //HTTP 代码不需要展示
- // StringColumn.Builder httpCodeColumnBuilder = StringColumn.builder(StringColumnId.of(labels.getLabel(HTTP_CODE_COLUMN)))
- // .putMetaData(TableComponent.WIDTH_RATIO, "2");
- StringColumn.Builder nameColumnBuilder = StringColumn.builder(StringColumnId.of(labels.getLabel(NAME_COLUMN)))
- .putMetaData(TableComponent.WIDTH_RATIO, "5");
- StringColumn.Builder schemaColumnBuilder = StringColumn.builder(StringColumnId.of(labels.getLabel(SCHEMA_COLUMN)))
- .putMetaData(TableComponent.WIDTH_RATIO, "3")
- .putMetaData(TableComponent.HEADER_COLUMN, "true");
- // 是否必填的列
- StringColumn.Builder requireColumnBuilder = StringColumn.builder(StringColumnId.of(labels.getLabel(REQUIRE)))
- .putMetaData(TableComponent.WIDTH_RATIO, "3")
- .putMetaData(TableComponent.HEADER_COLUMN, "true");
- StringColumn.Builder descriptionColumnBuilder = StringColumn.builder(StringColumnId.of(labels.getLabel(DESCRIPTION_COLUMN)))
- .putMetaData(TableComponent.WIDTH_RATIO, "9")
- .putMetaData(TableComponent.HEADER_COLUMN, "true");
- Map<String, Response> sortedResponses = toSortedMap(responses, config.getResponseOrdering());
- sortedResponses.forEach((String responseName, Response response) -> {
- String schemaContent = labels.getLabel(NO_CONTENT);
- if (response.getSchema() != null) {
- Property property = response.getSchema();
- Type type = new PropertyAdapter(property).getType(definitionDocumentResolver);
- if (config.isInlineSchemaEnabled()) {
- type = createInlineType(type, labels.getLabel(RESPONSE) + " " + responseName,
- operation.getId() + "" + labels.getLabel(RESPONSE) +" "
- + responseName, params.inlineDefinitions);
- }
- schemaContent = type.displaySchema(markupDocBuilder);
- }
- MarkupDocBuilder descriptionBuilder = copyMarkupDocBuilder(markupDocBuilder);
- descriptionBuilder.text(markupDescription(config.getSwaggerMarkupLanguage(),
- markupDocBuilder, response.getDescription()));
- // Map<String, Property> headers = response.getHeaders();
- // if (MapUtils.isNotEmpty(headers)) {
- // descriptionBuilder.newLine(true).boldText(
- // labels.getLabel(HEADERS_COLUMN)).text(COLON);
- // for (Map.Entry<String, Property> header : headers.entrySet()) {
- // descriptionBuilder.newLine(true);
- // Property headerProperty = header.getValue();
- // PropertyAdapter headerPropertyAdapter = new PropertyAdapter(headerProperty);
- // Type propertyType = headerPropertyAdapter.getType(definitionDocumentResolver);
- // String headerDescription = markupDescription(config.getSwaggerMarkupLanguage(),
- // markupDocBuilder, headerProperty.getDescription());
- // Optional<Object> optionalDefaultValue = headerPropertyAdapter.getDefaultValue();
- //
- // descriptionBuilder
- // .literalText(header.getKey())
- // .text(String.format("(%s)", propertyType.displaySchema(markupDocBuilder)));
- //
- // if (isNotBlank(headerDescription) || optionalDefaultValue.isPresent()) {
- // descriptionBuilder.text(COLON);
- //
- // if (isNotBlank(headerDescription) && !headerDescription.endsWith("."))
- // headerDescription += ".";
- //
- // descriptionBuilder.text(headerDescription);
- //
- // optionalDefaultValue.ifPresent(o -> descriptionBuilder.text(" ")
- // .boldText(labels.getLabel(DEFAULT_COLUMN))
- // .text(COLON).literalText(JSON.pretty(o)));
- // }
- // }
- // }
- //todo 不需要加载 http 代码
- // httpCodeColumnBuilder.add(boldText(markupDocBuilder, responseName));
- nameColumnBuilder.add(boldText(markupDocBuilder, responseName));
- requireColumnBuilder.add(getParameterRequireColumnContent(markupDocBuilder));
- descriptionColumnBuilder.add(descriptionBuilder.toString());
- schemaColumnBuilder.add(schemaContent);
- });
- // 显示顺序需要改为属性名, 类型, 是否必填, 描述
- responsesBuilder = tableComponent.apply(responsesBuilder,
- TableComponent.parameters(
- // httpCodeColumnBuilder.build(),
- nameColumnBuilder.build(),
- schemaColumnBuilder.build(),
- requireColumnBuilder.build(),
- descriptionColumnBuilder.build()));
- }
- applyPathsDocumentExtension(new PathsDocumentExtension.Context(
- PathsDocumentExtension.Position.OPERATION_RESPONSES_END,
- responsesBuilder, operation));
- String responsesContent = responsesBuilder.toString();
- applyPathsDocumentExtension(new PathsDocumentExtension.Context(
- PathsDocumentExtension.Position.OPERATION_RESPONSES_BEFORE,
- markupDocBuilder, operation));
- if (isNotBlank(responsesContent)) {
- markupDocBuilder.sectionTitleLevel(params.titleLevel, labels.getLabel(RESPONSES));
- markupDocBuilder.text(responsesContent);
- }
- applyPathsDocumentExtension(new PathsDocumentExtension.Context(
- PathsDocumentExtension.Position.OPERATION_RESPONSES_AFTER, markupDocBuilder, operation));
- return markupDocBuilder;
- }
- /**
- * Apply extension context to all OperationsContentExtension.
- *
- * @param context context
- */
- private void applyPathsDocumentExtension(PathsDocumentExtension.Context context) {
- extensionRegistry.getPathsDocumentExtensions().forEach(extension -> extension.apply(context));
- }
- public static class Parameters {
- private final PathOperation operation;
- private final int titleLevel;
- private final List<ObjectType> inlineDefinitions;
- public Parameters(PathOperation operation,
- int titleLevel,
- List<ObjectType> inlineDefinitions) {
- this.operation = Validate.notNull(operation, "PathOperation must not be null");
- this.titleLevel = titleLevel;
- this.inlineDefinitions = Validate.notNull(inlineDefinitions, "InlineDefinitions must not be null");
- }
- }
- /**
- * 功能描述:
- *
- * @param markupDocBuilder 文件编辑器
- * @return: 不用管
- * @since: v2.2
- * @Author:wangcanfeng
- * @Date: 2018/11/23 14:32
- */
- private String getParameterRequireColumnContent(MarkupDocBuilder markupDocBuilder) {
- MarkupDocBuilder parameterRequireContent = copyMarkupDocBuilder(markupDocBuilder);
- // 全部默认为需要
- parameterRequireContent.italicText(labels.getLabel(FLAGS_REQUIRED));
- return parameterRequireContent.toString();
- }
- }
来源: http://www.jianshu.com/p/039cc9dc9673