使用Spring Boot和Swagger进行API优先开发

论坛 期权论坛 脚本     
匿名技术用户   2021-1-3 16:16   27   0

his article was first published on reflectoring.io


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

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

通常,API规范也成为该API的文档。

GitHub logo thombergs / code-examples

A collection of code examples from blog posts etc.

Benefits of API-First

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

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

Creating an API Spec with the Swagger Editor

让我们在YAML文档中创建自己的OpenAPI规范。 为了更容易理解,我们将讨论分为正在创建的YAML文档的各个部分。

If you want to learn more details about the OpenAPI-Specification you can visit the Github repository.

General Information

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

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

The openapi field allows us to define the version of the OpenAPI spec that our document follows.

Within the info section, we add some information about our API. The fields should be pretty self-explanatory.

Finally, in the servers section, we provide a list of servers that implement the API.

Tags

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

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

The tags section provides fields for additional metadata which we can use to make our API more readable and easier to follow. We can add multiple tags, but each tag should be unique.

Paths

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

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: true
        schema:
          type: string
      responses:
        200:
          description: successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        404:
          description: User not found
          content: {}

The $ref field allows us to refer to objects in a self-defined schema. In this case we refer to the User schema object (see the next section about Components).

的摘要是该操作的简短说明。

With the operationId, we can define a unique identifier for the operation. We can think about it as our method name.

Finally, the responses object allows us to define the outcomes of an operation. We must define at least one successful response code for any operation call.

Components

API的对象均在组件 section. The objects defined within the 组件 object will not affect the API unless they are explicitly referenced from properties outside the 组件 object, as we have seen above:

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

The schemas section allows us to define the objects we want to use in our API.

In the securitySchemes section, we can define security schemes that can be used by the operations.

有两种使用安全方案的可能方法。

首先,我们可以使用安全领域:

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

在上述示例中,我们明确指定了/ user / {username}路径是通过api_key我们在上面定义的方案。

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

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

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

Generating Code From an API Specification

Having defined an API, we'll now create code from the YAML document above.

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

Generating Code from Swagger Editor

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

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

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

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

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

Generating Code with the OpenAPI Maven plugin

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

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

  • 应用程式, an 应用程式lication that implements the API from our 规格.规格, whose only job is to provide the API Specification for our 应用程式.

文件夹结构如下所示:

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

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

Our app is a simple Spring Boot project that we can automatically generate on start.spring.io, so let's focus on the pom.xml from the specification module, where we configure
the OpenAPI Maven plugin:

<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>true</delegatePattern>
                </configOptions>
            </configuration>
        </execution>
    </executions>
</plugin>

You can see the full pom.xml file on GitHub.

在本教程中,我们使用弹簧发电机。

只需运行命令./mvnw安装将生成实现我们的OpenAPI规范的代码!

看看文件夹目标/生成源/ openapi / src / main / java / io / reflectoring / model,我们找到了用户我们在YAML中定义的模型:

@javax.annotation.Generated(...)
public class User   {
  @JsonProperty("id")
  private Long id;

  @JsonProperty("username")
  private String username;

  @JsonProperty("firstName")
  private String firstName;

  // ... more properties

  @JsonProperty("userStatus")
  private Integer userStatus;

  // ... getters and setters

}

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

public interface UserApiDelegate {

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

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

    }
  // ... omit deleteUser, getUserByName and updateUser
}

当然,生成器无法为我们生成我们的业务逻辑,但是它确实会生成诸如UserApiDelegate以上供我们实施。

它还会创建一个UserApi将呼叫委托给的接口UserApiDelegate:

@Validated
@Api(value = "user", description = "the user API")
public interface UserApi {

    default UserApiDelegate getDelegate() {
        return new UserApiDelegate() {};
    }

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

    // ... other methods omitted
}

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

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

    private final UserApiDelegate delegate;

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

    @Override
    public UserApiDelegate getDelegate() {
        return delegate;
    }
}

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

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

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

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

因为我们没有实施UserApiDelegate界面和UserApiController使用默认值,它返回HttpStatus。NOT_IMPLEMENTED。

现在让我们实现UserApiDelegate:

@Service
public class UserApiDelegateImpl implements UserApiDelegate {

    @Override
    public ResponseEntity<User> getUserByName(String username) {
        User user = new User();
        user.setId(123L);
        user.setFirstName("Petros");

        // ... omit other initialization

        return ResponseEntity.ok(user);
    }
}

重要的是添加一个@服务要么@零件类的注释,以便Spring可以将其拾取并将其注入到UserApiController。

如果我们跑卷曲http:// localhost:8080 / v2 / user / Petros现在,我们将再次收到有效的JSON响应:

{
  "id": 123,
  "firstName": "Petros",
  // ... omit other properties
}

的UserApiDelegate是唯一的真理。 这使我们能够快速更改API。 例如,如果我们更改规范并再次生成它,则只需实现新生成的方法。

好消息是,如果我们不实现它们,我们的应用程序将不会中断。 默认情况下,这些端点将返回HTTP状态501(未实现)。

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

Conclusion

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

You can browse the example code on GitHub.

from: https://dev.to//petros0/api-first-development-with-spring-boot-and-swagger-1lm8

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP