转载

使用Spring Boot和Swagger进行API优先开发 - reflectoring.io

遵循API优先方法,我们在开始编码之前先指定一个API。通过API描述语言,团队可以进行协作而无需执行任何操作。

使用OpenAPI,我们可以创建一个API规范,我们可以在团队之间共享以交流合同。OpenAPI Maven插件使我们可以根据这样的规范为Spring Boot生成样板代码,因此我们只需要自己实现业务逻辑即可。

这些描述语言指定了端点,安全性模式,对象模式等。而且,大多数时候我们也可以生成这样的规范代码。通常,API规范也成为该API的文档。

您可以在 GitHub上 浏览示例代码。

API优先的好处

要开始进行组件或系统之间的集成,团队需要签订合同。在我们的案例中,合同是API规范。API-first帮助团队之间相互通信,而无需实现任何事情。它还使团队可以并行工作。

API优先方法的亮点在于构建更好的API。仅关注需要提供的功能。简约的API意味着需要维护的代码更少。

使用Swagger编辑器创建API规范

让我们在YAML文档中创建自己的OpenAPI规范。为了更容易理解,我们将讨论分为正在创建的YAML文档的各个部分。如果您想了解有关OpenAPI规范的更多详细信息,可以访问 Github存储库 。

我们从文档顶部的一些有关API的常规信息开始:

openapi: 3.0.2
info:
  title: Reflectoring
  description: <font>"Tutorials on Spring Boot and Java."</font><font>
  termsOfService: http:</font><font><i>//swagger.io/terms/</i></font><font>
  contact:
    email: petros.stergioulas94@gmail.com
  license:
    name: Apache 2.0
    url: http:</font><font><i>//www.apache.org/licenses/LICENSE-2.0.html</i></font><font>
  version: 0.0.1-SNAPSHOT
externalDocs:
  description: Find out more about Reflectoring
  url: https:</font><font><i>//reflectoring.io/about/</i></font><font>
servers:
- url: https:</font><font><i>//reflectoring.swagger.io/v2</i></font><font>
</font>

openapi 字段允许我们定义文档遵循的OpenAPI规范的版本。

在这一 info 部分中,我们添加了有关API的一些信息。这些字段应该是不言自明的。

最后,在本 servers 节中,我们提供了实现API的服务器列表。

然后是关于我们的API的一些其他元数据:

tags:
- name: user
  description: Operations about user
  externalDocs:
    description: Find out more about our store
    url: http:<font><i>//swagger.io</i></font><font>
</font>

tags 部分提供了其他元数据的字段,我们可以使用这些字段使我们的API更具可读性并易于遵循。我们可以添加多个标签,但是每个标签都应该是唯一的。

接下来,我们将描述一些路径。路径保存有关单个端点及其操作的信息:

paths:
  /user/{username}:
    get:
      tags:
      - user
      summary: Get user by user name
      operationId: getUserByName
      parameters:
      - name: username
        in: path
        description: 'The name that needs to be fetched. '
        required: <b>true</b>
        schema:
          type: string
      responses:
        200:
          description: successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        404:
          description: User not found
          content: {}

$ref 字段允许我们引用自定义模式中的对象。在这种情况下,我们指的是User架构对象(请参阅下一节有关 Components )。summary是该操作的简短说明。使用 operationId ,我们可以为操作定义唯一的标识符。我们可以将其视为我们的方法名称。最后, responses 对象允许我们定义操作的结果。我们必须为任何操作调用至少定义一个成功的响应代码。

组件

本components节中全部介绍了API的对象。除非我们从组件对象外部的属性中明确引用了它们,否则在组件对象中定义的对象将不会影响API,如上所述:

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
          format: <b>int</b>64
        username:
          type: string
        firstName:
          type: string
        ... more attributes
        userStatus:
          type: integer
          description: User Status
          format: <b>int</b>32
  securitySchemes:
    reflectoring_auth:
      type: oauth2
      flows:
        implicit:
          authorizationUrl: http:<font><i>//reflectoring.swagger.io/oauth/dialog</i></font><font>
          scopes:
            write:users: modify users
            read:users: read users
    api_key:
      type: apiKey
      name: api_key
      in: header
</font>

schemas 部分允许我们定义要在API中使用的对象。在本 securitySchemes 节中,我们可以定义操作可以使用的安全性方案。有两种使用安全方案的可能方法。

