Рубрики
Без рубрики

Загрузка API REST с помощью swagger

Введение Одной из наиболее обычных задач, требуемых для бэкенд-разработчиков, является создание… С тегами java, rest, springboot, swagger.

Одной из наиболее распространенных задач, требуемых для бэкенд-разработчиков, является создание API, ориентированного на микросервисы. Когда мы говорим о микросервисах, мы имеем в виду небольшие наборы конечных точек, каждая из которых решает разные проблемы, которые могут быть развернуты индивидуально быстрым и простым способом, поэтому их легко наращивать в случае, если нам нужно позаботиться о большом количестве запросов в определенной области. Это большое преимущество по сравнению с классической монолитной архитектурой, которая требует развертывания всех служб одновременно, что требует больше времени и ресурсов.

Службы REST более эффективны, чем службы SOAP, а также их проще защитить, поэтому неудивительно, что они становятся такими популярными, но развертывание и тестирование служб может быть проблематичным. К счастью, существуют некоторые фреймворки, которые предоставляют нам несколько простых в использовании инструментов.

Давайте разделим этот вопрос на 3 этапа:

  1. Создание службы REST с использованием JSON.
  2. Загрузка службы с помощью Spring boot.
  3. Документируйте его и получите удобный интерфейс для тестов с помощью Swagger.

В нашем примере кода мы собираемся смоделировать небольшую систему для обработки базы данных видеоигр с помощью REST. Пакеты кода будут определены следующим образом:

  • Пакет API будет содержать весь REST API, разделяя его по уровням (сервисы, объекты доступа к данным и передачи данных) и предоставляя контроллеру конечные точки.
  • Для настройки системы swagger нам понадобится файл конфигурации, плюс классы пакетов API должны получить новые аннотации для документирования его содержимого.
  • Наконец, мы добавим универсальный загрузчик Spring, который может использоваться для любого пакета и требует очень небольшой настройки.

1. Разработка фиктивного веб-сервиса RESTful

Первым шагом является создание базовой структуры для службы REST. Мы разделим его на разные слои:

  • Контроллер будет контроллером видеоигр и будет содержать все конечные точки, мы оставим это на потом, так как поговорим о его настройке с помощью swagger.
  • Интерфейс сервиса видеоигр определит уровень сервиса, который будет иметь “обычные” конечные точки.
  • День видеоигр дает представление о методах, доступных на уровне доступа к данным. Чтобы избежать больших проблем с настройкой подключения к базе данных или реального хранилища, мы будем издеваться над этим.

Хорошей практикой является разделение проектирования и реализации с помощью интерфейсов. На самом деле в этом нет необходимости, но это сделает код более многоразовым.

Поэтому Служба будет имитировать широко известные операции CRUD: получение (поиск), установка (обновление), добавление (сохранение) и удаление (удаление).

import org.thalion.snippet.swagger.api.dto.Videogame;

public interface IVideogameService {
  Collection findAll();
  Videogame findOne(String name);
  String save(Videogame videogame);
  void update(Videogame setName);
  void delete(String name);
}

Сегодня сервису не придется много делать, он будет подключаться только к уровню доступа к данным и позволит ему обрабатывать всю работу с базой данных.

import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thalion.snippet.swagger.api.dao.IVideogameDAO;
import org.thalion.snippet.swagger.api.dto.Videogame;

@Service
public class VideogameService implements IVideogameService {

  @Autowired
  private IVideogameDAO dao;

  @Override
  public Collection findAll() {
    return dao.findAll();
  }

  @Override
  public Videogame findOne(String name) {
    return dao.findOne(name);
  }

  @Override
  public String save(Videogame videogame) {
    return dao.save(videogame);
  }

  @Override
  public void update(Videogame videogame) {
    update(videogame);
  }

  @Override
  public void delete(String name) {
    dao.delete(name);
  }
}

Объект доступа к данным или DAO на самом деле выполнит тяжелую работу, именно он будет подключаться к базе данных.

