• Spring Boot 2.X(十五):集成 Swagger2 开发 API 文档(在线+离线)


    前言

    相信很多后端开发在项目中都会碰到要写 api 文档,不管是给前端、移动端等提供更好的对接,还是以后为了以后交接方便,都会要求写 api 文档。

    而手写 api 文档的话有诸多痛点:

    • 文档更新的时候,需要再次发送给对接人
    • 接口太对,手写文档很难管理
    • 接口返回的结果不明确
    • 不能直接在线测试接口,通常需要使用工具,如 postman 等

    Swagger 就很好的解决了这个问题。

    Swagger 简介

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

    官网:https://swagger.io

    Swagger 使用

    1.相关依赖

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

    2.Swagger 配置类

    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
    	@Bean
        public Docket buildDocket() {
            return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(buildApiInf()) //将api的元信息设置为包含在json resourcelisting响应中
            //.host("127.0.0.1:8080") //设置ip和端口,或者域名
            .select()  //启动用于api选择的生成器
            //.apis(RequestHandlerSelectors.any())
            .apis(RequestHandlerSelectors.basePackage("cn.zwqh.springboot.controller"))//指定controller路径
            .paths(PathSelectors.any()).build();
        }
    
        private ApiInfo buildApiInf() {
        	
            Contact contact=new Contact("朝雾轻寒","https://www.zwqh.top/","zwqh@clover1314.com");
            return new ApiInfoBuilder()
            .title("Swagger Demo Restful API Docs")//文档标题
            .description("Swagger 示例 Restful Api 文档")//文档描述
            .contact(contact)//联系人
            .version("1.0")//版本号
            //.license("")//更新此API的许可证信息
            //.licenseUrl("")//更新此API的许可证Url
            //.termsOfServiceUrl("")//更新服务条款URL
            .build();
    
        }
    }
    

    3.Spring MVC 相关配置

    @Configuration
    public class WebMvcConfig extends WebMvcConfigurationSupport {
    	/**
    	 * 静态资源配置(默认)
    	 */
    	@Override
    	public void addResourceHandlers(ResourceHandlerRegistry registry) {
    		registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");// 静态资源路径
    		registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
    		registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    		super.addResourceHandlers(registry);
    	}
    
    
    }
    

    如果不添加此静态资源配置会报错,找不到相关路径

    4.Model 中使用 Swagger 注解

    @ApiModel(value = "UserEntity", description = "用户对象")
    public class UserEntity implements Serializable{
    
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 5237730257103305078L;
    	@ApiModelProperty(value ="用户id",name="id",dataType="Long",required = false,example = "1",hidden = false )
    	private Long id;
    	@ApiModelProperty(value ="用户名",name="userName",dataType="String",required = false,example = "关羽" )
    	private String userName;
    	@ApiModelProperty(value ="用户性别",name="userSex",dataType="String",required = false,example = "男" )
    	private String userSex;
    
    	public Long getId() {
    		return id;
    	}
    
    	public void setId(Long id) {
    		this.id = id;
    	}
    
    	public String getUserName() {
    		return userName;
    	}
    
    	public void setUserName(String userName) {
    		this.userName = userName;
    	}
    
    	public String getUserSex() {
    		return userSex;
    	}
    
    	public void setUserSex(String userSex) {
    		this.userSex = userSex;
    	}
    
    }
    
    

    5. Controller 中使用 Swagger 注解

    
    @RestController
    @RequestMapping("/api")
    @Api(tags = { "接口分组1", "接口分组2" })
    public class ApiController {
    
    	@Autowired
    	private UserDao userDao;
    
    	@GetMapping("/getAllUser")
    	@ApiOperation(value = "获取所有用户", notes = "", httpMethod = "GET", tags = "接口分组3")
    	public List<UserEntity> getAll() {
    		return userDao.getAll();
    	}
    
    	@GetMapping("/getUserById")
    	@ApiOperation(value = "根据id获取用户", notes = "id必传", httpMethod = "GET")
    	@ApiImplicitParam(name = "id", value = "用户id",example = "1", required = true, dataType = "long", paramType = "query")
    	public UserEntity getOne(Long id) {
    		return userDao.getOne(id);
    	}
    
    	@PostMapping("/getUserByNameAndSex")
    	@ApiOperation(value = "根据name和sex获取用户", notes = "", httpMethod = "POST")
    	@ApiImplicitParams({
    			@ApiImplicitParam(name = "userName", value = "用户名", example = "关羽", required = true, dataType = "string", paramType = "query"),
    			@ApiImplicitParam(name = "userSex", value = "用户性别", example = "男", required = true, dataType = "string", paramType = "query") })
    	public UserEntity getUserByNameAndSex(String userName, String userSex) {
    		return userDao.getUserByNameAndSex(userName, userSex);
    	}
    
    	@PostMapping("/insertUser")
    	@ApiOperation(value = "新增用户", notes = "传json,数据放body", httpMethod = "POST")
    	@ApiImplicitParams({
    			@ApiImplicitParam(name = "body", value = "用户对象json", example = "{userName:'朝雾轻寒',userSex:'男'}", required = true) })
    	public String insertUser(@RequestBody String body) {
    		System.out.println(body);
    		UserEntity user = JSON.parseObject(body, UserEntity.class);
    		userDao.insertUser(user);
    		return "{code:0,msg:'success'}";
    	}
    
    	@PostMapping("/updateUser")
    	@ApiOperation(value = "修改用户", notes = "传json,数据放body", httpMethod = "POST")
    	@ApiImplicitParams({
    			@ApiImplicitParam(name = "body", value = "用户对象json", example = "{id:23,userName:'朝雾轻寒',userSex:'女'}", required = true) })
    	public String updateUser(@RequestBody String body) {
    		System.out.println(body);
    		UserEntity user = JSON.parseObject(body, UserEntity.class);
    		userDao.updateUser(user);
    		return "{code:0,msg:'success'}";
    	}
    
    	@PostMapping("/deleteUser")
    	@ApiOperation(value = "删除用户", notes = "id必传", httpMethod = "POST")
    	public String deleteUser(@ApiParam(name = "id", value = "用户id", required = true) Long id) {
    		userDao.deleteUser(id);
    		return "{code:0,msg:'success'}";
    	}
    }
    

    5.测试

    访问 http://127.0.0.1:8080/swagger-ui.html 进行接口在线测试

    Swagger 常用注解

    1.@Api

    用于类,表示标识这个类是swagger的资源。属性如下:

    • tags 表示说明,tags如果有多个值,会生成多个列表
    • value 表示说明,可以使用tags替代

    2.@ApiOperation

    用于方法,表示一个http请求的操作。属性如下:

    • value 用于方法描述
    • notes 用于提示内容
    • tags 用于API文档控制的标记列表,视情况而用,可以进行独立分组

    3.@ApiParam

    用于方法、参数、字段说明;表示对参数的添加元数据。

    • name 参数名
    • value 参数说明
    • required 是否必填

    4.@ApiModel

    用于类,表示对类进行说明,用于参数用实体类接受。

    • value 对象名
    • description 描述

    5.@ApiModelProperty

    用于方法、字段,表示对model属性的说明或者数据操作更改。

    • value 字段说明
    • name 重写属性名
    • dataType 重写属性数据类型
    • required 是否必填
    • example 举例说明
    • hidden 隐藏

    6.@ApiIgnore

    用于类、方法、方法参数,表示这个方法或者类被忽略,不在swagger-ui.html上显示。

    7.@ApiImplicitParam

    用于方法,表示单独的请求参数。

    • name 参数名
    • value 参数说明
    • dataType 数据类型
    • paramType 参数类型
    • example 举例说明

    8.@ApiImplicitParams

    用于方法,包含多个 @ApiImplicitParam。

    9.@ApiResponses @ApiResponse

    用于类或者方法,描述操作的可能响应。

    • code 响应的HTTP状态代码
    • message 响应附带的可读消息

    10.@ResponseHeader

    用于方法,响应头设置。

    • name 响应头名称
    • description 头描述
    • response 默认响应类 void
    • responseContainer 参考ApiOperation中配置

    Swagger 导出离线 api 文档

    1.导出 AsciiDocs、Markdown、Confluence 格式文档

    添加依赖

    <!-- swagger2markup 相关依赖 -->
    		<dependency>
    			<groupId>io.github.swagger2markup</groupId>
    			<artifactId>swagger2markup</artifactId>
    			<version>1.3.3</version>
    		</dependency>
    

    转换工具类

    public class SwaggerUtils {
    
    	private static final String url = "http://127.0.0.1:8080/v2/api-docs";
    	/**
    	 * 生成AsciiDocs格式文档
    	 * @throws MalformedURLException
    	 */
    	public static void generateAsciiDocs() throws MalformedURLException {
    		Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
    				.withMarkupLanguage(MarkupLanguage.ASCIIDOC)
    				.withOutputLanguage(Language.ZH)
    				.withPathsGroupedBy(GroupBy.TAGS)
    				.withGeneratedExamples()
    				.withoutInlineSchema().build();
    
    		Swagger2MarkupConverter.from(new URL(url))
    				.withConfig(config)
    				.build()
    				.toFolder(Paths.get("./docs/asciidoc/generated"));
    	}
    	/**
    	 * 生成AsciiDocs格式文档,并汇总成一个文件
    	 * @throws MalformedURLException
    	 */
    	public static void generateAsciiDocsToFile() throws MalformedURLException {
    		Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
                    .withMarkupLanguage(MarkupLanguage.ASCIIDOC)
                    .withOutputLanguage(Language.ZH)
                    .withPathsGroupedBy(GroupBy.TAGS)
                    .withGeneratedExamples()
                    .withoutInlineSchema()
                    .build();
    
            Swagger2MarkupConverter.from(new URL(url))
                    .withConfig(config)
                    .build()
                    .toFile(Paths.get("./docs/asciidoc/generated/all"));
    	}
    	
    	/**
    	 * 生成Markdown格式文档
    	 * @throws MalformedURLException
    	 */
    	public static void generateMarkdownDocs() throws MalformedURLException {
    		Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
                    .withMarkupLanguage(MarkupLanguage.MARKDOWN)
                    .withOutputLanguage(Language.ZH)
                    .withPathsGroupedBy(GroupBy.TAGS)
                    .withGeneratedExamples()
                    .withoutInlineSchema()
                    .build();
    
            Swagger2MarkupConverter.from(new URL(url))
                    .withConfig(config)
                    .build()
                    .toFolder(Paths.get("./docs/markdown/generated"));
    	}
    	/**
    	 * 生成Markdown格式文档,并汇总成一个文件
    	 * @throws MalformedURLException
    	 */
    	public static void generateMarkdownDocsToFile() throws MalformedURLException {
    		Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
                    .withMarkupLanguage(MarkupLanguage.MARKDOWN)
                    .withOutputLanguage(Language.ZH)
                    .withPathsGroupedBy(GroupBy.TAGS)
                    .withGeneratedExamples()
                    .withoutInlineSchema()
                    .build();
    
            Swagger2MarkupConverter.from(new URL(url))
                    .withConfig(config)
                    .build()
                    .toFile(Paths.get("./docs/markdown/generated/all"));
    	}
    	
    	/**
    	 * 生成Confluence格式文档
    	 * @throws MalformedURLException
    	 */
    	public static void generateConfluenceDocs() throws MalformedURLException {
    		Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
                    .withMarkupLanguage(MarkupLanguage.CONFLUENCE_MARKUP)
                    .withOutputLanguage(Language.ZH)
                    .withPathsGroupedBy(GroupBy.TAGS)
                    .withGeneratedExamples()
                    .withoutInlineSchema()
                    .build();
    
            Swagger2MarkupConverter.from(new URL(url))
                    .withConfig(config)
                    .build()
                    .toFolder(Paths.get("./docs/confluence/generated"));
    	}
    	
    	/**
    	 * 生成Confluence格式文档,并汇总成一个文件
    	 * @throws MalformedURLException
    	 */
    	public static void generateConfluenceDocsToFile() throws MalformedURLException {
    		Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
                    .withMarkupLanguage(MarkupLanguage.CONFLUENCE_MARKUP)
                    .withOutputLanguage(Language.ZH)
                    .withPathsGroupedBy(GroupBy.TAGS)
                    .withGeneratedExamples()
                    .withoutInlineSchema()
                    .build();
    
            Swagger2MarkupConverter.from(new URL(url))
                    .withConfig(config)
                    .build()
                    .toFile(Paths.get("./docs/confluence/generated/all"));
    	}
    	
    	
    }
    

    使用测试 Controller

    @RestController
    @RequestMapping("/export")
    @ApiIgnore
    public class ExportController {
    
    	
    	@RequestMapping("/ascii")
    	public String exportAscii() throws MalformedURLException{
    		SwaggerUtils.generateAsciiDocs();
    		return "success";
    	}
    	
    	@RequestMapping("/asciiToFile")
    	public String asciiToFile() throws MalformedURLException{
    		SwaggerUtils.generateAsciiDocsToFile();
    		return "success";
    	}
    	
    	@RequestMapping("/markdown")
    	public String exportMarkdown() throws MalformedURLException{
    		SwaggerUtils.generateMarkdownDocs();
    		return "success";
    	}
    	
    	@RequestMapping("/markdownToFile")
    	public String exportMarkdownToFile() throws MalformedURLException{
    		SwaggerUtils.generateMarkdownDocsToFile();
    		return "success";
    	}
    	
    	@RequestMapping("/confluence")
    	public String confluence() throws MalformedURLException{
    		SwaggerUtils.generateConfluenceDocs();
    		return "success";
    	}
    	
    	@RequestMapping("/confluenceToFile")
    	public String confluenceToFile() throws MalformedURLException{
    		SwaggerUtils.generateConfluenceDocsToFile();
    		return "success";
    	}
    }
    
    

    2.导出 html、pdf、xml 格式

    添加依赖

    <!--离线文档 -->
    		<dependency>
    			<groupId>org.springframework.restdocs</groupId>
    			<artifactId>spring-restdocs-mockmvc</artifactId>
    			<scope>test</scope>
    		</dependency>
    		<!--springfox-staticdocs 生成静态文档 -->
    		<dependency>
    			<groupId>io.springfox</groupId>
    			<artifactId>springfox-staticdocs</artifactId>
    			<version>2.6.1</version>
    		</dependency>
    
    <build>
    		<pluginManagement>
    			<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://127.0.0.1:8080/v2/api-docs</swaggerInput>
    						<outputDir>./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.3</version>
    					<!-- <version>2.0.0-RC.1</version> -->
    					<!-- Include Asciidoctor PDF for pdf generation -->
    					<dependencies>
    						<dependency>
    							<groupId>org.asciidoctor</groupId>
    							<artifactId>asciidoctorj-pdf</artifactId>
    							<version>1.5.0-alpha.10.1</version>
    						</dependency>
    						<dependency>
    							<groupId>org.jruby</groupId>
    							<artifactId>jruby-complete</artifactId>
    							<version>1.7.21</version>
    						</dependency>
    					</dependencies>
    					<configuration>
    						<sourceDirectory>./docs/asciidoc/generated</sourceDirectory>
    						<outputDirectory>./docs/asciidoc/html</outputDirectory> 
    						<backend>html</backend>
    						<!-- <outputDirectory>./docs/asciidoc/pdf</outputDirectory> 
    						<backend>pdf</backend> -->
    						<headerFooter>true</headerFooter> 
    						<doctype>book</doctype> 
    						<sourceHighlighter>coderay</sourceHighlighter>
    						<attributes>
    							<!-- 菜单栏在左边 -->
    							<toc>left</toc>
    							<!-- 多标题排列 -->
    							<toclevels>3</toclevels>
    							<!-- 自动打数字序号 -->
    							<sectnums>true</sectnums>
    						</attributes>
    					</configuration>					
    				</plugin>
    			</plugins>
    		</pluginManagement>
    
    	</build>
    
    

    可以修改此处 html 和 pdf,通过 mvn asciidoctor:process-asciidoc 可以导出相应格式文件

    <outputDirectory>./docs/asciidoc/html</outputDirectory> 
    						<backend>html</backend>
    

    执行 mvn asciidoctor:process-asciidoc 后再执行 mvn generate-resources,可在 targt/generated-docs 目录下生成 xml 格式文件。

    完整代码

    github

    码云

  • 相关阅读:
    IDEA常用快捷指令整理
    Python dict 字典
    内联函数
    【MFC】编辑框 CEdit Ctrl控件自动换行设置
    mysql 多sql文件恢复方案
    Linux: 用64位的系统,能编译32位的程序吗?
    C++ 遍历数组
    工业现场传感器传感器为什么采用电流形式输出?
    【转】电磁阀、电磁铁的工作原理说明
    PCB板强弱电隔离距离不够导致损坏和问题检查记录
  • 原文地址:https://www.cnblogs.com/zwqh/p/11803318.html
Copyright © 2020-2023  润新知