首先,我们可以使用security字段将安全方案添加到特定操作:

paths:
  /user/{username}:
    get:
      tags:
      - user
      summary: Get user by user name
      security: 
        - api_key: []

在上面的示例中,我们明确指定使用api_key我们上面定义的方案保护路径/ user / {username} 。

但是,如果我们要在整个项目中应用安全性,则只需将其指定为顶级字段即可:

paths:
  /user/{username}:
    get:
      tags:
      - user
      summary: Get user by user name
security: 
  - api_key: []

现在,我们的所有路径都应通过该api_key方案来保证。

从API规范生成代码

定义了API之后,我们现在将根据 上面 的 YAML文档 创建代码。

我们将研究两种不同的生成代码的方法:

  • 使用 Swagger编辑器 手动生成代码,以及
  • 使用 OpenAPI Maven插件 从Maven构建生成代码。

1.从Swagger编辑器生成代码

尽管这是我不会采用的方法,但让我们讨论一下并讨论为什么我认为这是一个坏主意。

让我们转到Swagger Editor,然后将我们的YAML文件粘贴到其中。然后,我们从菜单中选择“ 生成服务器”,然后选择我们要生成哪种服务器(我使用“ Spring”)。

那么,为什么这是个坏主意呢?

首先,为我生成的代码是使用Java 7和Spring Boot 1.5.22,它们都已经过时了。

其次,如果我们对规范进行更改(并且更改始终在发生),我们将不得不复制并粘贴手动更改的文件。

2.使用OpenAPI Maven插件生成代码

更好的替代方法是使用OpenAPI Maven插件从Maven构建中生成代码。

让我们看一下文件夹结构。我选择使用一个多模块Maven项目,其中有两个项目:

  • app,它是根据我们的规范实现API的应用程序。
  • specification,其唯一的工作就是为我们的应用提供API规范。

文件夹结构如下所示:

spring-boot-openapi
├── app
│   └── pom.xml
│   └── src
│       └── main
│           └── java
│               └── io.reflectoring
│                   └── OpenAPIConsumerApp.java
├── specification
│   └── pom.xml
│   └── src
│       └── resources
│           └── openapi.yml
└── pom.xml

为了简单起见,我们省略了测试文件夹。

我们app是一个简单的Spring启动的项目,我们可以自动生成上 start.spring.io ,让我们着眼于pom.xml从specification模块,在那里我们配置的OpenAPI Maven插件:

<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>4.2.3</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <inputSpec>
                  ${project.basedir}/src/main/resources/openapi.yml
                </inputSpec>
                <generatorName>spring</generatorName>
                <apiPackage>io.reflectoring.api</apiPackage>
                <modelPackage>io.reflectoring.model</modelPackage>
                <supportingFilesToGenerate>
                  ApiUtil.java
                </supportingFilesToGenerate>
                <configOptions>
                    <delegatePattern><b>true</b></delegatePattern>
                </configOptions>
            </configuration>
        </execution>
    </executions>
</plugin>

您可以 在GitHub上 查看完整pom.xml文件。

在本教程中,我们使用spring生成器。

简单地运行命令./mvnw install将生成实现我们的OpenAPI规范的代码!

查看文件夹target/generated-sources/openapi/src/main/java/io/reflectoring/model,我们找到了User在YAML中定义的模型的代码:

@javax.annotation.Generated(...)
<b>public</b> <b>class</b> User   {
  @JsonProperty(<font>"id"</font><font>)
  <b>private</b> Long id;

  @JsonProperty(</font><font>"username"</font><font>)
  <b>private</b> String username;

  @JsonProperty(</font><font>"firstName"</font><font>)
  <b>private</b> String firstName;
  
  </font><font><i>// ... more properties</i></font><font>

  @JsonProperty(</font><font>"userStatus"</font><font>)
  <b>private</b> Integer userStatus;

  </font><font><i>// ... getters and setters</i></font><font>

}
</font>

生成器不仅生成模型,还生成端点。让我们快速看一下我们生成的内容:

<b>public</b> <b>interface</b> UserApiDelegate {

    <b>default</b> Optional<NativeWebRequest> getRequest() {
        <b>return</b> Optional.empty();
    }

    <font><i>/**
     * POST /user : Create user
     * Create user functionality
     *
     * @param body Created user object (required)
     * @return successful operation (status code 200)
     * @see UserApi#createUser
     */</i></font><font>
    <b>default</b> ResponseEntity<Void> createUser(User body) {
        <b>return</b> <b>new</b> ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);

    }
  </font><font><i>// ... omit deleteUser, getUserByName and updateUser</i></font><font>
}
</font>

