Обработка исключений службы REST Spring Boot

173

Я пытаюсь настроить крупномасштабный сервер служб REST. Мы используем Spring Boot 1.2.1 Spring 4.1.5 и Java 8. Наши контроллеры реализуют @RestController и стандартные аннотации @RequestMapping.

Моя проблема в том, что Spring Boot устанавливает перенаправление по умолчанию для исключений контроллера /error. Из документов:

Spring Boot обеспечивает отображение / error по умолчанию, которое обрабатывает все ошибки разумным способом, и оно регистрируется как «глобальная» страница ошибок в контейнере сервлета.

Начиная с многолетнего написания REST-приложений на Node.js, для меня это не очень разумно. Любое исключение, которое генерирует конечная точка службы, должно возвращаться в ответе. Я не могу понять, почему вы отправляете перенаправление тем, кто, скорее всего, является пользователем Angular или JQuery SPA, который только ищет ответ и не может или не будет предпринимать никаких действий по перенаправлению.

Я хочу настроить глобальный обработчик ошибок, который может принимать любое исключение - либо намеренно выбрасывать из метода сопоставления запросов, либо автоматически генерировать Spring (404, если не найден метод обработчика для сигнатуры пути запроса), и возвращать стандартный отформатированный ответ об ошибке (400, 500, 503, 404) клиенту без каких-либо перенаправлений MVC. В частности, мы собираемся принять ошибку, зарегистрировать ее в NoSQL с UUID, а затем вернуть клиенту правильный код ошибки HTTP с UUID записи журнала в теле JSON.

Документы были расплывчаты о том, как это сделать. Мне кажется, что вы должны либо создать свою собственную реализацию ErrorController, либо каким- либо образом использовать ControllerAdvice , но все примеры, которые я видел, по-прежнему включают пересылку ответа на какое-то отображение ошибок, которое не помогает. Другие примеры предполагают, что вам нужно будет перечислить все типы исключений, которые вы хотите обработать, вместо того, чтобы просто перечислять «Throwable» и получать все.

Может кто-нибудь сказать мне, что я пропустил, или указать мне правильное направление, как это сделать, не предлагая цепочку, с которой было бы легче иметь дело с Node.js?

ogradyjd
источник
6
Клиент никогда не отправляет перенаправление. Перенаправление обрабатывается внутренне контейнером сервлета (например, Tomcat).
OrangeDog
1
Мне нужно было удалить аннотации @ResponseStatus в моих обработчиках исключений; см. stackoverflow.com/questions/35563968/…
pmorken

Ответы:

131

Новый ответ (2016-04-20)

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

Новый шаг 1. Легко и менее навязчиво добавить следующие свойства в application.properties:

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

Гораздо проще, чем изменить существующий экземпляр DispatcherServlet (как показано ниже)! - JO '

При работе с полным RESTful-приложением очень важно отключить автоматическое сопоставление статических ресурсов, поскольку, если вы используете конфигурацию Spring Boot для обработки статических ресурсов, обработчик ресурсов будет обрабатывать запрос (он упорядочен последним и сопоставлен с / ** это означает, что он принимает любые запросы, которые не были обработаны каким-либо другим обработчиком в приложении), поэтому у сервлета-диспетчера нет возможности вызвать исключение.


Новый ответ (2015-12-04)

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

Новый шаг 1 - я нашел гораздо менее навязчивый способ установки флага "throExceptionIfNoHandlerFound". Замените приведенный ниже код замены DispatcherServlet (Шаг 1) следующим образом в классе инициализации вашего приложения:

@ComponentScan()
@EnableAutoConfiguration
public class MyApplication extends SpringBootServletInitializer {
    private static Logger LOG = LoggerFactory.getLogger(MyApplication.class);
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
        DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    }

В этом случае мы устанавливаем флаг в существующем DispatcherServlet, который сохраняет любую автоконфигурацию средой Spring Boot.

Еще одна вещь, которую я нашел - аннотация @EnableWebMvc смертельно опасна для Spring Boot. Да, эта аннотация позволяет, например, перехватывать все исключения контроллера, как описано ниже, но также убивает МНОГО полезной автоматической конфигурации, которую обычно обеспечивает Spring Boot. Используйте эту аннотацию с особой осторожностью при использовании Spring Boot.


