使用Springfox使用Spring Rest API设置Swagger 2

2023/05/17

1. 概述

如今,前端和后端组件通常将 Web 应用程序分开。通常,我们将 API 公开为前端组件或第三方应用程序集成的后端组件。

在这种情况下,必须为后端 API 制定适当的规范。同时,API 文档应该信息丰富、易读且易于理解。

此外,参考文档应同时描述 API 中的每个更改。手动完成这项工作是一项乏味的工作,因此该过程的自动化是不可避免的。

在本教程中,我们将使用 Swagger 2 规范的 Springfox 实现来查看 Spring REST Web 服务的 Swagger 2。如果不熟悉 Swagger,请访问其网页以了解更多信息,然后再继续本教程。

值得一提的是,最新版本的 Swagger 规范,现在称为 OpenAPI 3.0,得到了 Springdoc 项目的更好支持,应该用于记录 Spring REST API。

进一步阅读:

使用 Swagger 生成Spring BootREST 客户端

了解如何使用 Swagger 代码生成器生成Spring BootREST 客户端。

阅读更多→

Spring REST 文档简介

本文介绍 Spring REST Docs,这是一种测试驱动的机制,可以为 RESTful 服务生成准确且可读的文档。

阅读更多→

Java 中的 Asciidoctor 简介

了解如何使用 AsciiDoctor 生成文档。

阅读更多→

2. 目标项目

我们将使用的 REST 服务的创建不在本文的讨论范围之内。如果已经有合适的项目,请使用它。如果没有,这些链接是一个很好的起点:

3.添加Maven依赖

如上所述,我们将使用 Swagger 规范的 Springfox 实现。最新版本可以 [在 Maven Central](https://search.maven.org/classic/#search ga 1 “Springfox Swagger2”)上找到。

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

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>3.0.0</version>
</dependency>

3.1。Spring Boot 依赖

对于基于Spring Boot的项目,添加一个springfox-boot-starter依赖项就足够了:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>

我们可以添加我们需要的任何其他启动器,其版本由Spring Boot父级管理:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.0</version>
</dependency>

4. 将 Swagger 2 集成到项目中

4.1。Java 配置

Swagger 的配置主要围绕Docket bean 进行:

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

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

我们可以在RequestHandlerSelectors和PathSelectors的帮助下配置用于选择RequestHandler的谓词。对两者都使用any()将使我们的整个 API 的文档可以通过 Swagger 获得。

4.2. 没有Spring Boot的配置

在普通的 Spring 项目中,我们需要显式启用 Swagger 2。为此,我们必须在配置类上使用@EnableSwagger2WebMvc:

@Configuration
@EnableSwagger2WebMvc
public class SpringFoxConfig {                                    
}

此外,如果没有 Spring Boot,我们就无法自动配置资源处理程序。

Swagger UI 添加了一组资源,我们必须将它们配置为扩展WebMvcConfigurerAdapter 并使用@EnableWebMvc 注解的类的一部分:

@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/");
}

4.3. 确认

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

http://localhost:8080/spring-security-rest/api/v2/api-docs

结果是带有大量键值对的 JSON 响应,人类可读性不强。幸运的是,Swagger为此提供了Swagger UI 。

5. 招摇用户界面

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

5.1。启用 Springfox 的 Swagger UI

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

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

现在我们可以通过访问在浏览器中测试它:

http://localhost:8080/your-app-root/swagger-ui/

顺便说一下,在我们的例子中,确切的 URL 将是:

http://localhost:8080/spring-security-rest/api/swagger-ui/

结果应如下所示:

截图_1

5.2. 探索 Swagger 文档

在 Swagger 的响应中是我们应用程序中定义的所有控制器的列表。单击其中任何一个将列出有效的 HTTP 方法(DELETE、GET、HEAD、OPTIONS、PATCH、POST、PUT)。

扩展每个方法会提供额外的有用数据,例如响应状态、内容类型和参数列表。也可以使用 UI 尝试每种方法。

Swagger 与我们的代码库同步的能力至关重要。为了证明这一点,我们可以向我们的应用程序添加一个新控制器:

@RestController
public class CustomController {

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

现在,如果我们刷新 Swagger 文档,我们会在控制器列表中看到custom- controller。众所周知,Swagger 的响应中只显示了一种方法( POST )。

6. Spring Data REST

Springfox通过其springfox-data-rest库为Spring Data REST提供支持。

如果 Spring Boot在 classpath 上发现spring-boot-starter-data-rest ,它将处理自动配置。

现在让我们创建一个名为User的实体:

@Entity
public class User {
    @Id
    private Long id;
    private String firstName;
    private int age;
    private String email;

