Spring MVC предоставляет мне несколько способов настроить обработку исключений.
Ссылка на эту статью:Exception Handling in Spring MVC
Настройте коды состояния HTTP для исключений
По умолчанию, если мы создадим исключение в контроллере, Spring MVC ответит пользователю страницей 500 с подробной информацией об ошибке.
Если мы хотим изменить код состояния HTTP, соответствующий ошибке, мы можем добавить его в соответствующее исключение.@ResponseStatus
Аннотация, с помощью которой мы можем установить код состояния HTTP и сообщение об ошибке, соответствующее этому исключению, например:
@Controller
public class ExceptionController {
@RequestMapping("/")
public void test(){
throw new NotFoundException();
}
}
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "not found")
public class NotFoundException extends RuntimeException{
}
Затем спросите, вы можете обнаружить, что страница отличается:
Обработка перехвата ошибок уровня контроллера
пройти через@ResponseStatus
Обратите внимание, что хотя мы можем настраивать коды состояния HTTP и сообщения об ошибках, этого недостаточно.
Во-первых, вы можете устанавливать только те исключения, которые написали сами, существующие исключения не могут быть расширены.
Во-вторых, страницу ошибки нельзя настроить, и мы практически не используем страницу ошибки по умолчанию.
Для двух вышеупомянутых проблем вы можете добавить в контроллер методы для перехвата и обработки исключений. необходимо использовать метод@ExceptionHandler
аннотация.注解后,方法会拦截токСпособ обработки запроса контроллера (по@RequestMapping
аннотированный метод) выдает исключение. В то же время этот метод перехвата исключения может возвращать представление, которое используется для отображения информации об ошибке. В то же время вы также можете использовать этот метод перехвата исключений.@ResponseStatus
Чтобы реализовать настройку кодов состояния HTTP для существующих исключений, см. пример:
@Controller
public class ExceptionHandlingController {
// 请求处理方法
...
// 异常处理方法
// 定制一个已有异常的HTTP状态码
@ResponseStatus(value=HttpStatus.CONFLICT,
reason="Data integrity violation") // 409
@ExceptionHandler(DataIntegrityViolationException.class)
public void conflict() {
// 啥也不干
}
// 指定view来渲染对应的异常
@ExceptionHandler({SQLException.class,DataAccessException.class})
public String databaseError() {
// Nothing to do. Returns the logical view name of an error page, passed
// to the view-resolver(s) in usual way.
// Note that the exception is NOT available to this view (it is not added
// to the model) but see "Extending ExceptionHandlerExceptionResolver"
// below.
// 啥也不干,就返回异常页面view的名称
// 注意这里的view访问不到异常,因为异常没有添加到model中
return "databaseError";
}
// 拦截该Controller抛出的所有异常,同时把异常信息通过ModelAndView传给视图
// 或者你可以继承ExceptionHandlerExceptionResolver来实现,见下文
@ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception ex) {
logger.error("Request: " + req.getRequestURL() + " raised " + ex);
ModelAndView mav = new ModelAndView();
mav.addObject("exception", ex);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
}
Обратите внимание, что использование@ExceptionHandler
Обязательно укажите, какое исключение обрабатывается, иначе будет сообщено об исключении:java.lang.IllegalArgumentException: No exception types mapped to {public java.lang.String XXController.exceptionHandler()}
Глобальное обращение с исключением
Хотя контроль исключений на уровне контроллера достаточно мощный, мы не можем написать метод handleError для каждого контроллера, поэтому нам нужен глобальный метод обработки исключений. с помощью@ControllerAdvice
Это требование может быть достигнуто просто и непосредственно.
@ControllerAdvice
Это аннотация, добавленная Spring 3.2.Как и имя, эта аннотация предоставляет функцию расширения контроллера, которую можно использовать в классе совета.@ExceptionHandler
,@InitBinder
,@ModelAttribute
Аннотированные методы применяются ко всем контроллерам. Наиболее часто используется@ExceptionHandler
. Изначально нам нужно определить в каждом из контроллеров@ExceptionHandler
Теперь мы можем объявить@ControllerAdvice
класс, затем определите единый@ExceptionHandler
метод.
Например, в приведенном выше примере используйте@ControllerAdvice
записывается следующим образом:
@ControllerAdvice
class GlobalControllerExceptionHandler {
@ResponseStatus(HttpStatus.CONFLICT) // 409
@ExceptionHandler(DataIntegrityViolationException.class)
public void handleConflict() {
// 啥也不干
}
}
Если вы хотите перехватить все ошибки, это то же самое, что и в приведенном выше примере уровня контроллера, установите перехваченное исключение какException.class
Вот и все.
@ControllerAdvice
class GlobalDefaultExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
@ExceptionHandler(value = Exception.class)
public ModelAndView
defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
// 这里需要注意一下,因为这个方法会拦截所有异常,包括设置了@ResponseStatus注解的异常,如果你不想拦截这些异常,可以过滤一下,然后重新抛出
if (AnnotationUtils.findAnnotation
(e.getClass(), ResponseStatus.class) != null)
throw e;
// 组装异常信息给视图
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
}
более глубокий перехват
Перехваты уровня Controller и Controller-Advice, упомянутые выше, основаны на аннотациях и являются расширенными функциями. В базовой реализации Spring используетHandlerExceptionResolver
.
Все определено вDispatcherServlet
Компонент в контексте приложения, если он реализованHandlerExceptionResolver
Интерфейс будет использоваться для обработки перехвата исключений.
Взгляните на определение интерфейса:
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex);
}
handler
Параметр является ссылкой на контроллер, вызвавший исключение.
Spring реализует несколькоHandlerExceptionResolver
, эти классы являются основой для нескольких функций, упомянутых выше:
-
ExceptionHandlerExceptionResolver
: определить, можно ли сопоставить исключение с соответствующим контроллером или советом контроллера.@ExceptionHandler
метод, если он может быть запущен (упомянутая выше функция метода перехвата исключений реализована этим классом) -
ResponseStatusExceptionResolver
: определить, является ли исключение@ResponseStatus
Аннотация, если да, используйте информацию аннотации для обновления ответа (упомянутый выше настраиваемый код состояния HTTP реализован с этой функцией) -
DefaultHandlerExceptionResolver
: преобразование исключений Spring в коды состояния HTTP (используемые внутри Spring).
эти несколькоHandlerExceptionResolver
Он будет выполняться в таком порядке, то есть по цепочке обработки исключений.
Здесь видно,resolveException
сигнатура метода не имеетModel
параметры, поэтому@ExceptionHandler
Метод не может внедрить этот параметр, поэтому в приведенном выше методе перехвата исключений можно только создать новую модель.
Так что, если вам нужно, вы можете унаследовать егоHandlerExceptionResolver
реализовать собственную цепочку обработки исключений. а затем реализоватьOrdered
интерфейс, чтобы можно было контролировать порядок выполнения процессоров.
SimpleMappingExceptionResolver
Spring обеспечивает очень удобный в использованииHandlerExceptionResolver
,ВызовSimpleMappingExceptionResolver
. Имеет множество полезных функций:
- Сопоставьте имена исключений с именами представлений (имена исключений должны указывать только имя класса, а не имя пакета)
- Укажите страницу ошибки по умолчанию
- распечатать исключение в журнал
- Укажите исключение для имени свойства в представлении, имя свойства по умолчанию — исключение. (
@ExceptionHandler
Представление, указанное методом, не может получить исключение по умолчанию, иSimpleMappingExceptionResolver
Указанный вид может)
Использование заключается в следующем:
<bean id="simpleMappingExceptionResolver" class=
"org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<map>
<entry key="DatabaseException" value="databaseError"/>
<entry key="InvalidCreditCardException" value="creditCardError"/>
</map>
</property>
<!-- See note below on how this interacts with Spring Boot -->
<property name="defaultErrorView" value="error"/>
<property name="exceptionAttribute" value="ex"/>
<!-- Name of logger to use to log exceptions. Unset by default,
so logging is disabled unless you set a value. -->
<property name="warnLogCategory" value="example.MvcLogger"/>
</bean>
Конфигурация Java:
@Configuration
@EnableWebMvc // Optionally setup Spring MVC defaults (if you aren't using
// Spring Boot & haven't specified @EnableWebMvc elsewhere)
public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Bean(name="simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver
createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r =
new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "databaseError");
mappings.setProperty("InvalidCreditCardException", "creditCardError");
r.setExceptionMappings(mappings); // None by default
r.setDefaultErrorView("error"); // No default
r.setExceptionAttribute("ex"); // Default is "exception"
r.setWarnLogCategory("example.MvcLogger"); // No default
return r;
}
...
}
Пожалуй, самое полезное здесьdefaultErrorView
Теперь его можно использовать для настройки страницы ошибок по умолчанию.
самоунаследованныйSimpleMappingExceptionResolver
Также очень распространено расширение функциональности
- Унаследованные классы могут устанавливать конфигурацию по умолчанию в конструкторе.
- покрытие
buildLogMessage
Метод настройки информации журнала, фиксированный возврат по умолчанию: выполнение обработчика привело к исключению - покрытие
doResolveException
метод, вы можете передать больше информации, которая вам нужна, в журнал ошибок
Примеры следующие:
public class MyMappingExceptionResolver extends SimpleMappingExceptionResolver {
public MyMappingExceptionResolver() {
// 默认启用日志
setWarnLogCategory(MyMappingExceptionResolver.class.getName());
}
@Override
public String buildLogMessage(Exception e, HttpServletRequest req) {
return "MVC exception: " + e.getLocalizedMessage();
}
@Override
protected ModelAndView doResolveException(HttpServletRequest req,
HttpServletResponse resp, Object handler, Exception ex) {
// 调用父类飞方法来获得ModelAndView
ModelAndView mav = super.doResolveException(req, resp, handler, ex);
// 添加额外的字段给视图
mav.addObject("url", request.getRequestURL());
return mav;
}
}
Обработка исключений REST
В стиле REST возвращаемое сообщение об ошибке представляет собой json вместо страницы, как это сделать? Особенно просто, определите класс, который возвращает информацию:
public class ErrorInfo {
public final String url;
public final String ex;
public ErrorInfo(String url, Exception ex) {
this.url = url;
this.ex = ex.getLocalizedMessage();
}
}
Затем добавьте обработчик ошибок@ResponseBody
Просто делать:
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MyBadDataException.class)
@ResponseBody ErrorInfo
handleBadRequest(HttpServletRequest req, Exception ex) {
return new ErrorInfo(req.getRequestURL(), ex);
}
Когда вы используете спецэффекты?
Весна предоставляет нам много вариантов, как мы выбираем?
- Если исключение объявлено вами, рассмотрите возможность использования
@ResponseStatus
аннотация - Можно использовать другие исключения
@ControllerAdvice
середина@ExceptionHandler
метод или использованиеSimpleMappingExceptionResolver
- Если контроллеру необходимо настроить исключение, его можно добавить в контроллер.
@ExceptionHandler
метод.
Если вы смешиваете эти функции, вы должны обратить внимание на то, что в контроллере@ExceptionHandler
коэффициент приоритета метода@ControllerAdvice
середина@ExceptionHandler
метод высокий, а если их несколько@ControllerAdvice
класс, порядок выполнения не определен.