使用Swagger框架自动生成API接口文档

1、Swagger介绍

随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染变成了前端渲染、前后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远。
前端和后端的唯一联系变成了API接口。API文档变成了前后端开发人员联系的纽带,变得越来越重要。Swagger就是一款让后端开发人员更好的书写API文档的框架。

官方说法:Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。

Swagger的一个最大的优点是能实时同步api与文档。在项目开发过程中,发生过多次:修改代码但是没有更新文档,前端还是按照老旧的文档进行开发,在联调过程中才发现问题的情况(当然依据开闭原则,对接口的修改是不允许的,但是在项目不稳定阶段,这种情况很难避免)。

Swagger的作用:

1)接口文档在线自动生成
2)功能测试

2、引入Swagger框架

    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
      <version>2.9.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
      <version>2.9.2</version>
    </dependency>

3、引入jackson框架

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.9.10</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.10</version>
    </dependency>

4、配置Swagger

package com.cskfz.student.config;

import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
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;

import java.util.stream.Stream;

import static java.util.stream.Collectors.toSet;

/**
 * @Author: yangzc
 * @Description:
 * @Date: Created on 16:47 2019/11/23
 * @Modified By:
 */
@Configuration
@EnableSwagger2
@ComponentScan(basePackages = {"com.cskfz.student.controller"})
public class SwaggerConfig{
    @Bean
    public Docket customDocket(){
        Docket docket = new Docket(DocumentationType.SWAGGER_2);
        return docket.apiInfo(apiInfo()).
                pathMapping("/")
                .select() // 选择那些路径和api会生成document
                .apis(RequestHandlerSelectors.any())// 对所有api进行监控
                //不显示错误的接口地址
                .paths(Predicates.not(PathSelectors.regex("/error.*")))//错误路径不监控
                .paths(PathSelectors.regex("/.*"))// 对根下所有路径进行监控
                .build().protocols(Stream.of("http", "https").collect(toSet()));
    }

    private ApiInfo apiInfo(){
        Contact contact = new Contact("yangzc","https://github.com/yangzc23/","876295854@qq.com");
        return new ApiInfoBuilder().
                title("学生管理API").
                description("学生管理接口说明文档").
                contact(contact).
                version("1.0.0").
                build();
    }

}

5、添加Swagger资源文件的映射

package com.cskfz.student.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Author: yangzc
 * @Description: 资源映射路径
 * @Date: Created on 12:54 2019/11/19
 * @Modified By:
 */
@Configuration
public class MyWebAppConfigurer implements WebMvcConfigurer {
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/upload/**").addResourceLocations("file:D:/upload/");
        registry.addResourceHandler("swagger-ui.html").
                addResourceLocations("classpath:/META-INF/resources/");//swagger的html文件
        registry.addResourceHandler("/webjars/**").
                addResourceLocations("classpath:/META-INF/resources/webjars/");//swagger的js和css文件
    }
}

6、Controller代码

package com.cskfz.student.controller;

import com.cskfz.student.controller.agent.annotation.ApiJsonObject;
import com.cskfz.student.controller.agent.annotation.ApiJsonProperty;
import com.cskfz.student.entity.Student;
import com.cskfz.student.pojo.ActionResult;
import com.cskfz.student.pojo.StudentVO;
import com.cskfz.student.utils.DBUtil;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

/**
 * @Author: yangzc
 * @Description:
 * @Date: Created on 11:39 2019/11/19
 * @Modified By:
 */
@Api(value = "student", description = "学生管理")
@Controller
@RequestMapping("/")
public class MyController {

    @Autowired
    private DBUtil dbUtil;

    @Value("${upload.file.path}")
    private String savePath;

