Эта статья впервые будет проанализировать механизм обработки исключений SPRINGMVC до 5 весны, затем пружин Global 2 WebFlux в основном на механизме обработки загрузки загрузки.
Обработка исключений в SpringMVC
В Spring есть три способа унифицировать обработку исключений, а именно:
- использовать
@ExceptionHandler
аннотация - выполнить
HandlerExceptionResolver
интерфейс - использовать
@controlleradvice
аннотация
использовать@ExceptionHandler
аннотация
Используется для захвата локального метода в том же классе контроллера, что и метод, вызывающий исключение:
@Controller
public class BuzController {
@ExceptionHandler({NullPointerException.class})
public String exception(NullPointerException e) {
System.out.println(e.getMessage());
e.printStackTrace();
return "null pointer exception";
}
@RequestMapping("test")
public void test() {
throw new NullPointerException("出错了!");
}
}
Приведенный выше код реализован дляBuzController
брошенныйNullPointerException
Exception поймает локальное исключение и вернет указанный контент.
выполнитьHandlerExceptionResolver
интерфейс
путем реализацииHandlerExceptionResolver
Интерфейс, здесь мы передаем наследованиеSimpleMappingExceptionResolver
Класс реализации (HandlerExceptionResolver
Реализация, которая позволяет сопоставлять имена классов исключений с именами представлений либо для заданного набора обработчиков, либо для всех обработчиков в DispatcherServlet) Определите глобальные исключения:
@Component
public class CustomMvcExceptionHandler extends SimpleMappingExceptionResolver {
private ObjectMapper objectMapper;
public CustomMvcExceptionHandler() {
objectMapper = new ObjectMapper();
}
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
Object o, Exception ex) {
response.setStatus(200);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache, must-revalidate");
Map<String, Object> map = new HashMap<>();
if (ex instanceof NullPointerException) {
map.put("code", ResponseCode.NP_EXCEPTION);
} else if (ex instanceof IndexOutOfBoundsException) {
map.put("code", ResponseCode.INDEX_OUT_OF_BOUNDS_EXCEPTION);
} else {
map.put("code", ResponseCode.CATCH_EXCEPTION);
}
try {
map.put("data", ex.getMessage());
response.getWriter().write(objectMapper.writeValueAsString(map));
} catch (Exception e) {
e.printStackTrace();
}
return new ModelAndView();
}
}
Поскольку используется приведенный выше пример, мы можем настроить реакцию на ошибку в соответствии с различными исключениями.
использовать@controlleradvice
аннотация
@ControllerAdvice
public class ExceptionController {
@ExceptionHandler(RuntimeException.class)
public ModelAndView handlerRuntimeException(RuntimeException ex) {
if (ex instanceof MaxUploadSizeExceededException) {
return new ModelAndView("error").addObject("msg", "文件太大!");
}
return new ModelAndView("error").addObject("msg", "未知错误:" + ex);
}
@ExceptionHandler(Exception.class)
public ModelAndView handlerMaxUploadSizeExceededException(Exception ex) {
if (ex != null) {
return new ModelAndView("error").addObject("msg", ex);
}
return new ModelAndView("error").addObject("msg", "未知错误:" + ex);
}
}
Отличие от первого способа в том, чтоExceptionHandler
Определение и перехват исключений можно расширить глобально.
Обработка исключений для Spring 5 Webflux
Webflux поддерживает аннотации mvc, что очень удобно, по сравнению с RouteFunction автоматическое сканирование и регистрация проще. Обработка исключений может следовать за ExceptionHandler. Следующая глобальная обработка исключений по-прежнему действительна для RestController.
@RestControllerAdvice
public class CustomExceptionHandler {
private final Log logger = LogFactory.getLog(getClass());
@ExceptionHandler(Exception.class)
@ResponseStatus(code = HttpStatus.OK)
public ErrorCode handleCustomException(Exception e) {
logger.error(e.getMessage());
return new ErrorCode("e","error" );
}
}
Пример WebFlux
WebFlux предоставляет набор функциональных интерфейсов, которые можно использовать для достижения эффектов, подобных MVC. Начнем с двух часто используемых.
Вариант осуществления определения контроллера Обработка логического запроса, основные аспекты:
- Логика обработки определения метода;
- Затем используйте аннотацию @RequestMapping, чтобы определить, на какой URL-адрес отвечает этот метод.
В режиме функциональной разработки WebFlux мы используем HandlerFunction и RouterFunction для достижения двух вышеуказанных пунктов.
HandlerFunction
HandlerFunction
Эквивалентно конкретному методу обработки в контроллере, ввод — это запрос, а вывод — ответ, установленный в Mono:
Mono<T> handle(ServerRequest var1);
В WebFlux запросы и ответы больше не являются ServletRequest и ServletResponse в WebMVC, а являются ServerRequest и ServerResponse. Последние представляют собой интерфейсы, используемые в реактивном программировании, они обеспечивают поддержку функций неблокировки и обратного давления, а также методы преобразования тел сообщений Http в реактивные типы Mono и Flux.
@Component
public class TimeHandler {
public Mono<ServerResponse> getTime(ServerRequest serverRequest) {
String timeType = serverRequest.queryParam("type").get();
//return ...
}
}
Как определено вышеTimeHandler
Возвращает текущее время согласно параметрам запроса.
RouterFunction
RouterFunction
, как следует из названия, маршрутизация, эквивалентная@RequestMapping
, используемый для определения того, какой тип URL-адреса соответствует этому конкретному URL-адресу.HandlerFunction
. Ввод - это запрос, вывод - в моноHandlerfunction
:
Mono<HandlerFunction<T>> route(ServerRequest var1);
Для функций, которые мы хотим предоставить извне, мы определяем Route.
@Configuration
public class RouterConfig {
private final TimeHandler timeHandler;
@Autowired
public RouterConfig(TimeHandler timeHandler) {
this.timeHandler = timeHandler;
}
@Bean
public RouterFunction<ServerResponse> timerRouter() {
return route(GET("/time"), req -> timeHandler.getTime(req));
}
}
Вы можете видеть, что запрос GET на доступ / время будет отправленTimeHandler::getTime
иметь дело с.
Обращение с исключениями на уровне функций
Если мы вызовем тот же адрес запроса без указания типа (типа) времени, например /time, будет выброшено исключение. API-интерфейсы Mono и Flux имеют два встроенных ключевых оператора для обработки ошибок на функциональном уровне.
Обработка ошибок с помощью onErrorResume
Вы также можете использовать onErrorResume для обработки ошибок.Резервный метод определяется следующим образом:
Mono<T> onErrorResume(Function<? super Throwable, ? extends Mono<? extends T>> fallback);
Когда возникает ошибка, мы используем резервный метод для выполнения альтернативного пути:
@Component
public class TimeHandler {
public Mono<ServerResponse> getTime(ServerRequest serverRequest) {
String timeType = serverRequest.queryParam("time").orElse("Now");
return getTimeByType(timeType).flatMap(s -> ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN).syncBody(s))
.onErrorResume(e -> Mono.just("Error: " + e.getMessage()).flatMap(s -> ServerResponse.ok().contentType(MediaType.TEXT_PLAIN).syncBody(s)));
}
private Mono<String> getTimeByType(String timeType) {
String type = Optional.ofNullable(timeType).orElse(
"Now"
);
switch (type) {
case "Now":
return Mono.just("Now is " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
case "Today":
return Mono.just("Today is " + new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
default:
return Mono.empty();
}
}
}
В приведенной выше реализации всякий раз, когдаgetTimeByType()
Когда вы сгенерируете исключение, мы выполним наше определение.fallback
метод. В дополнение к этому мы также можем перехватывать, оборачивать и повторно выдавать исключения, например, в качестве пользовательских бизнес-исключений:
public Mono<ServerResponse> getTime(ServerRequest serverRequest) {
String timeType = serverRequest.queryParam("time").orElse("Now");
return ServerResponse.ok()
.body(getTimeByType(timeType)
.onErrorResume(e -> Mono.error(new ServerException(new ErrorCode(HttpStatus.BAD_REQUEST.value(),
"timeType is required", e.getMessage())))), String.class);
}
Обработка ошибок с помощью onErrorReturn
Всякий раз, когда возникает ошибка, мы можем использоватьonErrorReturn()
Вернуть статические значения по умолчанию:
public Mono<ServerResponse> getDate(ServerRequest serverRequest) {
String timeType = serverRequest.queryParam("time").get();
return getTimeByType(timeType)
.onErrorReturn("Today is " + new SimpleDateFormat("yyyy-MM-dd").format(new Date()))
.flatMap(s -> ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN).syncBody(s));
}
глобальная обработка исключений
Приведенная выше конфигурация обрабатывает исключения на уровне метода.Как и глобальная обработка исключений для аннотированных контроллеров, режим функциональной разработки WebFlux также может обрабатывать глобальные исключения. Для этого нам просто нужно настроить глобальное свойство ответа на ошибку и реализовать глобальную логику обработки ошибок.
Мы имеем дело с исключениями, брошенными вашим государством, будет автоматически преобразован в текст ошибки HTTP и JSON. Чтобы настроить их, мы можем просто расширитьDefaultErrorAttributes
класс и переопределить егоgetErrorAttributes()
метод:
@Component
public class GlobalErrorAttributes extends DefaultErrorAttributes {
public GlobalErrorAttributes() {
super(false);
}
@Override
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
return assembleError(request);
}
private Map<String, Object> assembleError(ServerRequest request) {
Map<String, Object> errorAttributes = new LinkedHashMap<>();
Throwable error = getError(request);
if (error instanceof ServerException) {
errorAttributes.put("code", ((ServerException) error).getCode().getCode());
errorAttributes.put("data", error.getMessage());
} else {
errorAttributes.put("code", HttpStatus.INTERNAL_SERVER_ERROR);
errorAttributes.put("data", "INTERNAL SERVER ERROR");
}
return errorAttributes;
}
//...有省略
}
В приведенной выше реализации мы имеемServerException
обрабатывается специально, в зависимости от поступающихErrorCode
Объект создает соответствующий ответ.
Далее давайте реализуем глобальный обработчик ошибок. Для этой цели Spring предоставляет удобныйAbstractErrorWebExceptionHandler
Класс для расширения и реализации при работе с глобальными ошибками:
@Component
@Order(-2)
public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
//构造函数
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(final ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
private Mono<ServerResponse> renderErrorResponse(final ServerRequest request) {
final Map<String, Object> errorPropertiesMap = getErrorAttributes(request, true);
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(errorPropertiesMap));
}
}
Это устанавливает порядок глобальных обработчиков ошибок равным -2. Это для того, чтобы сделать его более@Order(-1)
ЗарегистрированоDefaultErrorWebExceptionHandler
Обработчик имеет более высокий приоритет.
Объект errorAttributes будет точной копией объекта, который мы передали в конструкторе обработчика сетевых исключений. В идеале это должен быть наш собственный класс атрибутов ошибок. Затем мы ясно даем понять, что хотим направить все запросы на обработку ошибок в метод renderErrorResponse(). Наконец, мы получаем атрибуты ошибки и вставляем их в тело ответа сервера.
Затем он генерирует ответ JSON с подробной информацией об ошибке, статусе HTTP и сообщением об исключении на стороне клиента компьютера. Для клиентов браузера у него есть обработчик ошибок whitelabel, который отображает те же данные в HTML. Конечно, это можно настроить.
резюме
В этом документе говорилось о механизме обработки исключений SpringMVC до 5 Spring, унифицированной обработке исключений SpringMVC. Существует три способа: использование@ExceptionHandler
HandlerExceptionResolver
интерфейс, использование@controlleradvice
Аннотации; затем создайте веб-приложение через функциональный интерфейс WebFlux и объясните функциональный уровень и глобальный механизм обработки исключений Spring Boot 2 Webflux (для стиля Spring WebMVC, если вы пишете отзывчивые веб-сервисы на основе аннотаций, вы все равно можете использовать SpringMVC для унификации обработки исключений.
Примечание. Вторая половина этой статьи в основном переведена сWoohoo. Возьмите Arlington Terrier.com/spring-web ..