1. Обзор
Как следует из названия, генератор Open API генерирует код из спецификации Open API . Он может создавать код для клиентских библиотек, заглушек серверов, документации и конфигурации.
Он поддерживает различные языки и фреймворки. Примечательно, что есть поддержка C++, C#, Java, PHP, Python, Ruby, Scala – почти все широко используемые .
В этом уроке мы узнаем как реализовать заглушку сервера на основе Spring с помощью генератора OpenAPI через его плагин maven . Другие способы использования генератора-через его CLI или онлайн-инструменты .
2. Файл YAML
Для начала нам понадобится файл YAML с указанием API. Мы дадим его в качестве входных данных для нашего генератора, чтобы создать заглушку сервера.
Вот фрагмент нашего зоомагазина.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
3.1. Плагин для генератора открытых API
Далее, давайте добавим зависимость Maven для плагина генератора:
org.openapitools openapi-generator-maven-plugin 5.1.0 generate ${project.basedir}/src/main/resources/petstore.yml spring com.baeldung.openapi.api com.baeldung.openapi.model ApiUtil.java true
Как мы видим, мы передали файл YAML как input Spec . После этого, поскольку нам нужен сервер на основе Spring, мы использовали имя генератора как spring .
Затем api Package указывает имя пакета, в котором будет сгенерирован API. Далее у нас есть пакет model , в котором генератор размещает модели данных. С шаблоном делегата , установленным в true , мы просим создать интерфейс, который может быть реализован как настроенный @Service класс.
Важно отметить, что параметры для Open API Generator одинаковы независимо от того, используете ли вы CLI, плагины Maven/Gradle или параметры онлайн-генерации .
3.2. Пружинные зависимости
Поскольку мы будем генерировать сервер Spring, нам также нужны его зависимости ( Spring Boot Starter Web и Spring Data JPA ), чтобы сгенерированный код компилировался и выполнялся должным образом :
org.springframework.boot spring-boot-starter-web 2.4.4 org.springframework.data spring-data-jpa 2.4.6
4. Генерация кода
Чтобы сгенерировать заглушку сервера, нам просто нужно запустить:
mvn clean install
В результате мы получаем:
Теперь давайте взглянем на код, начиная с содержимого пакета api .
Во-первых, мы получаем интерфейс API под названием PetsApi , который содержит все сопоставления запросов, определенные в спецификации YAML. Вот фрагмент:
@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 ResponseEntitylistPets(@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 }
Во-вторых, поскольку мы используем шаблон делегата, OpenAPI также генерирует для нас интерфейс делегатора с именем PetsApiDelegate . В частности, методы, объявленные в этом интерфейсе, возвращают состояние 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> 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 }
После этого мы видим есть класс ApiController , который просто подключается к делегатору :
@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; } }
В пакете model генерируется пара POJO модели данных , называемых Error и Pet , на основе схем , определенных во входных данных YAML.
Давайте посмотрим на один из них – Домашнее животное :
@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. Тестирование сервера
Теперь все, что требуется для того, чтобы заглушка сервера функционировала как сервер, – это добавить реализацию интерфейса делегатора.
Чтобы все было просто, здесь мы не будем этого делать и только проверим заглушку. Более того, перед этим нам понадобится пружина Приложение :
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
5.1. Тест С использованием локона
После запуска приложения мы просто выполним команду:
curl -I http://localhost:8080/pets/
И вот ожидаемый результат:
HTTP/1.1 501 Content-Length: 0 Date: Fri, 26 Mar 2021 17:29:25 GMT Connection: close
5.2. Интеграционные тесты
В качестве альтернативы мы можем написать простой интеграционный тест для того же самого:
@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. Заключение
В этом уроке мы видели, как создать заглушку сервера на основе Spring из спецификации YAML с помощью плагина maven генератора OpenAPI .
В качестве следующего шага мы также можем использовать его для создания клиента .
Как всегда, исходный код доступен на GitHub .