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

Руководство по веб-библиотеке Problem Spring

Узнайте, как использовать веб-библиотеку Problem Spring для создания сообщений с подробными сведениями об ошибках с помощью ответа application/problem+json.

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

1. Обзор

В этом уроке мы рассмотрим как создать приложение/проблему+json ответы с помощью веб-библиотеки Problem Spring . Эта библиотека помогает нам избежать повторяющихся задач, связанных с обработкой ошибок.

Интегрируя Problem Spring Web в наше приложение Spring Boot, мы можем упростить способ обработки исключений в рамках нашего проекта и генерировать соответствующие ответы .

2. Проблемная библиотека

Проблема -это небольшая библиотека, предназначенная для стандартизации того, как API Rest на основе Java выражают ошибки своим потребителям.

/| Проблема – это абстракция любой ошибки, о которой мы хотим сообщить. Он содержит удобную информацию об ошибке. Давайте посмотрим представление по умолчанию Проблемы ответа:

{
  "title": "Not Found",
  "status": 404
}

В этом случае для описания ошибки достаточно кода состояния и заголовка. Однако мы также можем добавить его подробное описание:

{
  "title": "Service Unavailable",
  "status": 503,
  "detail": "Database not reachable"
}

Мы также можем создавать пользовательские Проблемные объекты, которые адаптируются к нашим потребностям:

Problem.builder()
  .withType(URI.create("https://example.org/out-of-stock"))
  .withTitle("Out of Stock")
  .withStatus(BAD_REQUEST)
  .withDetail("Item B00027Y5QG is no longer available")
  .with("product", "B00027Y5QG")
  .build();

В этом уроке мы сосредоточимся на реализации проблемной библиотеки для проектов Spring Boot.

3. Проблема Весенней Веб-настройки

Поскольку это проект на основе Maven, давайте добавим зависимость problem-spring-web в pom.xml :


    org.zalando
    problem-spring-web
    0.23.0

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

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

Нам также нужны |/spring-boot-starter-web и spring-boot-starter-security |/зависимости. Безопасность Spring требуется начиная с версии 0.23.0 problem-spring-web .

4. Базовая конфигурация

В качестве первого шага нам нужно отключить страницу ошибок с белой меткой, чтобы вместо этого мы могли видеть наше пользовательское представление ошибок:

@EnableAutoConfiguration(exclude = ErrorMvcAutoConfiguration.class)

Теперь давайте зарегистрируем некоторые из необходимых компонентов в ObjectMapper bean:

@Bean
public ObjectMapper objectMapper() {
    return new ObjectMapper().registerModules(
      new ProblemModule(),
      new ConstraintViolationProblemModule());
}

После этого нам нужно добавить следующие свойства в файл application.properties :

spring.resources.add-mappings=false
spring.mvc.throw-exception-if-no-handler-found=true
spring.http.encoding.force=true

И, наконец, нам нужно реализовать интерфейс Обработки проблем :

@ControllerAdvice
public class ExceptionHandler implements ProblemHandling {}

5. Расширенная конфигурация

В дополнение к базовой конфигурации мы также можем настроить наш проект для решения проблем, связанных с безопасностью. Первым шагом является создание класса конфигурации для включения интеграции библиотеки с Spring Security:

@Configuration
@EnableWebSecurity
@Import(SecurityProblemSupport.class)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private SecurityProblemSupport problemSupport;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Other security-related configuration
        http.exceptionHandling()
          .authenticationEntryPoint(problemSupport)
          .accessDeniedHandler(problemSupport);
    }
}

И, наконец, нам нужно создать обработчик исключений для исключений, связанных с безопасностью:

@ControllerAdvice
public class SecurityExceptionHandler implements SecurityAdviceTrait {}

6. Контроллер REST

После настройки нашего приложения мы готовы создать контроллер RESTful:

@RestController
@RequestMapping("/tasks")
public class ProblemDemoController {

    private static final Map MY_TASKS;

    static {
        MY_TASKS = new HashMap<>();
        MY_TASKS.put(1L, new Task(1L, "My first task"));
        MY_TASKS.put(2L, new Task(2L, "My second task"));
    }

