Swagger在线离线文档详解

前言

1.什么是swagger

Swagger 是一个用于生成、描述和调用 RESTful 接口的 Web 服务。通俗的来讲,Swagger 就是将项目中所有(想要暴露的)接口展现在页面上,并且可以进行接口调用和测试的服务。

从上述 Swagger 定义我们不难看出 Swagger 有以下 3 个重要的作用:

将项目中所有的接口展现在页面上,这样后端程序员就不需要专门为前端使用者编写专门的接口文档;

当接口更新之后,只需要修改代码中的 Swagger 描述就可以实时生成新的接口文档了,从而规避了接口文档老旧不能使用的问题;

通过 Swagger 页面,我们可以直接进行接口调用,降低了项目开发阶段的调试成本。

Swagger3完全遵循了 OpenAPI 规范。Swagger 官网地址:https://swagger.io/

2. 什么是Knife4J? 和Swagger什么关系?

Knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案, 前身是swagger-bootstrap-ui,取名kni4j是希望她能像一把匕首一样小巧,轻量,并且功能强悍!

Knife4j的前身是swagger-bootstrap-ui,为了契合微服务的架构发展,由于原来swagger-bootstrap-ui采用的是后端Java代码+前端Ui混合打包的方式,在微服务架构下显的很臃肿,因此项目正式更名为knife4j

更名后主要专注的方面

前后端Java代码以及前端Ui模块进行分离,在微服务架构下使用更加灵活

提供专注于Swagger的增强解决方案,不同于只是改善增强前端Ui部分

相关文档请参考:https://doc.xiaominfo.com/knife

一.配置

  1. pom依赖

    <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.github.swagger2markup</groupId>
            <artifactId>swagger2markup</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

2.配置文件

 package com.example.test2.config;

import com.example.test2.model.enums.CommonResultEnum;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import io.swagger.annotations.Api;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.*;
import springfox.documentation.schema.ScalarType;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Configuration
@EnableSwagger2
@EnableKnife4j
public class Swagger2Config {
    @Bean
    public Docket appApi() {
        ArrayList<Response> responseMessages = new ArrayList<>();

//                如果没有做自定义响应处理,可以用sawgger做自定义响应处理
        //useDefaultResponseMessages 必须false
        Arrays.stream(CommonResultEnum.values()).forEach(
                codeEnum -> responseMessages.add(
                        new ResponseBuilder()
                                .code(codeEnum.getCode())
                                .description(codeEnum.getMessage())
                                .build())
        );

        return new Docket(DocumentationType.SWAGGER_2)
                //是否使用默认返回信息
                .useDefaultResponseMessages(false)
                //不太清楚这俩是干嘛的..
                /* .globalResponses(HttpMethod.GET,responseMessages)
                 .globalResponses(HttpMethod.POST,responseMessages)*/
                //项目名称
                .groupName("菜菜小菠菠")
                //配置特定的描述
                .apiInfo(apiInfo())
                .select()
                //扫描接口的方式
                //通过包名扫描
//                .apis(RequestHandlerSelectors.basePackage("com.example.test2.controller"))
                //扫描所有
//                .apis(RequestHandlerSelectors.any())
                //通过api注解扫描
                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
                //通过方法的注解ApiOperation扫描
//                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                //过滤所有路径
                .paths(PathSelectors.any())
                .build()
                //全局参数
                .globalRequestParameters(this.setHeader())
                // ⽀持的通讯协议集合
                .protocols(Sets.newHashSet("http", "https"));
    }

    /**
     * 配置swagger信息
     *
     * @return
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("菜菜小菠菠接口文档")
                .description("该文档主要提供菜菜小菠菠后端的接口 \r\n\n")
                .contact(new Contact("", "", null))
                .version("0.0.1")
                .build();

    }

    /**
     * 添加header配置
     *
     * @return
     */
    private List<RequestParameter> setHeader() {
        ArrayList<RequestParameter> parameters = new ArrayList<>();
        parameters.add(new RequestParameterBuilder()
                .name("token")
                .description("token令牌")
                .required(true)
                .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
                .in(ParameterType.HEADER)
                .build());
        return parameters;
    }
}

3.enum

package com.example.test2.model.enums;


import com.example.test2.model.transdto.BaseResult;

/**
 * 公共返回结果代码和信息 实现公共返回接口
 */
public enum CommonResultEnum implements BaseResult {

    /**
     * 公共信息
     */
    COMMON_SUCCESS("0", "请求成功"),
    COMMON_FAILED("-1", "请求失败"),

    COMMON_SYS_ERROR("1000", "系统运行时错误"),


    ;


    /**
     * 返回信息代码
     */
    private String code;

    /**
     * 返回信息内容
     */
    private String message;

    /**
     * constructors
     */
    CommonResultEnum(String code, String message) {
        this.code = code;
        this.message = message;
    }

    /**
     * getters
     */
    @Override
    public String getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }
}

二.主要注解

* @Api 类注解,在控制类添加此注解,可以对控制器类进⾏功能说明