import java.util.Collection;
import org.thalion.snippet.swagger.api.dto.Videogame;

public interface IVideogameDAO {
  Collection findAll();
  Videogame findOne(String name);
  String save(Videogame videogame);
  void update(Videogame videogame);
  void delete(String name);
}

Но в данном случае мы хотим, чтобы все было просто, так как мы фокусируемся на уровне сервисов. , , поэтому вместо настройки подключения к базе данных и написания некоторых запросов мы будем имитировать его с помощью простой структуры карты.

import java.util.Collection;
import java.util.Map;

import org.springframework.stereotype.Repository;
import org.thalion.snippet.swagger.api.dto.Videogame;

@Repository
public class VideogameDAOMocker implements IVideogameDAO {

  private Map storage;

  @Override
  public Collection findAll() {
    return storage.values();
  }

@Override
public Videogame findOne(String name) {
  return storage.get(name);
}

  @Override
  public String save(Videogame videogame) {
    storage.put(videogame.getName(), videogame);
    return videogame.getName();
  }

  @Override
  public void update(Videogame videogame) {
    storage.put(videogame.getName(), videogame);
  }

  @Override
  public void delete(String name) {
    storage.remove(name);
  }
}

Объект передачи данных или DTO сейчас не очень интересен, так как это было бы так же просто, как класс с парой строк для определения его атрибутов. Поэтому мы пока пропустим его содержание.

Наконец, мы будем ссылаться на конечную точку как на контроллер, и, поскольку мы будем использовать службу REST с JSON, нам нужно будет добавить ее зависимость maven.


  com.sun.jersey
  jersey-json

Контроллер для Spring MVC станет дополнительным слоем, на котором произойдет большинство интересных вещей, которые мы изучаем сегодня. Прежде всего, мы собираемся подключить его к сервису, а ОСТАЛЬНОЕ настроить с помощью аннотаций spring JAX-RS:

  • Сопоставление запросов настраивает путь.
  • Сопоставление запросов указывает, что метод ожидает HTTP-запрос.
  • Тело запроса получает информацию из тела HTTP.
  • Переменная пути получает значение из URL-адреса.
  • Статус ответа сохранит код состояния для отправки обратно.
  • Тело ответа получает информацию из тела HTTP
import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thalion.snippet.swagger.api.dto.Videogame;
import org.thalion.snippet.swagger.api.service.IVideogameService;

@RestController
@RequestMapping("/api/videogames")
public class VideogameControllerBeta {

  @Autowired
  private IVideogameService service;

  @ResponseStatus(HttpStatus.OK)
  @RequestMapping(method = RequestMethod.GET,
   produces = MediaType.APPLICATION_JSON_VALUE)
  public Collection getAllVideogames() {
    return service.findAll();
  }

  @ResponseStatus(HttpStatus.OK)
  @RequestMapping(method = RequestMethod.GET, value = "{name}",
   produces = MediaType.APPLICATION_JSON_VALUE)
  public Videogame getVideogameByName(@PathVariable String name) {
    return service.findOne(name);
  }

  @RequestMapping(method = RequestMethod.POST,
   produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity createVideogame(@RequestBody Videogame videogame) {
    String videogameCreatedId = service.save(videogame);
    return new ResponseEntity(videogameCreatedId, HttpStatus.CREATED);
  }

  @ResponseStatus(HttpStatus.NO_CONTENT)
  @RequestMapping(method = RequestMethod.PUT, value = "{name}")
  public void updateVideogame(@PathVariable String name,
   @RequestBody Videogame videogame) {
    videogame.setName(name);
    service.update(videogame);
  }

  @ResponseStatus(HttpStatus.NO_CONTENT)
  @RequestMapping(method = RequestMethod.DELETE, value = "{name}")
  public void deleteInfo( @PathVariable String name) {
    service.delete(name);
  }
}

С этим у нас все готово, так что давайте найдем место для запуска этого кода.

2. Давайте загрузим его!

Spring boot – это инструмент для создания приложения “просто запустите”, которое можно настроить с помощью всего нескольких строк кода и аннотаций без какой-либо конфигурации xml. Это делает его действительно интересным для быстрого запуска и тестирования микросервисов Java.

Давайте начнем с добавления зависимостей maven и настройки конфигурации “просто запуск”:


  
  
