Swagger2的集成

1.概述

在创建REST API时,良好的文档能极大地提升对外交互的效率。同时,文档必须详细的记录API中的每个细节。这是一项繁琐的工作,好在已经有人提供了各类非常好的自动化工具,Swagger就是其中非常优秀的一款。

今天我们将看一下如何将Swagger 2集成到我们的 Spring REST 服务中在本文中,我们将使用Swagger 2规范的Springfox实现。

如果您不熟悉Swagger,在继续阅读本文之前,您可以到Swagger官方站点了解一下(或者Google一些相关的简介,推荐百度,你懂的)。

2. 添加Maven依赖项

如上所述,我们将使用Swagger规范的Springfox实现。最新版本可在Maven Central上找到 。

要将它添加到我们的Maven项目中,我们需要pom.xml文件中的依赖项。

1
2
3
4
5
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>

3. 将Swagger 2集成到项目中

3.1 Java配置

Swagger的配置主要围绕Docket 来创建,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}

通过@ EnableSwagger2注解启用Swagger 2

定义Docket bean 之后,其select()方法返回ApiSelectorBuilder的一个实例,它提供了一种控制Swagger公开的端点的方法。

可以在RequestHandlerSelectorsPathSelectors的帮助下配置选择RequestHandler的断言,示例中的any()法将通过Swagger提供整个API的文档。

这种配置足以将Swagger 2集成到现有的Spring Boot项目中,对于其他Spring项目,需要进行一些额外的调整。

3.2 非Spring Boot环境下的配置

如果你的项目中没有使用Spring Boot,Swagger UI添加了一组资源,您必须将这些资源配置为扩展WebMvcConfigurerAdapter的类的一部分,并使用@EnableWebMvc进行注解。

1
2
3
4
5
6
7
8
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");

registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}

3.3 验证

要验证Springfox是否正常工作,您可以在浏览器中访问以下URL:

HTTP://本地主机:8080/demo/API/V1/API-文档

结果是具有大量键值对的JSON响应,当然,对于人来说,可读性还是太差,为此 Swagger 为此提供了Swagger UI

4. Swagger UI

Swagger UI是一个内置的解决方案,使用户与Swagger生成的API文档的交互更加容易。

4.1 启用Springfox的Swagger UI

要使用Swagger UI,需要一个额外的Maven依赖项:

1
2
3
4
5
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>

现在,您可以通过访问http://localhost:8080/your-app-root/swagger-ui.html 在浏览器中对其进行测试。

结果应如下所示:

upload successful

4.2 浏览 Swagger 文档

在Swagger中返回的是应用程序中定义的所有控制器的列表。单击其中任何一个将列出有效的HTTP方法(DELETEGETHEADOPTIONSPATCHPOSTPUT)。

扩展每个方法可提供其他有用的数据,例如响应状态,内容类型和参数列表。也可以使用U提供的 try 功能去测试每种方法。

同时Swagger能与您的应用代码实时同步,动态地去获取你所有的 Controller 接口,比如我们添加一个新的 Controller :

1
2
3
4
5
6
7
8
@RestController
public class CustomController {

@RequestMapping(value = "/custom", method = RequestMethod.POST)
public String custom() {
return "custom";
}
}

现在,如果刷新Swagger文档,您将在控制器列表中看到CustomController。同时根据 @RequestMapping(value = “/custom”, method = RequestMethod.POST) 定义,Swagger的响应中只显示了一种方法(POST)。

5. 高级配置

可以配置应用程序的Docket bean,以便更好地控制API文档生成过程。

5.1 过滤Swagger响应的API

有时候你并不希望公开整个API的文档,那么您可以通过将参数传递给Docket类的apis()paths()方法来限制Swagger的响应输出。

如上所示,RequestHandlerSelectors允许使用anynone 断言,但也可以用于根据基础包,类注解和方法注解过滤API。

PathSelectors使用断言提供额外的过滤,这些断言扫描应用程序的请求路径。您可以使用 any()none()regex()ant()

在下面的示例中,我们将使用ant()断言指示Swagger仅包含特定包中的控制器和特定路径。

1
2
3
4
5
6
7
8
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.doleje.web.controller"))
.paths(PathSelectors.ant("/foos/*"))
.build();
}

5.2 自定义信息

Swagger还在其响应中提供了一些默认值,您可以自定义它们,例如“Api文档”,“由联系人电子邮件创建”,“Apache 2.0”,要更改这些值,可以使用apiInfo(ApiInfo apiInfo)方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.controller"))
.paths(PathSelectors.ant("/foos/*"))
.build()
.apiInfo(apiInfo());
}

