Open API Server Implementation Using OpenAPI Generator – 使用OpenAPI生成器实现开放API服务器

最后修改: 2021年 3月 31日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

As the name suggests, the OpenAPI Generator generates code from an OpenAPI specification. It can create code for client libraries, server stubs, documentation and configuration.

顾名思义,OpenAPI Generator可以从OpenAPI规范中生成代码。它可以为客户端库、服务器存根、文档和配置创建代码。

It supports various languages and frameworks. Notably, there’s support for C++, C#, Java, PHP, Python, Ruby, Scala — almost all the widely used ones.

它支持各种语言和框架。值得注意的是,它支持C++、C#、Java、PHP、Python、Ruby、Scala – 几乎是所有广泛使用的语言。

In this tutorial, we’ll learn how to implement a Spring-based server stub using OpenAPI Generator via its Maven plugin.

在本教程中,我们将学习如何通过Maven插件使用OpenAPI Generator实现基于Spring的服务器存根。

Other ways of using the generator are through its CLI or online tools.

使用生成器的其他方法是通过其CLI在线工具

2. YAML File

2.YAML文件

To begin, we’ll need a YAML file specifying the API. We’ll give it as input to our generator to produce a server stub.

首先,我们需要一个指定API的YAML文件。我们将把它作为输入给我们的生成器,以产生一个服务器存根。

Here’s a snippet of our petstore.yml:

这里是我们的petstore.yml的一个片段。

openapi: "3.0.0"
paths:
  /pets:
    get:
      summary: List all pets
      operationId: listPets
      tags:
        - pets
      parameters:
        - name: limit
          in: query
          ...
      responses:
        ...
    post:
      summary: Create a pet
      operationId: createPets
      ...
  /pets/{petId}:
    get:
      summary: Info for a specific pet
      operationId: showPetById
      ...
components:
  schemas:
    Pet:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
        tag:
          type: string
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: integer
          format: int32
        message:
          type: string

3. Maven Dependencies

3.Maven的依赖性

3.1. Plugin for OpenAPI Generator

3.1.用于生成OpenAPI的插件

Next, let’s add the Maven dependency for the generator plugin:

接下来,让我们为生成器插件添加Maven依赖项

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

As we can see, we passed in the YAML file as inputSpec. After that, since we need a Spring-based server, we used the generatorName as spring.

我们可以看到,我们把YAML文件作为inputSpec传入。之后,由于我们需要一个基于Spring的服务器,我们把generatorName作为spring

Then apiPackage specifies the package name where the API will be generated into.

然后apiPackage指定API将被生成的包名。

Next, we have the modelPackage where the generator places the data models.

接下来,我们有一个modelPackage,生成器在这里放置数据模型。

With delegatePattern set to true, we’re asking to create an interface that can be implemented as a customized @Service class.

delegatePattern设置为true时,我们要求创建一个可以作为定制的@Service类实现的接口。

Importantly, options for OpenAPI Generator are the same whether we’re using the CLI, Maven/Gradle Plugins or online generation options.

重要的是,OpenAPI Generator的选项是相同的,无论我们是使用CLI、Maven/Gradle插件还是在线生成选项。

3.2. Maven Dependencies

3.2.Maven的依赖性

As we’ll be generating a Spring server, we also need its dependencies (Spring Boot Starter Web and Spring Data JPA) so that generated code compiles and runs as expected:

由于我们将生成一个 Spring 服务器,我们还需要它的依赖项(Spring Boot Starter WebSpring Data JPA),以便生成的代码能够按照预期进行编译和运行

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.4.4</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>2.4.6</version>
    </dependency>
</dependencies>

Apart from the above Spring dependencies, we’ll also need jackson-databind and swagger2 dependencies so that our generated code compiles successfully:

除了上述的Spring依赖,我们还需要jackson-databindswagger2依赖,这样我们生成的代码才能成功编译。

<dependency>
    <groupId>org.openapitools</groupId>
    <artifactId>jackson-databind-nullable</artifactId>
    <version>0.2.1</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>

4. Code Generation

4.代码生成

To generate the server stub, we simply need to run the following:

为了生成服务器存根,我们只需要运行以下内容。

mvn clean install

As a result, here’s what we get:

结果,我们得到的是这样的结果。

Now let’s take a look at the code, starting with the contents of apiPackage.

现在让我们看一下代码,从apiPackage的内容开始。

First, we get an API interface called PetsApi that contains all the requests mappings as defined in the YAML specification.

首先,我们得到一个名为PetsApi的API接口,其中包含YAML规范中定义的所有请求映射。

Here’s the snippet:

下面是这个片段。

@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", 
  date = "2021-03-22T23:26:32.308871+05:30[Asia/Kolkata]")
