欢迎访问宙启技术站
智能推送

springmvc实现restful api版本控制并兼容swagger的方法

发布时间:2023-05-17 07:12:43

一、Restful API版本控制

1、版本控制的概述

RESTful API版本控制是一种常见的API管理方式,主要是为了在API更新过程中不破坏已有的API接口,避免数据的不兼容或API过时而导致使用者的不满,通常会结合请求头中的Accept或者X-Version参数来标识当前请求的API版本。

2、版本控制的方案

(1)URL 方案

利用URL的路径来标识版本号,如api.example.com/v1/xxx。

优点: 相对简单易懂,扩展性好。

缺点: 如果后续需要调整版本控制,会存在大量URL重构的情况。

(2)参数方案

利用查询字符串参数或请求头来标识版本号,如api.example.com/xxx?v=1。

优点: 相对简单,易于控制版本和参数。

缺点: 在接口设计上需要规定好参数名称和取值,容易与业务参数混淆。

(3)Header 方案

利用请求头中的Accept或X-Version参数来标识版本号,如Accept: application/json; version=1。

优点: 与业务参数完全分离,易于设计;容易扩展多个版本,不需要URL重构,兼容http1.1和http2等多种协议。

缺点: 在接口文档中需要明确说明API版本和请求头的使用说明。

二、SpringMVC集成Swagger

1、Swagger的简介

Swagger是一个API文档自动生成工具,可以通过注解的方式将API规范描述和文档生成在一起,支持多种语言和多种框架的集成,可以自动生成HTML、Markdown和PDF等多种格式的文档。

2、SpringMVC集成Swagger的方法

(1)添加Swagger依赖

在pom.xml文件中添加如下依赖:

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

(2)创建Swagger配置类

创建SwaggerConfig类,并添加如下注解:

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()
                .apiInfo(apiInfo());
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("接口文档")
                .version("1.0")
                .build();
    }
}

上述配置会扫描所有的请求,生成文档,在访问http://host:port/swagger-ui.html 或者http://host:port/v2/api-docs 可以看到swagger的界面和API接口的文档描述。

(3)增加接口注解

在需要生成文档的方法上添加@Api注解、@ApiOperation注解、@ApiImplicitParam注解等,其中@Api用来描述整个接口,@ApiOperation用来描述接口的方法,@ApiImplicitParam用来描述接口参数。

@Api(value = "登录", tags = "用户")
@RestController
@RequestMapping("/api/user")
public class UserController {
    @ApiOperation(value = "用户登录", notes = "用户登录接口")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "username", value = "用户名", required = true),
            @ApiImplicitParam(paramType = "query", name = "password", value = "密码", required = true)
    })
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public Result login(String username, String password) {
        return userService.login(username, password);
    }
}

(4)版本号处理

在Swagger提供的上述配置中,并未处理版本号的问题,需要自行定制处理,在上述SwaggerConfig中添加如下代码:

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
                .paths(PathSelectors.any())
                .build()
                .apiInfo(apiInfo())
                .groupName("user")
                .directModelSubstitute(Date.class, String.class)
                .globalOperationParameters(createGlobalParam())
                .useDefaultResponseMessages(false)
                .pathMapping("/api/{version}")
                .enableUrlTemplating(false)
                .ignoredParameterTypes(HttpServletResponse.class)
                .genericModelSubstitutes(ResponseEntity.class)
                .securitySchemes(Collections.singletonList(apiKey()))
                .securityContexts(Collections.singletonList(securityContext()))
                .tags(new Tag("登录", "用户相关接口"))
                .enable(true);
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("API文档")
                .description("API文档")
                .version("1.0")
                .contact(new Contact("项目名称", "http://www.baidu.com","xxx@163.com"))
                .build();
    }

    private ApiKey apiKey() {
        return new ApiKey("token", "token", "header");
    }

    public List<Parameter> createGlobalParam() {
        List<Parameter> globalParam = new ArrayList<Parameter>();
        globalParam.add(new ParameterBuilder()
                .name("version")
                .description("API版本,默认为v1")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .defaultValue("v1")
                .build());
        return globalParam;
    }

    private SecurityContext securityContext() {
        return SecurityContext.builder()
                .securityReferences(Collections.singletonList(new SecurityReference("token", new AuthorizationScope[]{new AuthorizationScope("global", "accessEverything")})))
                .forPaths(PathSelectors.any())
                .build();
    }
}

上述配置中,增加了pathMapping()方法,将版本号写死为/api/v1,同时通过createGlobalParam()添加了版本号的请求头,并默认为v1。需要注意的是,由于Swagger2使用了WebMvcConfigurerAdapter来加载,如果同时使用了SpringMVC等其他框架,可能会产生冲突,导致全局异常处理等无法正常工作。

参考资料:

Springfox Documentation

官方GitHub

《Spring Cloud微服务实战》

《Spring Cloud与Docker微服务架构实战》

《深入浅出Spring Boot 2.x》

《深入浅出Spring Cloud》

《Spring Boot编程思想》

思否开发者社区