Одной из наиболее распространенных задач, требуемых для бэкенд-разработчиков, является создание API, ориентированного на микросервисы. Когда мы говорим о микросервисах, мы имеем в виду небольшие наборы конечных точек, каждая из которых решает разные проблемы, которые могут быть развернуты индивидуально быстрым и простым способом, поэтому их легко наращивать в случае, если нам нужно позаботиться о большом количестве запросов в определенной области. Это большое преимущество по сравнению с классической монолитной архитектурой, которая требует развертывания всех служб одновременно, что требует больше времени и ресурсов.
Службы REST более эффективны, чем службы SOAP, а также их проще защитить, поэтому неудивительно, что они становятся такими популярными, но развертывание и тестирование служб может быть проблематичным. К счастью, существуют некоторые фреймворки, которые предоставляют нам несколько простых в использовании инструментов.
Давайте разделим этот вопрос на 3 этапа:
- Создание службы REST с использованием JSON.
- Загрузка службы с помощью Spring boot.
- Документируйте его и получите удобный интерфейс для тестов с помощью 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, который настраивает этот класс в качестве контроллера для службы RESTEnableAutoConfiguration, это настраивает службу загрузки в соответствии с включенными нами зависимостями. Поскольку мы использовали контроллер, система рассмотрит возможность добавления 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”