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

Создайте REST API с помощью Spring и Java Config

Настройте REST API с конфигурацией на основе Spring и Java, кодами ответов HTTP, полезными нагрузками REST и маршалингом.

Автор оригинала: Eugen Paraschiv.

1. Обзор

В этой статье показано, как настроить REST в Spring – коды контроллера и HTTP-ответов, конфигурацию маршалинга полезной нагрузки и согласование содержимого.

Дальнейшее чтение:

Использование Spring @ResponseStatus для установки кода состояния HTTP

Аннотации Spring @Controller и @RestController

2. Понимание ОТДЫХА весной

Фреймворк Spring поддерживает два способа создания служб RESTful:

  • использование MVC с ModelAndView
  • использование конвертеров HTTP-сообщений

Подход ModelAndView является более старым и гораздо лучше документированным, но также более подробным и тяжелым для конфигурации. Он пытается встроить парадигму ОТДЫХА в старую модель, что не без проблем. Команда Spring поняла это и обеспечила первоклассную поддержку ОТДЫХА, начиная с Spring 3.0.

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

3. Конфигурация Java

@Configuration
@EnableWebMvc
public class WebConfig{
   //
}

Новая аннотация @EnableWebMvc делает некоторые полезные вещи – в частности, в случае REST она обнаруживает существование Jackson и JAXB 2 в пути к классу и автоматически создает и регистрирует преобразователи JSON и XML по умолчанию. Функциональность аннотации эквивалентна версии XML:

аннотациями/> аннотациями/>

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

3.1. Использование Пружинного Ботинка

Если мы используем аннотацию @SpringBootApplication и библиотека spring-webmvc находится в пути к классу, то аннотация @EnableWebMvc добавляется автоматически с автоконфигурацией по умолчанию .

Мы все еще можем добавить функциональность MVC в эту конфигурацию, реализовав интерфейс WebMvcConfigurer в аннотированном классе @Configuration . Мы также можем использовать экземпляр WebMvcRegistrationsAdapter для предоставления наших собственных реализаций RequestMappingHandlerMapping , RequestMappingHandlerAdapter или ExceptionHandlerExceptionResolver .

Наконец, если мы хотим отказаться от функций MVC Spring Boot и объявить пользовательскую конфигурацию, мы можем сделать это с помощью аннотации @EnableWebMvc .

4. Тестирование контекста Spring

Начиная с Spring 3.1, мы получаем первоклассную поддержку тестирования для @Configuration классов:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration( 
  classes = {WebConfig.class, PersistenceConfig.class},
  loader = AnnotationConfigContextLoader.class)
public class SpringContextIntegrationTest {

   @Test
   public void contextLoads(){
      // When
   }
}

Мы указываем классы конфигурации Java с помощью аннотации @ContextConfiguration . Новый AnnotationConfigContextLoader загружает определения компонентов из классов @Configuration .

Обратите внимание, что класс Web Config configuration не был включен в тест, поскольку он должен выполняться в контексте сервлета, который не предусмотрен.

4.1. Использование Пружинного Ботинка

Spring Boot предоставляет несколько аннотаций для настройки Spring ApplicationContext для наших тестов более интуитивно понятным способом.

Мы можем загрузить только определенный фрагмент конфигурации приложения или смоделировать весь процесс запуска контекста.

Например, мы можем использовать аннотацию @SpringBootTest , если хотим, чтобы весь контекст был создан без запуска сервера.

Имея это на месте, мы можем затем добавить @AutoConfigureMockMvc для внедрения экземпляра MockMvc и отправки HTTP-запросов :

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class FooControllerAppIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void whenTestApp_thenEmptyResponse() throws Exception {
        this.mockMvc.perform(get("/foos")
            .andExpect(status().isOk())
            .andExpect(...);
    }

}

Чтобы избежать создания всего контекста и тестировать только наши контроллеры MVC, мы можем использовать @WebMvcTest:

@RunWith(SpringRunner.class)
@WebMvcTest(FooController.class)
public class FooControllerWebLayerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private IFooService service;

    @Test()
    public void whenTestMvcController_thenRetrieveExpectedResult() throws Exception {
        // ...

        this.mockMvc.perform(get("/foos")
            .andExpect(...);
    }
}

Мы можем найти подробную информацию по этому вопросу в нашей статье “Тестирование в весенней загрузке”.

5. Контроллер

@RestController является центральным артефактом на всем веб – уровне RESTful API. Для целей этого поста контроллер моделирует простой ресурс REST – Foo :

@RestController
@RequestMapping("/foos")
class FooController {

    @Autowired
    private IFooService service;

    @GetMapping
    public List findAll() {
        return service.findAll();
    }