Оригинальный ответ:

После долгих исследований и отслеживания решений, опубликованных здесь (спасибо за помощь!) И немалого количества трассировки времени выполнения в коде Spring, я наконец нашел конфигурацию, которая будет обрабатывать все исключения (не ошибки, а чтение) в том числе 404 с.

Шаг 1 - скажите SpringBoot прекратить использование MVC для ситуаций «обработчик не найден». Мы хотим, чтобы Spring генерировал исключение, а не возвращал клиенту перенаправление представления на «/ error». Для этого вам нужно иметь запись в одном из ваших классов конфигурации:

// NEW CODE ABOVE REPLACES THIS! (2015-12-04)
@Configuration
public class MyAppConfig {
    @Bean  // Magic entry 
    public DispatcherServlet dispatcherServlet() {
        DispatcherServlet ds = new DispatcherServlet();
        ds.setThrowExceptionIfNoHandlerFound(true);
        return ds;
    }
}

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

Шаг 2 - Теперь, когда весенняя загрузка выдает исключение, когда обработчик не найден, это исключение может быть обработано с любыми другими в унифицированном обработчике исключений:

@EnableWebMvc
@ControllerAdvice
public class ServiceExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    ResponseEntity<Object> handleControllerException(HttpServletRequest req, Throwable ex) {
        ErrorResponse errorResponse = new ErrorResponse(ex);
        if(ex instanceof ServiceException) {
            errorResponse.setDetails(((ServiceException)ex).getDetails());
        }
        if(ex instanceof ServiceHttpException) {
            return new ResponseEntity<Object>(errorResponse,((ServiceHttpException)ex).getStatus());
        } else {
            return new ResponseEntity<Object>(errorResponse,HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Map<String,String> responseBody = new HashMap<>();
        responseBody.put("path",request.getContextPath());
        responseBody.put("message","The URL you have reached is not in service at this time (404).");
        return new ResponseEntity<Object>(responseBody,HttpStatus.NOT_FOUND);
    }
    ...
}

Имейте в виду, что я думаю, что аннотация "@EnableWebMvc" здесь важна. Кажется, что ничего из этого не работает без него. И все - ваше загрузочное приложение Spring теперь будет перехватывать все исключения, включая 404, в вышеприведенном классе обработчиков, и вы можете делать с ними, как вам угодно.

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

Любые комментарии / исправления / улучшения будут оценены.

ogradyjd
источник
вместо создания нового bean-компонента сервлета диспетчера вы можете перевернуть флаг в постпроцессоре: YourClass реализует BeanPostProcessor {... `public Object postProcessBeforeInitialization (Object bean, String beanName) бросает BeansException {if (экземпляр объекта DispatcherServlet) {// в противном случае мы получить 404 до того, как наш обработчик исключений сработает в компоненте ((DispatcherServlet)) .setThrowExceptionIfNoHandlerFound (true); } вернуть боб; } public Object postProcessAfterInitialization (Object bean, String beanName) бросает BeansException {return bean; }
wwadge
1
У меня есть эта проблема, но настройка DispatcherServlet не работает для меня. Есть ли какая-либо дополнительная магия, необходимая для загрузки, чтобы использовать этот дополнительный компонент и конфигурацию?
Ян Гилхэм
3
@IanGilham У меня тоже не получается работать с Spring Boot 1.2.7. Я даже не получаю никакого @ExceptionHandlerметода, вызываемого при помещении его в @ControllerAdviceкласс, хотя они правильно работают, если помещены в @RestControllerкласс. @EnableWebMvcнаходится на @ControllerAdviceи @Configuration(я проверял каждую комбинацию) класс. Любая идея или рабочий пример? // @ Энди Уилкинсон
FrVaBe
1
Любой, кто читает этот вопрос и ответ, должен взглянуть на соответствующую проблему SpringBoot на github .
FrVaBe
1
Не уверен, @agpt. У меня есть внутренний проект, который я могу перейти на 1.3.0 и посмотреть, как это повлияет на мои настройки, и сообщить вам, что я нахожу.
ogradyjd
41

В Spring Boot 1.4+ были добавлены новые классные классы для облегчения обработки исключений, которые помогают удалить шаблонный код.

Для @RestControllerAdviceобработки исключений предусмотрено новое , это комбинация @ControllerAdviceи @ResponseBody. Вы можете удалить метод @ResponseBodyon @ExceptionHandlerпри использовании этой новой аннотации.

т.е.

@RestControllerAdvice
public class GlobalControllerExceptionHandler {

    @ExceptionHandler(value = { Exception.class })
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiErrorResponse unknownException(Exception ex, WebRequest req) {
        return new ApiErrorResponse(...);
    }
}

Для обработки 404 ошибок достаточно добавить @EnableWebMvcаннотацию и следующее в application.properties:
spring.mvc.throw-exception-if-no-handler-found=true

Вы можете найти и поиграть с источниками здесь:
https://github.com/magiccrafter/spring-boot-exception-handling

magiccrafter
источник
7
Это действительно полезно, спасибо. Но я не понял, почему мы должны `@EnableWebMvc` с` spring.mvc.throw-exception-if-no-handler-found = true `. Я ожидал обработать все исключения через @RestControllerAdviceбез дополнительной настройки. Что мне здесь не хватает?
fiskra
28

Я думаю, ResponseEntityExceptionHandlerотвечает вашим требованиям. Пример кода для HTTP 400:

@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {

  @ResponseStatus(value = HttpStatus.BAD_REQUEST)
  @ExceptionHandler({HttpMessageNotReadableException.class, MethodArgumentNotValidException.class,
      HttpRequestMethodNotSupportedException.class})
  public ResponseEntity<Object> badRequest(HttpServletRequest req, Exception exception) {
    // ...
  }
}

Вы можете проверить этот пост

Эфе Кахраман
источник
6
Я видел этот код раньше, и после его реализации класс перехватил исключения, возникающие в методах сопоставления запросов контроллера. Это все еще не перехватывает 404 ошибки, которые обрабатываются в методе ResourceHttpRequestHandler.handleRequest или, если используется аннотация @EnableWebMvc, в DispatcherServlet.noHandlerFound. Мы хотим справиться с любой ошибкой, включая 404, но последняя версия Spring Boot кажется невероятно тупой, как это сделать.
ogradyjd
Я написал один и тот же способ обработки HttpRequestMethodNotSupportedExceptionи добавления одного и того же баночка в несколько микро-сервисов, для некоторых бизнес-целей нам нужно ответить псевдонимом микро-сервиса в ответе. Есть ли способ получить основное имя микро-службы / имя контроллера? Я знаю HandlerMethod, предоставит имя метода Java, из которого происходит исключение. Но здесь ни один из методов не получил запрос, поэтому HandlerMethodне будет инициализирован. Так есть ли решение, чтобы решить эту проблему?
Парамеш Корракути
Рекомендации контроллера - это хороший подход, но всегда помните, что исключения не являются частью потока, они должны возникать в исключительных случаях!
Хорхе Товар
17

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

В настоящее время я создаю REST API, который использует Spring Boot 1.5.2.RELEASE с Spring Framework 4.3.7.RELEASE. Я использую подход Java Config (в отличие от конфигурации XML). Кроме того, мой проект использует глобальный механизм обработки исключений с использованием @RestControllerAdviceаннотации (см. Ниже).

У моего проекта те же требования, что и у вас: я хочу, чтобы мой REST API возвращал a HTTP 404 Not Foundс полезной нагрузкой JSON в ответе HTTP клиенту API, когда он пытается отправить запрос на несуществующий URL-адрес. В моем случае полезная нагрузка JSON выглядит следующим образом (что явно отличается от Spring Boot по умолчанию, кстати):

{
    "code": 1000,
    "message": "No handler found for your request.",
    "timestamp": "2017-11-20T02:40:57.628Z"
}

Я наконец заставил это работать. Вот основные задачи, которые вы должны сделать вкратце:

  • Убедитесь, что NoHandlerFoundExceptionвыбрасывается, если клиенты API вызывают URLS, для которых не существует метода-обработчика (см. Шаг 1 ниже).
  • Создайте собственный класс ошибок (в моем случае ApiError), который содержит все данные, которые должны быть возвращены клиенту API (см. Шаг 2).
  • Создайте обработчик исключений, который реагирует на NoHandlerFoundException и возвращает правильное сообщение об ошибке клиенту API (см. Шаг 3).
  • Напишите для него тест и убедитесь, что он работает (см. Шаг 4).

Хорошо, теперь к деталям:

Шаг 1. Настройте application.properties

Мне пришлось добавить следующие два параметра конфигурации в application.propertiesфайл проекта :

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

Это гарантирует, что NoHandlerFoundExceptionон генерируется в тех случаях, когда клиент пытается получить доступ к URL-адресу, для которого не существует метода контроллера, который мог бы обработать запрос.

Шаг 2. Создание класса для ошибок API

Я сделал класс, похожий на предложенный в этой статье в блоге Евгения Параскива. Этот класс представляет ошибку API. Эта информация отправляется клиенту в теле ответа HTTP в случае ошибки.

public class ApiError {

    private int code;
    private String message;
    private Instant timestamp;

    public ApiError(int code, String message) {
        this.code = code;
        this.message = message;
        this.timestamp = Instant.now();
    }

    public ApiError(int code, String message, Instant timestamp) {
        this.code = code;
        this.message = message;
        this.timestamp = timestamp;
    }

    // Getters and setters here...
}

Шаг 3. Создание / настройка глобального обработчика исключений

Я использую следующий класс для обработки исключений (для простоты я удалил операторы импорта, регистрационный код и некоторые другие, не относящиеся к делу части кода):

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ApiError noHandlerFoundException(
            NoHandlerFoundException ex) {

        int code = 1000;
        String message = "No handler found for your request.";
        return new ApiError(code, message);
    }

    // More exception handlers here ...
}

Шаг 4: Написать тест

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

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SprintBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@ActiveProfiles("dev")
public class GlobalExceptionHandlerIntegrationTest {

    public static final String ISO8601_DATE_REGEX =
        "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$";

    @Autowired
    private MockMvc mockMvc;

    @Test
    @WithMockUser(roles = "DEVICE_SCAN_HOSTS")
    public void invalidUrl_returnsHttp404() throws Exception {
        RequestBuilder requestBuilder = getGetRequestBuilder("/does-not-exist");
        mockMvc.perform(requestBuilder)
            .andExpect(status().isNotFound())
            .andExpect(jsonPath("$.code", is(1000)))
            .andExpect(jsonPath("$.message", is("No handler found for your request.")))
            .andExpect(jsonPath("$.timestamp", RegexMatcher.matchesRegex(ISO8601_DATE_REGEX)));
    }

    private RequestBuilder getGetRequestBuilder(String url) {
        return MockMvcRequestBuilders
            .get(url)
            .accept(MediaType.APPLICATION_JSON);
    }

@ActiveProfiles("dev")Аннотации можно оставить далеко. Я использую его только потому, что работаю с разными профилями. Это RegexMatcherпользовательский сопоставитель Hamcrest, который я использую для лучшей обработки полей меток времени. Вот код (я нашел его здесь ):

public class RegexMatcher extends TypeSafeMatcher<String> {

    private final String regex;

    public RegexMatcher(final String regex) {
        this.regex = regex;
    }

    @Override
    public void describeTo(final Description description) {
        description.appendText("matches regular expression=`" + regex + "`");
    }

    @Override
    public boolean matchesSafely(final String string) {
        return string.matches(regex);
    }

    // Matcher method you can call on this matcher class
    public static RegexMatcher matchesRegex(final String string) {
        return new RegexMatcher(regex);
    }
}

Некоторые дополнительные заметки с моей стороны:

  • Во многих других публикациях в StackOverflow люди предлагали установить @EnableWebMvcаннотацию. Это не было необходимо в моем случае.
  • Этот подход хорошо работает с MockMvc (см. Тест выше).
Андре Гассер
источник
Это решило проблему для меня. Просто добавьте, что мне не хватало аннотации @ RestControllerAdvice, поэтому я добавил ее вместе с аннотацией @ ControllerAdvice, чтобы она справлялась со всеми, и это помогло.
PGMacDesign
13

Как насчет этого кода? Я использую аварийное отображение запросов, чтобы поймать 404 ошибки.

@Controller
@ControllerAdvice
public class ExceptionHandlerController {

    @ExceptionHandler(Exception.class)
    public ModelAndView exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception ex) {
        //If exception has a ResponseStatus annotation then use its response code
        ResponseStatus responseStatusAnnotation = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);

        return buildModelAndViewErrorPage(request, response, ex, responseStatusAnnotation != null ? responseStatusAnnotation.value() : HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @RequestMapping("*")
    public ModelAndView fallbackHandler(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return buildModelAndViewErrorPage(request, response, null, HttpStatus.NOT_FOUND);
    }

    private ModelAndView buildModelAndViewErrorPage(HttpServletRequest request, HttpServletResponse response, Exception ex, HttpStatus httpStatus) {
        response.setStatus(httpStatus.value());

        ModelAndView mav = new ModelAndView("error.html");
        if (ex != null) {
            mav.addObject("title", ex);
        }
        mav.addObject("content", request.getRequestURL());
        return mav;
    }

}
Людовик Мартин
источник
6

По умолчанию Spring Boot выдает json с подробной информацией об ошибке.

curl -v localhost:8080/greet | json_pp
[...]
< HTTP/1.1 400 Bad Request
[...]
{
   "timestamp" : 1413313361387,
   "exception" : "org.springframework.web.bind.MissingServletRequestParameterException",
   "status" : 400,
   "error" : "Bad Request",
   "path" : "/greet",
   "message" : "Required String parameter 'name' is not present"
}

Это также работает для всех видов ошибок отображения запросов. Проверьте эту статью http://www.jayway.com/2014/10/19/spring-boot-error-responses/

Если вы хотите создать, войдите в NoSQL. Вы можете создать @ControllerAdvice, где вы будете регистрировать его, а затем повторно выдать исключение. В документации есть пример https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

PaintedRed
источник
DispatcherServlet по умолчанию жестко задан для выполнения перенаправления с MVC, а не для выдачи исключения при получении запроса на несуществующее сопоставление - если только вы не установили флаг, как я делал в посте выше.
ogradyjd
Кроме того, причина, по которой мы реализовали класс ResponseEntityExceptionHandler, заключается в том, что мы можем контролировать формат выходных данных и регистрировать трассировки стека ошибок в решении NoSQL, а затем отправлять безопасное для клиента сообщение об ошибке.
ogradyjd
6

@RestControllerAdvice - это новая функция Spring Framework 4.3 для обработки исключений с помощью RestfulApi с помощью комплексного решения проблемы:

 package com.khan.vaquar.exception;

import javax.servlet.http.HttpServletRequest;

import org.owasp.esapi.errors.IntrusionException;
import org.owasp.esapi.errors.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.khan.vaquar.domain.ErrorResponse;

/**
 * Handles exceptions raised through requests to spring controllers.
 **/
@RestControllerAdvice
public class RestExceptionHandler {

    private static final String TOKEN_ID = "tokenId";

    private static final Logger log = LoggerFactory.getLogger(RestExceptionHandler.class);

    /**
     * Handles InstructionExceptions from the rest controller.
     * 
     * @param e IntrusionException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IntrusionException.class)
    public ErrorResponse handleIntrusionException(HttpServletRequest request, IntrusionException e) {       
        log.warn(e.getLogMessage(), e);
        return this.handleValidationException(request, new ValidationException(e.getUserMessage(), e.getLogMessage()));
    }

    /**
     * Handles ValidationExceptions from the rest controller.
     * 
     * @param e ValidationException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = ValidationException.class)
    public ErrorResponse handleValidationException(HttpServletRequest request, ValidationException e) {     
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);

        if (e.getUserMessage().contains("Token ID")) {
            tokenId = "<OMITTED>";
        }

        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(),
                                    e.getUserMessage());
    }

    /**
     * Handles JsonProcessingExceptions from the rest controller.
     * 
     * @param e JsonProcessingException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = JsonProcessingException.class)
    public ErrorResponse handleJsonProcessingException(HttpServletRequest request, JsonProcessingException e) {     
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(),
                                    e.getOriginalMessage());
    }

    /**
     * Handles IllegalArgumentExceptions from the rest controller.
     * 
     * @param e IllegalArgumentException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IllegalArgumentException.class)
    public ErrorResponse handleIllegalArgumentException(HttpServletRequest request, IllegalArgumentException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(), 
                                    e.getMessage());
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = UnsupportedOperationException.class)
    public ErrorResponse handleUnsupportedOperationException(HttpServletRequest request, UnsupportedOperationException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(), 
                                    e.getMessage());
    }

    /**
     * Handles MissingServletRequestParameterExceptions from the rest controller.
     * 
     * @param e MissingServletRequestParameterException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public ErrorResponse handleMissingServletRequestParameterException( HttpServletRequest request, 
                                                                        MissingServletRequestParameterException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.BAD_REQUEST.value(), 
                                    e.getClass().getSimpleName(), 
                                    e.getMessage());
    }

    /**
     * Handles NoHandlerFoundExceptions from the rest controller.
     * 
     * @param e NoHandlerFoundException
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(value = NoHandlerFoundException.class)
    public ErrorResponse handleNoHandlerFoundException(HttpServletRequest request, NoHandlerFoundException e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.info(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.NOT_FOUND.value(), 
                                    e.getClass().getSimpleName(), 
                                    "The resource " + e.getRequestURL() + " is unavailable");
    }

    /**
     * Handles all remaining exceptions from the rest controller.
     * 
     * This acts as a catch-all for any exceptions not handled by previous exception handlers.
     * 
     * @param e Exception
     * @return error response POJO
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = Exception.class)
    public ErrorResponse handleException(HttpServletRequest request, Exception e) {
        String tokenId = request.getParameter(TOKEN_ID);
        log.error(e.getMessage(), e);
        return new ErrorResponse(   tokenId,
                                    HttpStatus.INTERNAL_SERVER_ERROR.value(), 
                                    e.getClass().getSimpleName(), 
                                    "An internal error occurred");
    }   

}
Ваквар хан
источник
3

Для контроллеров REST я бы рекомендовал использовать Zalando Problem Spring Web.

https://github.com/zalando/problem-spring-web

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

<dependency>
    <groupId>org.zalando</groupId>
    <artifactId>problem-spring-web</artifactId>
    <version>LATEST</version>
</dependency>

А затем определите одну или несколько советов для ваших исключений (или используйте те, которые предусмотрены по умолчанию)

public interface NotAcceptableAdviceTrait extends AdviceTrait {

    @ExceptionHandler
    default ResponseEntity<Problem> handleMediaTypeNotAcceptable(
            final HttpMediaTypeNotAcceptableException exception,
            final NativeWebRequest request) {
        return Responses.create(Status.NOT_ACCEPTABLE, exception, request);
    }

}

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

@ControllerAdvice
class ExceptionHandling implements MethodNotAllowedAdviceTrait, NotAcceptableAdviceTrait {

}
Жан Вальжан
источник
2

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

@Controller
public class CustomErrorController extends BasicErrorController {

    public CustomErrorController(ServerProperties serverProperties) {
        super(new DefaultErrorAttributes(), serverProperties.getError());
    }

    @Override
    public ResponseEntity error(HttpServletRequest request) {
        HttpStatus status = getStatus(request);
        if (status.equals(HttpStatus.INTERNAL_SERVER_ERROR)){
            return ResponseEntity.status(status).body(ResponseBean.SERVER_ERROR);
        }else if (status.equals(HttpStatus.BAD_REQUEST)){
            return ResponseEntity.status(status).body(ResponseBean.BAD_REQUEST);
        }
        return super.error(request);
    }
}

ResponseBeanВот мой заказ POJO для ответа.

Lym Zoy
источник
0

Решение с dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);и @EnableWebMvc @ControllerAdvice работал для меня с Spring Boot 1.3.1, пока не работал на 1.2.7

Деннис Р
источник