* tags="说明该类的作⽤,可以在UI界⾯上看到的注解"

* value="该参数没什么意义,可以不配置"

* @ApiOperation ⽅法注解:说明接⼝⽅法的作⽤

* value="说明⽅法的⽤途、作⽤"

* notes="⽅法的备注说明"

* @ApiImplicitParams和@ApiImplicitParam ⽅法注解,说明接⼝⽅法的参数

* @ApiImplicitParam:⽤在@ApiImplicitParams注解中,指定⼀个请求参数的各个⽅⾯

* name:参数名

* value:参数的汉字说明、解释

* required:参数是否必须传

* paramType:参数放在哪个地⽅

* · header --> 请求参数的获取:@RequestHeader

* · query --> 请求参数的获取:@RequestParam

* · path(⽤于restful接⼝)--> 请求参数的获取:@PathVariable

* · div(不常⽤)

* · form(不常⽤)

* dataType:参数类型,默认String,其它值dataType="Integer" * defaultValue:参数的默认值

* @ApiIgnore ⽅法注解,接⼝不会在ui⽂档中出现

* @ApiResponses:⽤在请求的⽅法上,表示⼀组响应

* @ApiResponse:⽤在@ApiResponses中,⼀般⽤于表达⼀个错误的响应信息

* code:状态码,例如400

* message:信息,例如"请求参数没填好"

* response:抛出异常的类

三.界面展示

get请求

    @GetMapping(value = "test2")
    @ApiOperation(value = "get请求", notes = "get请求notes")
    public TestDto getMonthly(
                                 @ApiParam(required = true, value = "角色") @RequestParam String name,
                                 @ApiParam(required = true, value = "职业") String job) {
        TestDto monthly = iservice.getdto(name, job);
        return monthly;
    }

输入http://localhost:8088/swagger-ui.html

接口内部就不给大家展示了,相信大家也都用过swagger-ui,接下来展示doc.html

输入http://localhost:8088/doc.html

和Swagger2Config.java的对应

header的对应 类似于postman的环境变量

接口的配置

可直接进行请求,方便调试

接口文档下载

文档内容:

文档基本能满足接口文档的标准,有些东西自己可以手动改改,比如字体,段落什么的

响应实例的特殊符号修改方法:

ctrl+f查找

其他符号同理

四.详细用法

  1. post请求使用参数传参

可以看到,请求类型变成了body.前端同学看到后肯定会迷惑

加上RequestParam注解后

    @PostMapping(value = "test1")
    @ApiOperation(value = "get请求", notes = "get请求notes")
    public TestDto getMonthly(
                                 @ApiParam(required = true, value = "角色") @RequestParam String name,
                                 @ApiParam(required = true, value = "职业") String job) {
        TestDto monthly = iservice.getdto(name, job);
        return monthly;
    }

可以看到 加上RequestParam注解后,name参数的请求类型变成了query

还有一种方式,是swagger的正统解决方式 ApiImplicitParam注解

可以看到,name的请求类型也会变成query

  1. 统一响应类中包含响应结果

实际应用中,我们都会使用响应类来包含响应结果,类似这样

   public ResultData getMonthly(
                                 @ApiParam(required = true, value = "角色")  String name,
                                 @ApiParam(required = true, value = "职业") String job) {
        TestDto monthly = iservice.getdto(name, job);
        return ResultData.ok(monthly);
    }

响应结果会变成这样:

data里的TestDto类不予显示.我们可以把ResultData里的data的格式是object

    /**
     * 数据
     */
    private Object data;
  private ResultData(BaseResult baseResult, Object data) {
        this.status = baseResult.getCode();
        this.message = baseResult.getMessage();
        this.data = data;
    }

将ResultData类中的所有data的类型,全部改为T

   /**
     * 数据
     */
    private T data;
    private ResultData(BaseResult baseResult, T data) {
        this.status = baseResult.getCode();
        this.message = baseResult.getMessage();
        this.data = data;
    }

同时要将方法的返回值改为ResultData<TestDto>

  public ResultData<TestDto> getMonthly(
                                 @ApiParam(required = true, value = "角色")  String name,
                                 @ApiParam(required = true, value = "职业") String job) {
        TestDto monthly = iservice.getdto(name, job);
        return ResultData.ok(monthly);
    }

swagger页面就有值了

五.踩坑系列

  1. Failed to load API definition

