Swagger

一、描述

现代化的研发 组织 架构中,一个研发团队基本包括了 产品组、后端组、前端组、APP端研发、 测试组、 UI 组等,各个细分组织人员各司其职,共同完成产品的全周期工作。如何进行组织架构内的有效高效沟通就显得尤其重要。其中,如何构建一份合理高效的接口文档更显重要。 随着互联网技术的发展,现在的网站架构基本都由原来的后端变成前后端分离。前后端的唯一联系,是通过API接口;API文档变成了前后端开发人员联系的纽带,变得越来越重要。

二、API文档

解决方案:手写文档完成前后端开发

开发文档手册:

https://wenku.baidu.com/view/f88529d185868762caaedd3383c4bb4cf6ecb707.html

手写文档存在的问题

  • 文档需要更新的时候,需要再次发送一份给前端,也就是文档更新交流不及时。
  • 接口返回结果不明确
  • 不能直接在线测试接口,通常需要使用工具,比如:Postman
  • 接口文档太多,不好进行维护管理
  • 文档是接口提供方手动导入的,是静态文档,没有提供接口测试功能

三、 Swagger

1、简介

官网:https://swagger.io/ Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。 Swagger 也就是为了解决这个问题,当然也不能说 Swagger 就一定是完美的,当然也有缺点,最明显的就是代码植入性比较强。

作用:

1. 接口的文档在线自动生成。

2. 功能测试。

2、Swagger组件

Swagger是一组开源项目,其中主要要项目如下:

  1. Swagger-tools:提供各种与Swagger进行集成和交互的工具。例如模式检验、Swagger 1.2文档转换成Swagger 2.0文档等功能。
  2. Swagger-core: 用于Java/Scala的的Swagger实现。与JAX-RS(Jersey、Resteasy、CXF...)、Servlets和Play框架进行集成。
  3. Swagger-js: 用于JavaScript的Swagger实现。
  4. Swagger-node-express: Swagger模块,用于node.js的Express web应用框架。
  5. Swagger-ui:一个无依赖的HTML、JS和CSS集合,可以为Swagger兼容API动态生成优雅文档。
  6. Swagger-codegen:一个模板驱动引擎,通过分析用户Swagger资源声明以各种语言生成客户端代码。

3、Swagger集成

增加 Swagger2 所需依赖

<!-- Swagger2 Begin -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.8.0</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.8.0</version>
</dependency>
<!-- Swagger2 End -->

创建Swagger2配置类

/**
 * @ClassName SwaggerConfig
 * author wangpengcheng
 */

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * Swagger2配置类 基于SpringBoot
 * 通过@Configuration注解,让Spring来加载该类配置。
 * 再通过@EnableSwagger2注解来启用Swagger2。
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig {

    /**
     * 创建API应用
     * apiInfo() 增加API相关信息
     * 通过select()函数返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现,
     * 本例采用指定扫描的包路径来定义指定要建立API的目录。
     *
     * @return
     */
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.wpc.springbootssm.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    /**
     * 创建该API的基本信息(这些基本信息会展现在文档页面中)
     * 访问地址:http://项目实际地址/swagger-ui.html
     *
     * @return
     */
    private ApiInfo apiInfo() {

//联系人基本信息
        Contact contact = new Contact("wangpengcheng", "url", "pengcheng965@163.com");

        return new ApiInfoBuilder()
                .title("会议达人 --Swagger2构建API文档")
                .description("更多请关注")
                .termsOfServiceUrl("http://localhost:8081/swagger-ui.html")
                .contact(contact)
                .version("1.0")
                .build();
    }
}

查看swagger2

完成上述代码添加,启动Spring Boot程序

访问:http://localhost:8081/springboot/swagger-ui.html

(有应用名需要加上相应的应用名)

API项目架构拓扑图 构建api_spring

四、swagger2注解说明

代码的植入性(侵入性)比较强。

注解:

Controller JavaBean

1 、类描述