    org.springframework.boot>/groupId>
    spring-boot-starter-web
  




  
    
      org.springframework.boot
      spring-boot-maven-plugin
    
  

Затем нам нужно настроить его в самом коде Java. Самый длинный способ его настройки – использовать следующие примечания:

  • RestController , это Spring-MVC, который настраивает этот класс в качестве контроллера для службы REST
  • EnableAutoConfiguration , это настраивает службу загрузки в соответствии с включенными нами зависимостями. Поскольку мы использовали контроллер, система рассмотрит возможность добавления Spring MVC и Tomcat.
  • Компоненты могут сканировать компоненты пакета и его дочерних пакетов.

Но все эти 3 аннотации можно свести к одной: Приложение SpringBootApplication (сканировать базовые пакеты = {"replace_with_main_package"}) , которое используется в примере.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* Generic SpringBoot, only configured by setting the scanBasePackages
* restricts the scanned packages
*/
@SpringBootApplication(scanBasePackages = { "org.thalion.snippet.swagger" })
public class SpringbootRunner {

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

Таким образом, просто запустив этот класс, будет развернут локальный сервер, и мы готовы протестировать службу… но это не интуитивно понятно. Давайте сделаем это лучше.

3. Что делает его красивым и простым в тестировании, а также позволяет избежать настройки клиентской части

Документирование API, чтобы его было легко понять другим разработчикам и сделать его удобным для тестировщиков, – непростая задача. Swagger – это платформа, которая, используя несколько дополнительных аннотаций, способна создавать простой веб-интерфейс пользователя с помощью REST-вызовов API, документированных с использованием доступных метаданных. Мы собираемся использовать версию springfox, так как она уже поставляется в комплекте с правильными аннотациями.

❕ Это была стабильная версия, когда сообщение было первоначально написано.


  
  
    io.springfox
    springfox-swagger-ui
    2.3.0
  
  
    io.springfox
    springfox-swagger2
    2.3.0
  

Наиболее обычными аннотациями являются:

  • Api : для классов контроллеров он устанавливает документацию по конечной точке API.
  • Модель Api : для имен классов объектов передачи данных.
  • Свойство ApiModelProperty : для атрибутов объектов передачи данных.
  • Операция Api : они выходят за рамки методов или служб, доступных на конечной точке.
  • ApiParam : для входного параметра метода.
  • Ответ Api : для выходных параметров. У вас может быть несколько ответов с набором ответов Api (например, Также добавьте ошибку 404 и т. Д.).

Затем, чтобы полностью настроить это, необходимо перепроверить 2 важных момента: DTO и контроллер.

Давайте начнем с DTO, так как это самая простая часть: нам нужно только документировать информацию о самом определении класса и атрибутах.

package org.thalion.snippet.swagger.api.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

@ApiModel(value = "Videogame entity", description = "Complete data of an entity
 videogame")
public class Videogame {

  @ApiModelProperty(value = "The name of the videogame", required = true)
  private String name;
  @ApiModelProperty(value = "The developer of the videogame", required = false)
  private String developer;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getDeveloper() {
    return developer;
  }

  public void setDeveloper(String developer) {
    this.developer = developer;
  }
}

Контроллер немного сложнее, так как нам нужно документировать каждую отдельную конечную точку.

import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thalion.snippet.swagger.api.dto.Videogame;
import org.thalion.snippet.swagger.api.service.IVideogameService;

import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;

@RestController
@RequestMapping("/api/videogames")
public class VideogameController {

  @Autowired
  private IVideogameService service;

  @ResponseStatus(HttpStatus.OK)
  @RequestMapping(method = RequestMethod.GET, produces = 
  MediaType.APPLICATION_JSON_VALUE)
  @ApiOperation(value = "Get Videogames", notes = "Returns all the videogame data")
  @ApiResponses({ @ApiResponse(code = 200, message = "Returns this information") })
  public Collection getAllVideogames() {
    return service.findAll();
  }

  @ResponseStatus(HttpStatus.OK)
  @RequestMapping(method = RequestMethod.GET, value = "{name}", produces = 
  MediaType.APPLICATION_JSON_VALUE)
  @ApiOperation(value = "Get the info for one videogame", notes = "Returns the info
   from one videogame")
  @ApiResponses({ @ApiResponse(code = 200, message = "Exists this information") })
  public Videogame getVideogameByName(
@ApiParam(defaultValue = "default", value = "The name of the videogame to return")
 @PathVariable String name) {
    return service.findOne(name);
  }

  @RequestMapping(method = RequestMethod.POST, produces =
   MediaType.APPLICATION_JSON_VALUE)
  @ApiOperation(value = "Create videogame information", notes = "Create a videogame
   entry")
  @ApiResponses({ @ApiResponse(code = 201, message = "The videgame entry was created
   successfully") })
  public ResponseEntity createVideogame(@RequestBody Videogame videogame) {
    String videogameCreatedId = service.save(videogame);
    return new ResponseEntity(videogameCreatedId, HttpStatus.CREATED);
  }

  @ResponseStatus(HttpStatus.NO_CONTENT)
  @RequestMapping(method = RequestMethod.PUT, value = "{name}")
  @ApiOperation(value = "Update videogame information", notes = "Update a videogame
   information entry")
  @ApiResponses({ @ApiResponse(code = 204, message = "The videgame entry was updated
   successfully") })
  public void updateVideogame(
@ApiParam(defaultValue = "Default", value = "The name of the videogame to update")
 @PathVariable String name,
@RequestBody Videogame videogame) {
    videogame.setName(name);
    service.update(videogame);
  }

@ResponseStatus(HttpStatus.NO_CONTENT)
@RequestMapping(method = RequestMethod.DELETE, value = "{name}")
@ApiOperation(value = "Delete videogame", notes = "Deletes a videogame entry")
@ApiResponses({ @ApiResponse(code = 204, message = "The videgame entry was deleted
 successfully") })
public void deleteInfo(
@ApiParam(defaultValue = "Default", value = "The name of the videogame to delete")
 @PathVariable String name) {
    service.delete(name);
  }
}

Как только все аннотации к документации будут записаны, нам просто нужно настроить страницу API с помощью конструктора списков дел, который предоставляет основной графический интерфейс для нашей реализации swagger.

Поскольку мы работаем с API, мы будем беспокоиться только об URL-адресах в шаблоне /api/ , которые мы можем выбрать с помощью регулярных выражений.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

  @Bean
  public Docket newsApi() {
    return new Docket(DocumentationType.SWAGGER_2).groupName("api-videogames")
    .apiInfo(apiVideogames()).select().paths(PathSelectors.regex("/api.*")).build();
  }

  private ApiInfo apiVideogames() {
    return new ApiInfoBuilder().title("Videogames REST api POC")
.description("PoC of a REST api, to test both Springboot and Swagger")
.termsOfServiceUrl("https://creativecommons.org/licenses/by/4.0/")
.contact("abcd@mail.com")
.license("GNU General Public License v3.0").licenseUrl(
    "https://www.gnu.org/licenses/gpl-3.0.en.html").version("3.0").build();
  }
}

И мы, наконец, получаем эту прекрасную веб-страницу со всеми конечными точками, с которыми мы можем легко взаимодействовать на localhost:8080/swagger-ui.html . Мы можем взаимодействовать с различными методами и настраивать аргументы для выполнения быстрых тестов, избегая необходимости писать клиента или использовать генератор запросов REST, такой как SoapUI.

Поздравляю! ваша демонстрационная версия полностью настроена и готова к работе.

Оригинал: “https://dev.to/angelesbroullon/booting-a-rest-api-with-swagger-6c4”