@Validated
@Api(value = "pets", description = "the pets API")
public interface PetsApi {
    /**
     * GET /pets : List all pets
     *
     * @param limit How many items to return at one time (max 100) (optional)
     * @return A paged array of pets (status code 200)
     *         or unexpected error (status code 200)
     */
    @ApiOperation(value = "List all pets", nickname = "listPets", notes = "", 
      response = Pet.class, responseContainer = "List", tags={ "pets", })
    @ApiResponses(value = { @ApiResponse(code = 200, message = "A paged array of pets", 
      response = Pet.class, responseContainer = "List"),
      @ApiResponse(code = 200, message = "unexpected error", response = Error.class) })
    @GetMapping(value = "/pets", produces = { "application/json" })
    default ResponseEntity<List> listPets(@ApiParam(
      value = "How many items to return at one time (max 100)") 
      @Valid @RequestParam(value = "limit", required = false) Integer limit) {
        return getDelegate().listPets(limit);
    }

    // other generated methods
}

Second, since we’re using the delegate pattern, OpenAPI also generates a delegator interface for us called PetsApiDelegate.

其次,由于我们使用的是委托模式,OpenAPI也为我们生成了一个名为PetsApiDelegate的委托者接口。

In particular, methods declared in this interface return an HTTP status of 501 Not Implemented by default:

特别是,在此接口中声明的方法默认返回HTTP状态为501未实施

@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", 
  date = "2021-03-22T23:26:32.308871+05:30[Asia/Kolkata]")
public interface PetsApiDelegate {
    /**
     * GET /pets : List all pets
     *
     * @param limit How many items to return at one time (max 100) (optional)
     * @return A paged array of pets (status code 200)
     *         or unexpected error (status code 200)
     * @see PetsApi#listPets
     */
    default ResponseEntity<List<Pet>> listPets(Integer limit) {
        getRequest().ifPresent(request -> {
            for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
                if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
                    String exampleString = "{ \"name\" : \"name\", \"id\" : 0, \"tag\" : \"tag\" }";
                    ApiUtil.setExampleResponse(request, "application/json", exampleString);
                    break;
                }
            }
        });
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
    }

    // other generated method declarations
}

After that, we see there’s a PetsApiController class that simply wires in the delegator:

之后,我们看到有一个PetsApiController类,它简单地连接了委托人

@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", 
  date = "2021-03-22T23:26:32.308871+05:30[Asia/Kolkata]")
@Controller
@RequestMapping("${openapi.swaggerPetstore.base-path:}")
public class PetsApiController implements PetsApi {

    private final PetsApiDelegate delegate;

    public PetsApiController(
      @org.springframework.beans.factory.annotation.Autowired(required = false) PetsApiDelegate delegate) {
        this.delegate = Optional.ofNullable(delegate).orElse(new PetsApiDelegate() {});
    }

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

In the modelPackage, a couple of data model POJOs called Error and Pet are generated, based on the schemas defined in our YAML input.

modelPackage中,根据我们YAML输入中定义的schemas,生成了一对名为ErrorPet的数据模型POJOs。

Let’s look at one of them — Pet:

让我们看看其中之一–Pet

@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", 
  date = "2021-03-22T23:26:32.308871+05:30[Asia/Kolkata]")
public class Pet {
  @JsonProperty("id")
  private Long id;

  @JsonProperty("name")
  private String name;

  @JsonProperty("tag")
  private String tag;

  // constructor

  @ApiModelProperty(required = true, value = "")
  @NotNull
  public Long getId() {
    return id;
  }

  // other getters and setters

  // equals, hashcode, and toString methods
}

5. Testing the Server

5.测试服务器

Now all that is required for the server stub to be functional as a server is to add an implementation of the delegator interface.

现在,服务器存根作为一个服务器的功能所需要的就是添加一个委托人接口的实现。

To keep things simple, we won’t do that here and instead only test the stub.

为了保持简单,我们在这里不做这个,而只测试存根。

Moreover, before doing that, we’ll need a Spring Application:

此外,在这样做之前,我们需要一个Spring Application

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

5.1. Test Using curl

5.1.使用curl进行测试

After starting up the application, we’ll simply run the command:

启动应用程序后,我们将简单地运行该命令。

curl -I http://localhost:8080/pets/

And here’s the expected result:

而这里是预期的结果。

HTTP/1.1 501 
Content-Length: 0
Date: Fri, 26 Mar 2021 17:29:25 GMT
Connection: close

5.2. Integration Tests

5.2.集成测试

Alternatively, we can write a simple integration test for the same:

另外,我们也可以写一个简单的integration test来实现。

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class OpenApiPetsIntegrationTest {
    private static final String PETS_PATH = "/pets/";

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void whenReadAll_thenStatusIsNotImplemented() throws Exception {
        this.mockMvc.perform(get(PETS_PATH)).andExpect(status().isNotImplemented());
    }

    @Test
    public void whenReadOne_thenStatusIsNotImplemented() throws Exception {
        this.mockMvc.perform(get(PETS_PATH + 1)).andExpect(status().isNotImplemented());
    }
}

6. Conclusion

6.结语

In this article, we saw how to generate a Spring-based server stub from a YAML specification using the OpenAPI generator’s Maven plugin.

在本文中,我们看到了如何使用OpenAPI生成器的Maven插件从YAML规范中生成一个基于Spring的服务器存根。

As a next step, we can also use it to generate a client.

作为下一步,我们还可以用它来生成一个客户端

As always, the source code is available over on GitHub.

一如既往,源代码可在GitHub上获取。