    @GetMapping(value = "/{id}")
    public Foo findById(@PathVariable("id") Long id) {
        return RestPreconditions.checkFound(service.findById(id));
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Long create(@RequestBody Foo resource) {
        Preconditions.checkNotNull(resource);
        return service.create(resource);
    }

    @PutMapping(value = "/{id}")
    @ResponseStatus(HttpStatus.OK)
    public void update(@PathVariable( "id" ) Long id, @RequestBody Foo resource) {
        Preconditions.checkNotNull(resource);
        RestPreconditions.checkNotNull(service.getById(resource.getId()));
        service.update(resource);
    }

    @DeleteMapping(value = "/{id}")
    @ResponseStatus(HttpStatus.OK)
    public void delete(@PathVariable("id") Long id) {
        service.deleteById(id);
    }

}

Возможно, вы заметили, что я использую простой стиль гуавы RestPreconditions утилита:

public class RestPreconditions {
    public static  T checkFound(T resource) {
        if (resource == null) {
            throw new MyResourceNotFoundException();
        }
        return resource;
    }
}

Реализация контроллера является непубличной-это потому, что в этом нет необходимости.

Обычно контроллер является последним в цепочке зависимостей. Он получает HTTP-запросы от контроллера Spring front ( DispatcherServlet ) и просто делегирует их на уровень обслуживания. Если нет случая использования, когда контроллер должен быть введен или управляться с помощью прямой ссылки, то я предпочитаю не объявлять его общедоступным.

Сопоставления запросов просты. Как и в случае с любым контроллером, фактическое значение сопоставления, а также метод HTTP определяют целевой метод для запроса. @ @ Тело запроса свяжет параметры метода с телом HTTP-запроса, тогда как @ResponseBody

То @RestController это стенография чтобы включить оба @ResponseBody и @Контроллер аннотации в нашем классе .

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

6. Сопоставление кодов ответов HTTP

Коды состояния HTTP-ответа являются одной из наиболее важных частей службы REST, и тема может быстро стать очень сложной. Правильное их получение может быть тем, что создает или нарушает сервис.

6.1. Несопоставленные запросы

Если Spring MVC получает запрос, который не имеет сопоставления, он считает, что запрос не разрешен, и возвращает клиенту МЕТОД 405, КОТОРЫЙ НЕ РАЗРЕШЕН.

Также рекомендуется включать заголовок Allow HTTP при возврате 405 клиенту, чтобы указать, какие операции разрешены. Это стандартное поведение Spring MVC и не требует дополнительной настройки.

6.2. Действительные Сопоставленные запросы

Для любого запроса, который имеет сопоставление, Spring MVC считает запрос допустимым и отвечает 200 OK, если другой код состояния не указан иначе.

Именно из-за этого контроллер объявляет разные @ResponseStatus для действий create , update и delete , но не для get , которые действительно должны возвращать значение по умолчанию 200 OK.

6.3. Ошибка клиента

В случае ошибки клиента пользовательские исключения определяются и сопоставляются с соответствующими кодами ошибок.

Простое выбрасывание этих исключений из любого из уровней веб-уровня гарантирует, что Spring отобразит соответствующий код состояния в HTTP-ответе:

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
   //
}
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
   //
}

Эти исключения являются частью API REST и, как таковые, должны использоваться только в соответствующих слоях, соответствующих REST; если, например, существует слой DAO/DAL, он не должен использовать исключения напрямую.

Обратите также внимание, что это не проверенные исключения, а исключения во время выполнения – в соответствии с практикой и идиомами Spring.

6.4. Использование @ExceptionHandler

Другой вариант сопоставления пользовательских исключений с конкретными кодами состояния-использовать аннотацию @ExceptionHandler в контроллере. Проблема с таким подходом заключается в том, что аннотация применяется только к контроллеру, в котором она определена. Это означает, что нам нужно вводить данные в каждом контроллере индивидуально.

Конечно, существует больше способов обработки ошибок как в Spring, так и в Spring Boot, которые обеспечивают большую гибкость.

7. Дополнительные зависимости Maven

В дополнение к spring-webmvc зависимости , необходимой для стандартного веб-приложения , нам нужно будет настроить сортировку и отмену сортировки контента для REST API:


   
      com.fasterxml.jackson.core
      jackson-databind
      2.9.8
   
   
      javax.xml.bind
      jaxb-api
      2.3.1
      runtime
   

Это библиотеки, используемые для преобразования представления ресурса REST в JSON или XML.

7.1. Использование Пружинного Ботинка

Если мы хотим получить ресурсы в формате JSON, Spring Boot обеспечивает поддержку различных библиотек, а именно Jackson, Gson и JSON-B.

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

Обычно, если мы разрабатываем веб-приложение, мы просто добавляем зависимость spring-boot-starter-web и полагаемся на нее, чтобы включить все необходимые артефакты в наш проект :


    org.springframework.boot
    spring-boot-starter-web
    2.4.0

Spring Boot по умолчанию использует Джексона.

Если мы хотим сериализовать наши ресурсы в формате XML, нам придется добавить расширение Jackson XML ( jackson-dataformat-xml ) в наши зависимости или вернуться к реализации JAXB (предоставленной по умолчанию в JDK) с помощью аннотации @XmlRootElement на нашем ресурсе.

8. Заключение

В этом руководстве показано, как реализовать и настроить службу REST с помощью Spring и конфигурации на основе Java.

В следующих статьях серии я сосредоточусь на обнаруживаемости API , расширенном согласовании контента и работе с дополнительными представлениями ресурса .

Весь код этой статьи доступен на Github . Это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.