@Api:用在请求的类上,表示对类的说明 tags="说明该类的作用,可以在UI界面上看到的注解" value="该参数没什么意义,在UI界面上也看到,所以不需要配置"

@Controller @RequestMapping("user") @Api(tags = {"用户列表","用户列表控制类"})

2、 方法描述

@ApiOperation:用在请求的方法上,说明方法的用途、作用

value="说明方法的用途、作用" notes="方法的备注说明"

 @ApiOperation(value = "查询用户",notes = "根据用户的请求ID查询用户信息")

3、方法参数描述

@ApiImplicitParams:用在请求的方法上,表示一组参数说明

@ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面 name:参数名 value:参数的汉字说明、解释 required:参数是否必须传 paramType:参数放在哪个地方 · header --> 请求参数的获取:@RequestHeader · query --> 请求参数的获取:@RequestParam · path(用于restful接口)-->请求参数的获取:@PathVariable [实战中建议不加,用默认] · body(不常用) · form(不常用) dataType:参数类型,默认String,其它值dataType="Integer"

defaultValue:参数的默认值


@ApiOperation(value = "添加用户",notes = "添加用户信息")
@ApiImplicitParams({
    @ApiImplicitParam(name = "name", value = "姓名", required = true, paramType = "query", dataType = "String"),
    @ApiImplicitParam(name ="telphone",value ="手机号",required = true,paramType ="query",dataType = "String"),
    @ApiImplicitParam(name ="status",value ="状态",required = false,paramType ="query",defaultValue = "0",dataType = "String")
})
@RequestMapping(value ="add",method = RequestMethod.PUT)

public String add(User user,Model model){

}

4、方法响应参数

@ApiResponses:用在请求的方法上,表示一组响应

@ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息 code:数字,例如400 message:信息,例如"请求参数没填好" response:抛出异常的类

@ApiResponses({
           @ApiResponse(code = 400,message = "客户端请求参数填写异常"),
           @ApiResponse(code = 404,message = "您所请求的资源无法找到")
   })

完整 代码如下:

@ApiOperation(value="获取用户详细信息", notes="根据url的id来获取用户详细信息")
   @ApiImplicitParams({
           @ApiImplicitParam(name = "id", value = "用户ID", required = true,dataType = "String")
   })
   @ApiResponses({
           @ApiResponse(code = 400,message = "客户端请求参数填写异常"),
           @ApiResponse(code = 404,message = "您所请求的资源无法找到")
   })
    @RequestMapping(value = "{id}", method = RequestMethod.GET)  // user/id
    public ResponseEntity<ResultJson<User>> getUserById (@PathVariable(value = "id")  Integer id) {

        User user = userService.selectByPrimaryKey(id);
        ResultJson<User>  resultJson=null;
        if(user!=null){
            resultJson=new ResultJson<>(200,"success",user);
        }else{
            resultJson=new ResultJson<>(40013,"invalid id",user);
        }
        ResponseEntity re=new ResponseEntity(resultJson, HttpStatus.OK);
        return re;
    }
 
五、自

定义返回码

1、简介:

模仿 微信开发者文档API调用 示例:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839

2、自定义创建类

ResultJson

package com.wpc.util.resulst;

import lombok.Data;

@Data
public class ResultJson<T> {

    /*响应码*/
    private String code;
    /*消息提示内容文件*/
    private String msg;
    /*返回指定对象*/
    private T data;

    /** 成功的方法*/
    public ResultJson(T t) {
        this.setCode(ResultCode.SUCCESS.getCode());
        this.setMsg(ResultCode.SUCCESS.getMsg());
        this.setData(t);
    }
    /**已有的ResultCode 进行返回*/
    public ResultJson(T t,ResultCode code){
        this.setCode(code.getCode());
        this.setMsg(code.getMsg());
        this.setData(t);
    }
    /** 完全自定义返回 */
    public ResultJson(T t,String code,String message){
        this.setCode(code);
        this.setMsg(message);
        this.setData(t);
    }

}