    // getters and setters
}

然后我们将创建UserRepository以在User实体上添加 CRUD 操作:

@Repository
public interface UserRepository extends CrudRepository<User, Long> {
}

最后,我们将SpringDataRestConfiguration类导入SpringFoxConfig类:

@EnableSwagger2WebMvc
@Import(SpringDataRestConfiguration.class)
public class SpringFoxConfig {
    //...
}

注意:我们使用@EnableSwagger2WebMvc注解来启用 Swagger,因为它已经替换了库版本 3 中的@EnableSwagger2注解。

让我们重新启动应用程序以生成 Spring Data REST API 的规范:

swagger_user_1

我们可以看到 Springfox使用GET、POST、PUT、PATCH和DELETE等 HTTP 方法生成了User实体的规范。

7. Bean 验证

Springfox 还通过其springfox-bean-validators库支持bean 验证注解。

首先,我们将 Maven 依赖项添加到我们的pom.xml中:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-bean-validators</artifactId>
    <version>2.9.2</version>
</dependency>

同样,如果我们使用 Spring Boot,我们不必显式提供上述依赖项。

接下来,让我们为User实体添加一些验证注解,例如@NotNull和@Min :

@Entity
public class User {
    //...
    
    @NotNull(message = "First Name cannot be null")
    private String firstName;
    
    @Min(value = 15, message = "Age should not be less than 15")
    @Max(value = 65, message = "Age should not be greater than 65")
    private int age;
}

最后,我们将BeanValidatorPluginsConfiguration类导入SpringFoxConfig类:

@EnableSwagger2
@Import(BeanValidatorPluginsConfiguration.class)
public class SpringFoxConfig {
    //...
}

我们来看看API规范的变化:

swagger_user_2

在这里,我们可以观察到User模型在firstName上有 required。此外,还为年龄定义了最小值和最大值。

8. 插件

为了在 API 规范中添加特定的功能,我们可以创建一个 Springfox 插件。插件可以提供各种功能,从丰富模型和属性到自定义 API 列表和默认值。

Springfox 通过其spi模块支持插件创建。spi 模块提供了一些接口,例如ModelBuilderPlugin、ModelPropertyBuilderPlugin和ApiListingBuilderPlugin,它们充当实现自定义插件的可扩展性挂钩。

为了演示这些功能,让我们创建一个插件来丰富User模型的email属性。我们将使用ModelPropertyBuilderPlugin接口并设置模式和示例的值。

首先,让我们创建EmailAnnotationPlugin类并重写support 方法以允许任何文档类型,例如 Swagger 1.2 和 Swagger 2:

@Component
@Order(Validators.BEAN_VALIDATOR_PLUGIN_ORDER)
public class EmailAnnotationPlugin implements ModelPropertyBuilderPlugin {
    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }
}

然后我们将覆盖ModelPropertyBuilderPlugin的apply方法来设置构建器属性的值:

@Override
public void apply(ModelPropertyContext context) {
    Optional<Email> email = annotationFromBean(context, Email.class);
     if (email.isPresent()) {
        context.getSpecificationBuilder().facetBuilder(StringElementFacetBuilder.class)
          .pattern(email.get().regexp());
        context.getSpecificationBuilder().example("email@email.com");
    }
}

因此,API 规范将显示带有@Email注解的属性的模式和示例值。

接下来,我们将@Email注解添加到User实体:

@Entity
public class User {
    //...

    @Email(regexp=".@...", message = "Email should be valid")
    private String email;
}

最后,我们将通过注册为 bean 来启用 SpringFoxConfig 类中的 EmailAnnotationPlugin :

@Import({BeanValidatorPluginsConfiguration.class})
public class SpringFoxConfig {
    //...

    @Bean
    public EmailAnnotationPlugin emailPlugin() {
        return new EmailAnnotationPlugin();
    }
}

让我们看看正在运行的EmailAnnotationPlugin:

swagger_user_3

我们可以从User实体的email属性中看到该模式的值是相同的正则表达式 (.@…) 。

同样,示例( email@email.com ) 的值与EmailAnnotationPlugin的apply方法中定义的值相同。

9. 高级配置

我们的应用程序的Docket bean 可以配置为让我们更好地控制 API 文档生成过程。

9.1。过滤 Swagger 响应的 API

公开整个 API 的文档并不总是可取的。我们可以通过向Docket类的apis()和paths()方法传递参数来限制 Swagger 的响应。

如上所示,RequestHandlerSelectors允许使用any或none谓词,但也可用于根据基包、类注解和方法注解过滤 API。

PathSelectors使用谓词提供额外的过滤,它扫描我们应用程序的请求路径。我们可以使用any()、 none() 、regex()或ant()。

在下面的示例中,我们将使用ant()谓词指示 Swagger 仅包含来自特定包的具有特定路径的控制器:

@Bean
public Docket api() {                
    return new Docket(DocumentationType.SWAGGER_2)          
      .select()                                       
      .apis(RequestHandlerSelectors.basePackage("com.baeldung.web.controller"))
      .paths(PathSelectors.ant("/foos/"))                     
      .build();
}

9.2. 自定义信息

Swagger 在其响应中也提供了一些默认值,我们可以自定义,例如“Api Documentation”、“Created by Contact Email”和“Apache 2.0”。

要更改这些值,我们可以使用apiInfo(ApiInfo apiInfo)方法 —包含有关 API 的自定义信息的ApiInfo类:

@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());
}

9.3. 自定义方法响应消息

Swagger 允许通过Docket的globalResponses()方法全局覆盖 HTTP 方法的响应消息。

首先,我们需要指示 Swagger 不要使用默认响应消息。假设我们要覆盖所有GET方法的500和403响应消息。

为此,必须在Docket的初始化块中添加一些代码(为清楚起见,排除了原始代码):

.useDefaultResponseMessages(false)
.globalResponses(HttpMethod.GET, newArrayList(
    new ResponseBuilder().code("500")
        .description("500 message").build(),
    new ResponseBuilder().code("403")
        .description("Forbidden!!!!!").build()
));

截图_2

10. 带有 OAuth 安全 API 的 Swagger UI

Swagger UI 提供了许多非常有用的功能,到目前为止我们已经很好地介绍了这些功能。但是如果我们的 API 是安全的并且不可访问,我们就不能真正使用其中的大部分。

让我们看看如何允许 Swagger 在此示例中使用授权代码授权类型访问受 OAuth 保护的 API。

我们将配置 Swagger 以使用SecurityScheme和SecurityContext支持访问我们的安全 API:

@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()));
}

10.1。安全配置

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

@Bean
public SecurityConfiguration security() {
    return SecurityConfigurationBuilder.builder()
        .clientId(CLIENT_ID)
        .clientSecret(CLIENT_SECRET)
        .scopeSeparator(" ")
        .useBasicAuthenticationWithAccessCodeGrant(true)
        .build();
}

10.2. 安全计划

接下来,我们将定义我们的SecurityScheme;这用于描述我们的 API 是如何保护的(基本身份验证、OAuth2 ……)。

在我们这里的例子中,我们将定义一个用于保护我们的资源服务器的 OAuth 方案:

private SecurityScheme securityScheme() {
    GrantType grantType = new AuthorizationCodeGrantBuilder()
        .tokenEndpoint(new TokenEndpoint(AUTH_SERVER + "/token", "oauthtoken"))
        .tokenRequestEndpoint(
          new TokenRequestEndpoint(AUTH_SERVER + "/authorize", CLIENT_ID, CLIENT_SECRET))
        .build();

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

请注意,我们使用了授权代码授权类型,我们需要为此提供令牌端点和 OAuth2 授权服务器的授权 URL。

以下是我们需要定义的范围:

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 定义的范围同步。

10.3. 安全上下文

最后,我们需要为示例 API定义一个SecurityContext :

private SecurityContext securityContext() {
    return SecurityContext.builder()
      .securityReferences(
        Arrays.asList(new SecurityReference("spring_oauth", scopes())))
      .forPaths(PathSelectors.regex("/foos."))
      .build();
}

请注意我们在参考中使用的名称 — spring_oauth — 如何与我们之前在SecurityScheme中使用的名称同步。

10.4. 测试

现在我们已经准备好一切准备就绪,让我们看看我们的 Swagger UI 并尝试访问 Foo API。

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

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

正如我们所见,由于我们的安全配置,现在存在一个新的 Authorize 按钮:

招摇_1

当我们单击 Authorize 按钮时,我们可以看到以下弹出窗口来授权我们的 Swagger UI 访问受保护的 API:

招摇_2

注意:

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

以下是安全 API 的标记方式:

招摇_3

现在,终于,我们可以使用我们的 API 了!

当然,几乎不用说我们需要小心我们如何在外部公开 Swagger UI,因为这个安全配置是活动的。

11. 总结

在本文中,我们设置 Swagger 2 来为 Spring REST API 生成文档。我们还探索了可视化和自定义 Swagger 输出的方法。最后,我们查看了 Swagger 的简单 OAuth 配置。

本教程的完整实现可以在GitHub 项目中找到。要查看引导项目中的设置,请查看此 GitHub 模块。

对于 OAuth 部分,代码在我们的spring-security-oauth 存储库中可用。

如果是REST With Spring的学生,请转到模块 7 的第 1 课,深入了解如何使用 Spring 和Spring Boot设置 Swagger。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章