java.lang.NullPointerException: null
    at springfox.documentation.swagger2.mappers.RequestParameterMapper.mapParameter(RequestParameterMapper.java:55) ~[springfox-swagger2-3.0.0.jar:3.0.0]
    at springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper.beforeMappingOperations(ServiceModelToSwagger2Mapper.java:125) ~[springfox-swagger2-3.0.0.jar:3.0.0]
    at springfox.documentation.swagger2.mappers.ServiceModelToSwagger2MapperImpl.mapOperation(ServiceModelToSwagger2MapperImpl.java:109) ~[springfox-swagger2-3.0.0.jar:3.0.0]
    at springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper.mapOperations(ServiceModelToSwagger2Mapper.java:270) ~[springfox-swagger2-3.0.0.jar:3.0.0]
    at springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper.lambda$mapApiListings$2(ServiceModelToSwagger2Mapper.java:258) ~[springfox-swagger2-3.0.0.jar:3.0.0]
    at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) ~[na:1.8.0_181]
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382) ~[na:1.8.0_181]
    at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580) ~[na:1.8.0_181]
    at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:270) ~[na:1.8.0_181]
    at java.util.TreeMap$ValueSpliterator.forEachRemaining(TreeMap.java:2897) ~[na:1.8.0_181]
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) ~[na:1.8.0_181]
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[na:1.8.0_181]
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[na:1.8.0_181]
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:1.8.0_181]
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_181]
    at java.util.stream.ReferencePipeline.forEachOrdered(ReferencePipeline.java:423) ~[na:1.8.0_181]
    at springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper.mapApiListings(ServiceModelToSwagger2Mapper.java:253) ~[springfox-swagger2-3.0.0.jar:3.0.0]
    at springfox.documentation.swagger2.mappers.ServiceModelToSwagger2MapperImpl.mapDocumentation(ServiceModelToSwagger2MapperImpl.java:48) ~[springfox-swagger2-3.0.0.jar:3.0.0]
    at springfox.documentation.swagger2.web.Swagger2ControllerWebMvc.getDocumentation(Swagger2ControllerWebMvc.java:99) ~[springfox-swagger2-3.0.0.jar:3.0.0]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197) ~[spring-web-5.3.7.jar:5.3.7]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) ~[spring-web-5.3.7.jar:5.3.7]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.7.jar:5.3.7]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) ~[spring-webmvc-5.3.7.jar:5.3.7]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.7.jar:5.3.7]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.7.jar:5.3.7]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1063) ~[spring-webmvc-5.3.7.jar:5.3.7]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.7.jar:5.3.7]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.7.jar:5.3.7]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.7.jar:5.3.7]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:626) ~[tomcat-embed-core-9.0.46.jar:4.0.FR]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.7.jar:5.3.7]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.46.jar:4.0.FR]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.7.jar:5.3.7]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.7.jar:5.3.7]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.7.jar:5.3.7]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.7.jar:5.3.7]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.7.jar:5.3.7]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.7.jar:5.3.7]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) [tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) [tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) [tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) [tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) [tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) [tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) [tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707) [tomcat-embed-core-9.0.46.jar:9.0.46]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.46.jar:9.0.46]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_181]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_181]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.46.jar:9.0.46]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_181]

这个报错我是百思不得其解,为什么会报空指针?为什么另一个项目好使,我新建的demo就不好使?我建项目的时候难道有什么错误步骤吗?我为什么一步一个坎?我是谁?我在哪?

这个错误我找了一天,也在网上找了很多答案,有的说缺少spring-boot-starter-web依赖,但我在建项目时这个依赖就已经被idea自动添加了.终于在第二天,一个偶然的机会发现了是因为参数名字和注解的参数名字没对应.

    @ApiOperation(value = "添加接口", notes = "添加接口")
    @PostMapping(value = "test2")
    @ApiImplicitParam(name = "aaa", type = "body", dataTypeClass = TestDto.class, required = true)
    public ResultData<String> add(@RequestBody TestDto testDto) {
        return ResultData.ok("success");
    }

参数名字叫testDto,注解里写的是name = "aaa",改对了就可以快乐的开发了~~~

  1. swagger页面无报错,控制台也无报错

但是没有接口.

最后发现是配置类中包名写错

.apis(RequestHandlerSelectors.basePackage("com.example.test3.controller"))

包名应该是test2,写成了test3

最惨的是,我同时遇到了这两个错误,包名没写对的同时,参数名也写错了...这就造成,我把包名写对,就报错,包名写错,就没接口,脑子都凌乱了....

六.swagger升级为3.0

将pom文件中这两个依赖

<dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

改为

       <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>

配置类的注解更改为

@Configuration
//@EnableSwagger2
@EnableOpenApi
@EnableKnife4j
public class Swagger3Config {

配置类中的swagger类型改为

//        return new Docket(DocumentationType.SWAGGER_2)
        return new Docket(DocumentationType.OAS_30)

浏览器中输入的swagger地址由

http://端口号/swagger-ui.html

改为http://端口号/swagger-ui/ 最后的/别忘了

swagger新增的swagger-bootstrap-ui 可以看这篇文档

http://www.hzhcontrols.com/new-1205817.html

swagger原理解析可以看这篇文档

https://blog.csdn.net/hxpjava1/article/details/78423726/

swagger通过接口生成ts或js代码可以看这篇

https://blog.csdn.net/weixin_44241402/article/details/128964496?utm_source=702048761

本项目文档地址

https://gitee.com/mhjo/swagger/tree/master/src

大家有发现哪里写的谬误敬请指出

  • 20
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值