    /**
     * 学生列表
     * @param params
     * @return
     */
    @ApiOperation(value = "学生列表", notes = "根据指定的页码和行数返回学生列表")
    @PostMapping(value = "/welcome", produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public ActionResult getStudents(@ApiJsonObject(name = "params", value = {
            @ApiJsonProperty(key = "page", example = "1", description = "页码"),
            @ApiJsonProperty(key = "rows", example = "5", description = "行数")
    }) @RequestBody Map<String,String> params) {
        //PrintWriter pw = null;
        Connection conn = null;
        PreparedStatement stmt = null;
        //
        Map<String,Object> data = new HashMap<String,Object>();
        List<Student> list = new ArrayList<Student>();
        ActionResult result = null;
        int rows = Integer.parseInt(params.get("rows"));
        int page = Integer.parseInt(params.get("page"));
        int begin = (page-1)*rows;
        try {
            conn = dbUtil.getConnection();
            stmt = conn.prepareStatement("SELECT COUNT(*) FROM STUDENT");
            ResultSet rs = stmt.executeQuery();
            rs.next();
            data.put("total", rs.getInt(1));
            stmt = conn.prepareStatement("SELECT * FROM STUDENT ORDER BY SNO LIMIT ?,?");
            stmt.setInt(1, begin);
            stmt.setInt(2, rows);
            rs = stmt.executeQuery();
            while(rs.next()) {
                list.add(new Student(rs.getInt(1),rs.getString(2),rs.getString(3).equals("男"),rs.getDate(4),rs.getString(5)));
            }
            data.put("rows", list);
            result = ActionResult.ok(data);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                conn.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 学生编辑
     * @param sid
     * @return
     */
    @ApiOperation(value = "学生信息加载", notes = "根据学号获取该学生的信息")
    @ApiImplicitParam(name = "sid", value = "学号", dataType = "int", paramType = "path", example = "1001")
    @GetMapping(value = "/edit/{sid}", produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public ActionResult loadStudent(@PathVariable Integer sid){
        Connection conn = null;
        PreparedStatement stmt = null;
        ActionResult result = null;
        try {
            conn = dbUtil.getConnection();
            stmt = conn.prepareStatement("SELECT * FROM STUDENT WHERE SNO=?");
            stmt.setInt(1, sid);
            ResultSet rs = stmt.executeQuery();
            if(rs.next()) {
                result = ActionResult.ok(new Student(rs.getInt(1),rs.getString(2),rs.getString(3).equals("男"),rs.getDate(4),rs.getString(5)));
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                conn.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return result;
    }


    /**
     * 学生保存
     * @param stu
     * @return
     */
    @ApiOperation(value = "学生信息保存", notes = "将输入的学生信息保存到数据库")
    @PostMapping(value = "/save", produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public ActionResult saveStudent(StudentVO stu){
        Connection conn = null;
        PreparedStatement stmt = null;
        ActionResult result = null;
        String sno = stu.getSid();
        //
        String sname = stu.getSname();
        //
        String gender = stu.getGender().equals("0")?"女":"男";
        //
        String birth = stu.getBirth();
        //
        String filePath = stu.getFilePath();
        //
        try {
            conn = dbUtil.getConnection();
            if(sno==null||sno.equals("")) {
                stmt = conn.prepareStatement("INSERT INTO STUDENT(SNAME,GENDER,BIRTH,PHOTO_URL) VALUES(?,?,?,?)");
                stmt.setString(1, sname);
                stmt.setString(2, gender);
                stmt.setString(3, birth);
                stmt.setString(4, filePath);
            }else {
                stmt = conn.prepareStatement("UPDATE STUDENT SET SNAME=?,GENDER=?,BIRTH=?,PHOTO_URL=? WHERE SNO=?");
                stmt.setString(1, sname);
                stmt.setString(2, gender);
                stmt.setString(3, birth);
                stmt.setString(4, filePath);
                stmt.setInt(5, Integer.parseInt(sno));
            }
            stmt.executeUpdate();
            result = ActionResult.ok();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                conn.close();//关闭数据库的连接
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return result;
    }


    /**
     * 学生删除
     * @param sid
     * @return
     */
    @ApiOperation(value = "删除学生信息", notes = "根据学号删除该学生的信息")
    @ApiImplicitParam(name = "sid", value = "学号", dataType = "int", paramType = "path", example = "1001")
    @DeleteMapping(value = "/delete/{sid}",  produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public ActionResult delStudent(@PathVariable Integer sid){
        Connection conn = null;
        PreparedStatement stmt = null;
        ActionResult result = null;
        try {
            conn = dbUtil.getConnection();
            stmt = conn.prepareStatement("DELETE FROM STUDENT WHERE SNO = ?");
            stmt.setInt(1, sid);
            stmt.executeUpdate();
            result = ActionResult.ok();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                conn.close();//关闭数据库的连接
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return result;
    }


    /**
     * 文件上传
     * @param file
     * @param req
     * @return
     */
    @ApiOperation(value = "头像上传", notes = "文件上传")
    @ApiImplicitParam(name = "source", value = "图片", dataType = "__file", required = true, paramType = "form")
    @PostMapping(value = "/upload/file", produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public ActionResult uploadFile(@RequestParam("source") MultipartFile file, HttpServletRequest req) {
        ActionResult result = null;
        try {
            // 截取不同类型的文件需要自行判断
            String filename = file.getOriginalFilename();
            if (!file.isEmpty()) {
                String extName = filename.substring(filename.indexOf("."));// 取文件格式后缀名
                String uuid = UUID.randomUUID().toString().replace("-", "");
                // 新名称
                String newName = uuid + extName;// 在这里用UUID来生成新的文件夹名字,这样就不会导致重名
                file.transferTo(new File(savePath+"/"+newName));
                result = ActionResult.ok("upload/"+newName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

}

7、访问学生管理的接口文档

在这里插入图片描述

8、使用swagger调用学生管理接口

在这里插入图片描述
在这里插入图片描述

9、github项目地址

https://github.com/yangzc23/yangzc

10、参考资料

[01] 关于通过Swagger查看接口浏览器跳出Unable to infer base url. This is common when using dynamic servlet registrat…
[02] How can I set a description and an example in Swagger with Swagger annotations?
[03] SpringBoot + Swagger + ReDoc 笔记
[04] 如何手动描述java@RequestBodyMap<String,String>的示例输入?
[05] SpringMVC-如何接收各种参数(普通参数,对象,JSON, URL)
[06] swagger常用注解说明
[07] swagger ui swagger2 文件上传参数 input file
[08] AJAX发送GET、POST、DELETE、PUT请求到服务器
[09] springboot2.x集成swagger
[10] @ApiImplicitParams dataType file __ file swagger升级的一些坑
[11] springfox + swagger2自定义JsonObject/Map参数文档
[12] Swagger2 关于Map参数在API文档中展示详细参数以及参数说明
[13] swagger生成接口文档和map类型参数解析
[14] Swagger2.9.2的NumberFormatException
[15] swagger2 Illegal DefaultValue for parameter type integer
[16] Swagger使用总结
[17] Idea修改编译器版本
[18] Springboot 集成Swagger2后 接受实体类对象传参的实现方式
[19] Swagger生成的接口需要权限验证的处理方法
[20] Swagger学习
[21] @requestMapping的produces和consumes属性
[22] 解决访问swaggerUI接口文档显示basic-error-controler问题
[23] SpringBoot 整合 oauth2(三)实现 token 认证
[24] springfox项目github地址
[25] swagger api一键导入postman
[26] 在spring Boot中使用swagger-bootstrap-ui
[27] swagger-ui升级swagger-bootstrap-ui界面好看到起飞
[28] swagger-bootstrap-ui 开发指南

微信扫一扫关注公众号
image.png
点击链接加入群聊

https://jq.qq.com/?_wv=1027&k=5eVEhfN
软件测试学习交流QQ群号:511619105

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值