常量工具类:

ResultCode

package com.wpc.util.resulst;


public enum  ResultCode {
    //##########TODO 请求成功 2**
    /** 成功 */
    SUCCESS("2000", "成功"),

    /** 操作失败 */
    FAIL("2001", "操作失败"),

    /** 数据已存在 */
    SUCCESS_IS_HAVE("2002", "数据已存在"),
    /** 没有结果 */
    NOT_DATA("2003", "没有结果"),

    //##########TODO 客户端错误 4**
    /** 没有登录 */
    NOT_LOGIN("4000", "没有登录"),

    /** 发生异常 */
    EXCEPTION("4001", "发生异常"),

    /** 系统错误 */
    SYS_ERROR("4002", "系统错误"),

    /** 参数错误 */
    PARAMS_ERROR("4003", "参数错误 "),

    /** 不支持或已经废弃 */
    NOT_SUPPORTED("4004", "不支持或已经废弃"),

    /** AuthCode错误 */
    INVALID_AUTHCODE("4005", "无效的AuthCode"),

    /** 太频繁的调用 */
    TOO_FREQUENT("4006", "太频繁的调用"),

    /** 未知的错误 */
    UNKNOWN_ERROR("4007", "未知错误"),

    /** 未设置方法 */
    NOT_METHOD("4008", "未设置方法");

    //##########TODO 服务器错误 5**

    private ResultCode(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    /**对应状态码*/
    private String code;
    /**返回内容*/
    private String msg;

    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
}

3、代码调用示例

@RequestMapping(value = "r/{id}",method = RequestMethod.GET)  // meetingpub/r/id
    public ResultJson selectMeetingpubById1(@PathVariable("id") final String id){
        Meetingpub meetingpub=meetingpubService.selectByPrimaryKey(id);
        if (meetingpub==null){
           return ResultJson.failed("用户ID不存在"); //失败后调用
        }
        return ResultJson.ok(meetingpub);  //成功后调用
    }

使用spring ResponseEntity处理http响应

使用Spring-ResponseEntity可以响应json格式的数据,非常方便

ResponseEntity标识整个http相应:状态码、头部信息以及相应体内容。因此我们可以使用其对http响应实现完整配置。

4、代码API调用示例:

@RequestMapping(value = "{id}", method = RequestMethod.GET)  // user/id
    public ResponseEntity<ResultJson<User>> getUserById (@PathVariable(value = "id")          Integer id) {

        User user = userService.selectByPrimaryKey(id);
        ResultJson<User>  resultJson=null;
        if(user!=null){
            resultJson=new ResultJson<>(200,"success",user);
        }else{
            resultJson=new ResultJson<>(40013,"invalid id",user);
        }
        ResponseEntity re=new ResponseEntity(resultJson, HttpStatus.OK);
        return re;
    }

5、调用显示返回JSON:

http://localhost:8080/user/1

成功数据:

{"code":200,"message":"success","data":{"id":1,"name":"1","telphone":"123","status":1}}

失败数据:

{"code":40013,"message":"invalid id","data":null}

尽管ResponseEntity非常强大,但不应该过度使用。在一些简单情况下,还有其他方法能满足我们的需求,使代码更整洁。

替代方法 @ResponseBody 典型spring mvc应用,请求点通常返回html页面。有时我们仅需要实际数据,如使用ajax请求。这时我们能通过@ResponseBody注解标记请求处理方法,审批人能够处理方法结果值作为http响应体。

@ResponseStatus 当请求点成功返回,spring提供http 200(ok)相应。如果请求点抛出异常,spring查找异常处理器,由其返回相应的http状态码。对这些方法增加@ResponseStatus注解,spring会返回自定义http状态码。

6、其它方案:

使用 Spring REST Docs 创建 REST 服务文档

简介:

Spring REST Docs 是一个为 Spring 项目生成 API 文档的框架,它通过在单元测试中额外添加 API 信息描述,从而自动生成对应的文档片段。