private ApiInfo apiInfo() {
return new ApiInfo(
"My REST API",
"Some custom description of API.",
"API TOS",
"Terms of service",
new Contact("John Doe", "www.example.com", "myeaddress@company.com"),
"License of API", "API license URL", Collections.emptyList());
}

5.3 自定义方法响应消息

Swagger允许通过DocketglobalResponseMessage()方法全局覆盖HTTP方法的响应消息,要启用这个功能必须指示Swagger不要使用默认响应消息。

假设您希望覆盖所有GET方法的500403响应消息。为此,必须将一些代码添加到Docket的初始化块中:

1
2
3
4
5
6
7
8
9
10
11
.useDefaultResponseMessages(false)                                   
.globalResponseMessage(RequestMethod.GET,
newArrayList(new ResponseMessageBuilder()
.code(500)
.message("500 message")
.responseModel(new ModelRef("Error"))
.build(),
new ResponseMessageBuilder()
.code(403)
.message("Forbidden!")
.build()));

upload successful

6. 使用OAuth保护的API Swagger UI

Swagger UI提供了许多非常有用的功能。但是,如果我们的API受到保护且无法访问,我们就无法真正使用其中的大多数。实际上 Swagger 支持采用OAuth 来保护和授权我们API的安全。

我们将使用SecuritySchemeSecurityContext支持配置Swagger来访问我们的安全API :

1
2
3
4
5
6
7
8
9
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.securitySchemes(Arrays.asList(securityScheme()))
.securityContexts(Arrays.asList(securityContext()));
}

6.1 ecurity Configuration

我们将在Swagger配置中定义一个SecurityConfiguration bean 并设置一些默认值:

1
2
3
4
5
6
7
8
9
@Bean
public SecurityConfiguration security() {
return SecurityConfigurationBuilder.builder()
.clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)
.scopeSeparator(" ")
.useBasicAuthenticationWithAccessCodeGrant(true)
.build();
}

6.2 SecurityScheme

接下来,定义SecurityScheme,这用于描述我们的API如何受到保护(比如基本身份验证,OAuth2或等 )。代码如下所示(这里要用到 OAuth 的相关技术,大家可以参考我其它的关于 SpringSecurity OAuth 的博客):

1
2
3
4
5
6
7
8
9
10
11
12
13
private SecurityScheme securityScheme() {
GrantType grantType = new AuthorizationCodeGrantBuilder()
.tokenEndpoint(new TokenEndpoint(AUTH_SERVER + "/token", "oauthtoken"))
.tokenRequestEndpoint(
new TokenRequestEndpoint(AUTH_SERVER + "/authorize", CLIENT_ID, CLIENT_ID))
.build();

SecurityScheme oauth = new OAuthBuilder().name("spring_oauth")
.grantTypes(Arrays.asList(grantType))
.scopes(Arrays.asList(scopes()))
.build();
return oauth;
}

请注意,我们使用了授权代码授权类型 - 我们需要为其提供令牌端点和OAuth2授权服务器的授权URL。以下是我们需要定义的范围:

1
2
3
4
5
6
7
private AuthorizationScope[] scopes() {
AuthorizationScope[] scopes = {
new AuthorizationScope("read", "for read operations"),
new AuthorizationScope("write", "for write operations"),
new AuthorizationScope("foo", "Access foo API") };
return scopes;
}

这些与/ foos API 在我们的应用程序中实际定义的范围保持一致。

6.3 Security Context

最后,我们需要为示例API定义安全上下文:

1
2
3
4
5
6
7
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(
Arrays.asList(new SecurityReference("spring_oauth", scopes())))
.forPaths(PathSelectors.regex("/foos.*"))
.build();
}

请注意我们在这里使用的名称 - spring_oauth - 与SecurityScheme中我们之前使用的名称保持一致。

6.4 测试

好了,现在我们已经准备好了所有内容,让我们看看我们的Swagger UI并尝试访问Foo API:

我们可以在本地访问Swagger UI:

1
http://localhost:8082/spring-security-oauth-resource/swagger-ui.html

我们可以看到由于我们的安全配置,现在存在一个新的“Authorize”按钮:

upload successful

当我们点击“Authorize”按钮时,我们可以看到以下弹出窗口 - 授权我们的Swagger UI访问安全API:

upload successful

注意:

  • 我们已经可以看到CLIENT_ID和CLIENT_SECRET - 因为我们之前已经预先配置了它们(但我们仍然可以更改它们)
  • 我们现在可以选择我们需要的范围

如下图所示受保护的API标记:

upload successful

现在,最后,我们可以点击我们的API!