    @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public List getTasks() {
        return new ArrayList<>(MY_TASKS.values());
    }

    @GetMapping(value = "/{id}",
      produces = MediaType.APPLICATION_JSON_VALUE)
    public Task getTasks(@PathVariable("id") Long taskId) {
        if (MY_TASKS.containsKey(taskId)) {
            return MY_TASKS.get(taskId);
        } else {
            throw new TaskNotFoundProblem(taskId);
        }
    }

    @PutMapping("/{id}")
    public void updateTask(@PathVariable("id") Long id) {
        throw new UnsupportedOperationException();
    }

    @DeleteMapping("/{id}")
    public void deleteTask(@PathVariable("id") Long id) {
        throw new AccessDeniedException("You can't delete this task");
    }

}

В этом контроллере мы намеренно создаем некоторые исключения. Эти исключения будут автоматически преобразованы в объекты Problem для получения ответа application/problem+json с подробной информацией об ошибке.

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

7. Встроенные функции советов

Совет-это небольшой обработчик исключений, который улавливает исключения и возвращает правильный объект проблемы.

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

throw new UnsupportedOperationException();

В результате мы получим ответ:

{
    "title": "Not Implemented",
    "status": 501
}

Поскольку мы также настроили интеграцию с Spring Security, мы можем создавать исключения, связанные с безопасностью:

throw new AccessDeniedException("You can't delete this task");

И получить надлежащий ответ:

{
    "title": "Forbidden",
    "status": 403,
    "detail": "You can't delete this task"
}

8. Создание пользовательской проблемы

Можно создать пользовательскую реализацию Проблемы . Нам просто нужно расширить Абстрактную выбрасываемую проблему класс:

public class TaskNotFoundProblem extends AbstractThrowableProblem {

    private static final URI TYPE
      = URI.create("https://example.org/not-found");

    public TaskNotFoundProblem(Long taskId) {
        super(
          TYPE,
          "Not found",
          Status.NOT_FOUND,
          String.format("Task '%s' not found", taskId));
    }

}

И мы можем бросить нашу пользовательскую проблему следующим образом:

if (MY_TASKS.containsKey(taskId)) {
    return MY_TASKS.get(taskId);
} else {
    throw new TaskNotFoundProblem(taskId);
}

В результате выбрасывания Задачи Не найдена Проблема проблема, мы получим:

{
    "type": "https://example.org/not-found",
    "title": "Not found",
    "status": 404,
    "detail": "Task '3' not found"
}

9. Работа со следами стека

Если мы хотим включить трассировки стека в ответ, нам необходимо соответствующим образом настроить наш модуль Problem :

ObjectMapper mapper = new ObjectMapper()
  .registerModule(new ProblemModule().withStackTraces());

Причинно-следственная цепочка причин отключена по умолчанию, но мы можем легко включить ее, переопределив поведение:

@ControllerAdvice
class ExceptionHandling implements ProblemHandling {

    @Override
    public boolean isCausalChainsEnabled() {
        return true;
    }

}

После включения обеих функций мы получим ответ, похожий на этот:

{
  "title": "Internal Server Error",
  "status": 500,
  "detail": "Illegal State",
  "stacktrace": [
    "org.example.ExampleRestController
      .newIllegalState(ExampleRestController.java:96)",
    "org.example.ExampleRestController
      .nestedThrowable(ExampleRestController.java:91)"
  ],
  "cause": {
    "title": "Internal Server Error",
    "status": 500,
    "detail": "Illegal Argument",
    "stacktrace": [
      "org.example.ExampleRestController
        .newIllegalArgument(ExampleRestController.java:100)",
      "org.example.ExampleRestController
        .nestedThrowable(ExampleRestController.java:88)"
    ],
    "cause": {
      // ....
    }
  }
}

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

В этой статье мы рассмотрели, как использовать веб-библиотеку Problem Spring для создания ответов с подробными сведениями об ошибках с помощью application/problem+json response. Мы также узнали, как настроить библиотеку в нашем приложении Spring Boot и создать пользовательскую реализацию объекта Problem .

Реализацию этого руководства можно найти в проекте GitHub – это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.