当然,生成器无法为我们生成我们的业务逻辑,但是它确实会UserApiDelegate为我们实现上述接口。

它还创建一个UserApi接口,将调用委托给UserApiDelegate:

@Validated
@Api(value = <font>"user"</font><font>, description = </font><font>"the user API"</font><font>)
<b>public</b> <b>interface</b> UserApi {

    <b>default</b> UserApiDelegate getDelegate() {
        <b>return</b> <b>new</b> UserApiDelegate() {};
    }

    </font><font><i>/**
     * POST /user : Create user
     * Create user functionality
     *
     * @param body Created user object (required)
     * @return successful operation (status code 200)
     */</i></font><font>
    @ApiOperation(value = </font><font>"Create user"</font><font>, 
      nickname = </font><font>"createUser"</font><font>, 
      notes = </font><font>"Create user functionality"</font><font>, 
      tags={ </font><font>"user"</font><font>, })
    @ApiResponses(value = { 
        @ApiResponse(code = 200, message = </font><font>"successful operation"</font><font>) })
    @RequestMapping(value = </font><font>"/user"</font><font>,
        method = RequestMethod.POST)
    <b>default</b> ResponseEntity<Void> createUser(
      @ApiParam(value = </font><font>"Created user object"</font><font> ,required=<b>true</b> )  
      @Valid 
      @RequestBody User body) {
        <b>return</b> getDelegate().createUser(body);
    }
    
    </font><font><i>// ... other methods omitted</i></font><font>
}
</font>

生成器还为我们创建了一个Spring控制器,用于实现UserApi接口:

@javax.annotation.Generated(...)
@Controller
@RequestMapping(<font>"${openapi.reflectoring.base-path:/v2}"</font><font>)
<b>public</b> <b>class</b> UserApiController implements UserApi {

    <b>private</b> <b>final</b> UserApiDelegate delegate;

    <b>public</b> UserApiController(
      @Autowired(required = false) UserApiDelegate delegate) {
        <b>this</b>.delegate = Optional.ofNullable(delegate)
            .orElse(<b>new</b> UserApiDelegate() {});
    }

    @Override
    <b>public</b> UserApiDelegate getDelegate() {
        <b>return</b> delegate;
    }
}
</font>

如果Spring UserApiDelegate在应用程序上下文中找到我们的实现,它将注入到控制器的构造函数中。否则,将使用默认实现。

让我们启动应用程序并点击GET端点/v2/user/{username}。

curl -I http:<font><i>//localhost:8080/v2/user/Petros</i></font><font>
HTTP/1.1 501
Content-Length: 0
</font>

但是为什么我们会收到501响应(未实现)?

因为我们没有实现UserApiDelegate接口,所以UserApiController使用了默认接口,该接口 返回HttpStatus.NOT_IMPLEMENTED。

现在让我们实现UserApiDelegate:

@Service
<b>public</b> <b>class</b> UserApiDelegateImpl implements UserApiDelegate {

    @Override
    <b>public</b> ResponseEntity<User> getUserByName(String username) {
        User user = <b>new</b> User();
        user.setId(123L);
        user.setFirstName(<font>"Petros"</font><font>);
        
        </font><font><i>// ... omit other initialization</i></font><font>

        <b>return</b> ResponseEntity.ok(user);
    }
}
</font>

在类中添加@Service或@Component批注很重要,以便Spring可以将其拾取并注入到中UserApiController。

如果现在curl http://localhost:8080/v2/user/Petros再次运行,将收到有效的JSON响应:

{
  <font>"id"</font><font>: 123,
  </font><font>"firstName"</font><font>: </font><font>"Petros"</font><font>,
  </font><font><i>// ... omit other properties</i></font><font>
}
</font>

我认为,使用Maven插件而不是Swagger Editor生成OpenAPI规范是更好的选择。那是因为我们对我们的选择有更多的控制权。该插件提供了一些配置和使用Git作为版本控制工具,我们可以放心地跟踪在任的任何变化pom.xml和openapi.yml。

可以在 GitHub上 浏览示例代码。

原文  https://www.jdon.com/53930
正文到此结束
Loading...