Обробка винятків з служби Spring Boot REST


172

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

Моя проблема полягає в тому, що Spring Boot встановлює переспрямування за замовчуванням для виключень контролера до /error. З документів:

Spring Boot надає відображення помилок / помилок за замовчуванням, що обробляє всі помилки розумним чином, і воно реєструється як "глобальна" сторінка помилок у контейнері сервлетів.

Починаючи з років написання REST-програм із Node.js, це, для мене, що-небудь розумне. Будь-який виняток, який генерує кінцева точка служби, повинен повертатися у відповідь. Я не можу зрозуміти, чому ви надішлете переадресацію до того, що швидше за все є споживачем Angular або JQuery SPA, який лише шукає відповідь і не може чи не буде робити жодних дій щодо переадресації.

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

Документи були неясними, як це зробити. Мені здається, що вам доведеться або створити власну реалізацію ErrorController, або використовувати ControllerAdvice якось, але всі приклади, які я бачив, все ще включають пересилання відповіді на якесь відображення помилок, що не допомагає. Інші приклади передбачають, що вам доведеться перелічити кожен тип винятку, який ви хочете обробити, а не просто перелічити "Throwable" та отримати все.

Хто-небудь може сказати мені, що я пропустив, або вказати мені в правильному напрямку, як це зробити, не підказуючи ланцюжок, з яким Node.js було б легше мати справу?


6
Клієнт ніколи фактично не надсилає переадресацію. Переадресація обробляється внутрішньо контейнером сервлетів (наприклад, Tomcat).
OrangeDog

1
Видалення анотацій @ResponseStatus з моїх обробників виключень було те, що мені було потрібно; см stackoverflow.com/questions/35563968 / ...
pmorken

Відповіді:


131

Нова відповідь (2016-04-20)

Використання Spring Boot 1.3.1.RELEASE

Новий крок 1 - Додавати такі властивості до програми легко і менш нав'язливо:

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 викинула виняток замість того, щоб повертати клієнту переспрямування подання на "/ помилка". Для цього вам потрібно мати запис в одному з ваших класів конфігурації:

// 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, у вищевказаному класі обробника, і ви можете робити з ними як завгодно.

Останнє питання - здається, не існує способу отримати це, щоб зловити викинуті помилки. У мене є дурне уявлення про використання аспектів для вилучення помилок і перетворення їх на Винятки, з якими можна звернутись до вищевказаного коду, але я ще не встиг реально спробувати його реалізувати. Сподіваюся, що це комусь допоможе.

Будь-які коментарі / виправлення / удосконалення будуть вдячні.


замість створення нового сервілету для диспетчера ви можете перевернути прапор у поштовому процесорі: YourClass реалізує BeanPostProcessor {... `public Object postProcessBeforeInitialization (Object bean, String beanName) викидає BeansException {if (bean instanceof DispatcherServlet) {// інакше ми отримати 404, перш ніж наш обробник винятків запускає ((DispatcherServlet) боб) .setThrowExceptionIfNoHandlerFound (вірно); } повернути боб; } public Object postProcessAfterInitialization (Object bean, String beanName) кидає BeansException {return bean; }
wwadge

1
У мене ця проблема, але налаштування DispatcherServlet не працює для мене. Чи потрібна додаткова магія, щоб Boot використовував цей додатковий боб і конфігурацію?
IanGilham

3
@IanGilham Я теж не змушую це працювати з Spring Boot 1.2.7. Я навіть не отримую жодного @ExceptionHandlerметоду, який називається при розміщенні його в @ControllerAdviceкласі, хоча вони належним чином працюють, якщо їх розміщують у @RestControllerкласі. @EnableWebMvcзнаходиться на @ControllerAdviceі @Configuration(я тестував кожну комбінацію) клас. Будь-яка ідея чи робочий приклад? // @Andy Wilkinson
FrVaBe

1
Хто читає це запитання та відповідь, повинен подивитися відповідну проблему SpringBoot на github .
FrVaBe

1
Не впевнений @agpt. У мене є внутрішній проект, за допомогою якого я можу перейти до 1.3.0 і побачити, який вплив має на моє налаштування, і повідомляти вам, що я знаходжу.
ogradyjd

41

Завдяки Spring Boot 1.4+ були додані нові класні класи для полегшення роботи з виключеннями, що допомагає видалити код котла.

Для @RestControllerAdviceобробки винятків передбачено нове , це комбінація @ControllerAdviceта @ResponseBody. Ви можете видалити @ResponseBodyна @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


7
Це дуже корисно, дякую. Але я не зрозумів, чому нам потрібно "@EnableWebMvc" з "spring.mvc.throw-виключення-якщо-ні-обробник-знайдено = 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його не буде ініціалізовано. То чи є рішення для вирішення цього питання?
Параміш Корракуті

Поради щодо контролерів - це хороший підхід, але завжди пам’ятайте, що винятки не є частиною потоку, вони повинні відбуватися у виняткових випадках!
JorgeTovar

17

Хоча це питання старіше, я хотів би поділитися своїми думками з цього приводу. Я сподіваюся, що це буде корисно для когось із вас.

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

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

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

Я нарешті змусив це працювати. Ось основні завдання, які вам потрібно зробити коротко:

  • Переконайтеся в тому, що він NoHandlerFoundExceptionвидається, якщо клієнти API викликають URL-адреси, для яких не існує методу обробника (див. Крок 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


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 для відповіді.


0

Розв’язував dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);і @EnableWebMvc @ControllerAdvice працював для мене з Spring Boot 1.3.1, тоді як 1.2.7 не працював

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.