Эта статья участвует в "Месяце тем Java - Заметки по отладке Java", подробности см.Ссылка на мероприятие
Обычно унифицированная обработка исключений, которую мы настроили в Spring Boot (@RestControllerAdvice
Сотрудничать@ExceptionHandler
реализация) может обрабатывать только исключения, генерируемые контроллером. Некоторые запросы имеют исключения до того, как они достигают контроллера, и эти исключения не могут быть перехвачены унифицированными исключениями, такими как некоторые исключения в контейнере сервлета. Сегодня я столкнулся с одним в разработке проекта, который меня очень огорчил, потому что формат возвращаемого им сообщения об ошибке не может быть обработан единообразно, поэтому я решил найти решение для решения этой проблемы.
ErrorPageFilter
Я считаю, что такая картина не редкость.Пока Spring Boot делает ошибку, это то, что отражается на странице. Если вы получаете исключение с чем-то вроде Postman:
{
"timestamp": "2021-04-29T22:45:33.231+0000",
"status": 500,
"message": "Internal Server Error",
"path": "foo/bar"
}
Как это достигается? Spring Boot регистрируетErrorPageFilter
, когда в сервлете возникает исключение, фильтр перехватит обработку и обработает исключение по разным стратегиям:Если исключение уже обрабатывается, обработайте его напрямую, в противном случае перенаправьте его на соответствующую страницу с ошибкой..有兴趣的可以去看下源码,逻辑不复杂,这里就不贴了。
Кроме того, когда сервлет выдает аномалию, исключение службы доступно изHttpServletRequest
Получите несколько свойств, как показано ниже:
Мы можем получить информацию об исключении из нескольких свойств выше.
Страница ошибки по умолчанию
Обычно Spring Boot переходит к значению по умолчанию, когда возникает исключение/error
обрабатывается, пока/error
Соответствующая логика задается выражениемBasicErrorController
осуществленный.
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
//返回错误页面
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
// 返回json
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<>(status);
}
Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
return new ResponseEntity<>(body, status);
}
// 其它省略
}
И соответствующая конфигурация:
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes,
ObjectProvider<ErrorViewResolver> errorViewResolvers) {
return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
errorViewResolvers.orderedStream().collect(Collectors.toList()));
}
Итак, нам просто нужно заново реализоватьErrorController
А внедрение Spring IoC может заменить механизм обработки по умолчанию. И мы можем ясно найти этоBasicErrorController
не толькоErrorController
Реализация также является контроллером.Если мы позволим методу контроллера генерировать исключение, оно определенно может быть обработано пользовательским унифицированным исключением. так что я правBasicErrorController
Обновлено:
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class ExceptionController extends AbstractErrorController {
public ExceptionController(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
@Override
@Deprecated
public String getErrorPath() {
return null;
}
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
throw new RuntimeException(getErrorMessage(request));
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
throw new RuntimeException(getErrorMessage(request));
}
private String getErrorMessage(HttpServletRequest request) {
Object code = request.getAttribute("javax.servlet.error.status_code");
Object exceptionType = request.getAttribute("javax.servlet.error.exception_type");
Object message = request.getAttribute("javax.servlet.error.message");
Object path = request.getAttribute("javax.servlet.error.request_uri");
Object exception = request.getAttribute("javax.servlet.error.exception");
return String.format("code: %s,exceptionType: %s,message: %s,path: %s,exception: %s",
code, exceptionType, message, path, exception);
}
}
Непосредственное создание исключений — это просто и экономно! Большинство пойманных здесь исключений не прошли через Контроллер, мы проходимExceptionController
Relay также позволяет единообразно обрабатывать эти исключения, гарантируя, что обработка исключений во всем приложении поддерживает единый внешний вид. Я не знаю, есть ли у вас лучший способ, добро пожаловать, чтобы оставить сообщение для обсуждения.