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

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

1. обзор

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

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

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

Посмотрите на аннотацию @ResponseStatus и как использовать ее для установки кода состояния ответа.

Read more

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

Узнайте о различиях между аннотациями @Controller и @RestController в Spring MVC.

Read more

2. Понимание REST весной

Платформа Spring поддерживает два способа создания сервисов RESTful:

  • используя MVC сModelAndView

  • используя конвертеры HTTP-сообщений

ПодходModelAndView более старый и гораздо лучше документирован, но также более подробен и сложен в настройке. Он пытается внедрить парадигму REST в старую модель, которая не без проблем. Команда Spring поняла это и предоставила первоклассную поддержку REST, начиная с Spring 3.0.

The new approach, based on HttpMessageConverter and annotations, is much more lightweight and easy to implement. Конфигурация минимальна, и она обеспечивает разумные значения по умолчанию для того, что вы ожидаете от службы RESTful.

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

@Configuration
@EnableWebMvc
public class WebConfig{
   //
}

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

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

3.1. Использование Spring Boot

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

Мы все еще можем добавить функциональность 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 загружает определения bean-компонентов из классов@Configuration.

Обратите внимание, что класс конфигурацииWebConfig не был включен в тест, потому что он должен работать в контексте сервлета, который не предоставляется.

4.1. Использование Spring Boot

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

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

Например, мы можем использовать аннотацию@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(...);
    }
}

Подробную информацию по этому поводу можно найти наour ‘Testing in Spring Boot' article.

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

The @RestController is the central artifact in the entire Web Tier of the 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 в стиле Guava:

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

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

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

Отображения запроса просты. As with any controller, the actual value of the mapping, as well as the HTTP method, determine the target method for the request. @RequestBody привяжет параметры метода к телу HTTP-запроса, тогда как@ResponseBody делает то же самое для ответа и типа возвращаемого значения.

@RestController  - этоshorthand, чтобы включить аннотации@ResponseBody и @Controller в наш класс.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

6.4. Используя@ExceptionHandler

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

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

7. Additional Maven Dependencies **

В дополнение к зависимостиspring-webmvcrequired for the standard web application нам потребуется настроить маршалинг и демаршаллинг контента для 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. Использование Spring Boot

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

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

Обычно, если мы разрабатываем веб-приложение,we’ll just add the spring-boot-starter-web dependency and rely on it to include all the necessary artifacts to our project:


    org.springframework.boot
    spring-boot-starter-web
    2.1.2.RELEASE

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

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

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

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

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

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