В этой статье в основном описывается, как использовать возможности интерфейса ErrorController, предоставляемые SpringBoot; он имеет встроенный BasicErrorController для единообразной обработки исключений. Когда в контроллере возникает исключение, он автоматически перенаправляет запрос на путь запроса /error ( /error — это сопоставление по умолчанию, предоставляемое SpringBoot). BasicErrorController выдает две ошибки возврата: 1. возврат страницы, 2. возврат json.
задний план
При разработке возникла проблема: все остальные запросы в проекте возвращаются в виде json, а унифицированный объект структуры данных настроен следующим образом:
public class Response<T> {
// 数据
private T data;
// success 标记
private boolean success;
// 异常信息
private String error;
// 省略 get set
}
Эта структура очень распространена, и я полагаю, что многие разработчики играли именно так. Все результаты, возвращаемые оставшимся запросом в проекте, возвращаются в виде объектов Response следующим образом:
@RequestMapping("test")
public Response<String> testApi(){
Response<String> result = new Response<>();
result.setData("this is glmapper blog");
result.setSuccess(true);
return result;
}
По сути, это упрощенная версия модели; из соображений безопасности теперь требуется проверять каждый запрос, например проверять, содержит ли запрос токен. Идея очень проста — перехватывать и проверять запросы с помощью перехватчиков или фильтров.
На самом деле, будь то перехватчик или фильтр, проблема, которую необходимо рассмотреть, заключается в том, как вернуть информацию об исключении в унифицированном формате данных, указанном в проекте, то есть вернуть Response, когда проверка не удалась или произошло исключение во время проверки.
Напишите ответ прямо обратно
Используйте PrintWriter, предоставленный в ServletResponse, чтобы напрямую распечатать ответ в формате json. Примерный код выглядит следующим образом:
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String requestURI = request.getRequestURI();
// mock 测试异常请求
if (requestURI.contains("testTokenError")) {
Response<String> response = new Response<>();
response.setError("token validation fails");
// 回写异常信息
returnResponse((HttpServletResponse)servletResponse,JSONObject.toJSONString(response));
// 返回
return;
}
chain.doFilter(servletRequest, servletResponse);
}
private void returnResponse(HttpServletResponse response, String data) {
PrintWriter writer = null;
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=utf-8");
try {
writer = response.getWriter();
// 通过 PrintWriter 将 data 数据直接 print 回去
writer.print(data);
} catch (IOException e) {
} finally {
if (writer != null)
writer.close();
}
}
Этот метод относительно прост и понятен.После печати аномальных данных вернитесь напрямую и не продолжайте цепочку фильтров.
Сгенерировать исключение и обработать его с помощью BasicErrorController.
Этот метод использует возможности, предоставляемые самой SpringBoot, что позволяет более изящно обрабатывать сообщения об ошибках. Код примерно такой:
1. Выдает исключение прямо в фильтре
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String requestURI = request.getRequestURI();
// mock 测试异常请求
if (requestURI.contains("testTokenError")) {
// 直接返回一个自定义的异常
throw new ValidationException("token validation fails");
}
chain.doFilter(servletRequest, servletResponse);
}
2. Определите контроллер обработки исключений
Определите здесь TokenErrorController, наследуйте от класса BasicErrorController, предоставленного SpringBoot, а затем переопределите метод ошибки (если это страница, переопределите метод errorHtml) для возврата пользовательских данных ответа. код показывает, как показано ниже:
@RestController
public class TokenErrorController extends BasicErrorController {
// 重写 error 方法
@Override
@RequestMapping(produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
// 拿到 body 中的异常 message
String message = body.get("message").toString();
// 构建 Response 对象
Response response = new Response();
// 将 message 的 设置到 response
response.setError(message);
// 返回
return new ResponseEntity(response, status);
}
// 省略其他无关代码
}
Это позволяет обрабатывать только исключения, созданные в дополнительных фильтрах, без изменения какого-либо кода в предыдущем проекте. Следует отметить, что описанное выше предназначено для принятия информации об исключении, созданной фильтром, через BasicErrorController, а затем для переноса и возврата информации об исключении через BasicErrorController. Зачем это поднимать? Основная цель состоит в том, чтобы отличить его от двух аннотаций, предоставляемых уровнем запроса REST в SpringBoot для обработки глобальных исключений.Эти две аннотации — @ControllerAdvice и @RestControllerAdvice.Из названия аннотации видно, что в SpringBoot вы можете Эти две аннотации используются для реализации глобального перехвата классов, помеченных @Controller и @RestController.Поскольку это перехват АОП на уровне контроллера, для исключений, созданных в фильтре, глобальные исключения, определенные аннотациями @ControllerAdvice и @RestControllerAdvice Процессор не могу справиться с этим.
Ниже приведено краткое введение в использование двух аннотаций @ControllerAdvice и @RestControllerAdvice.
глобальная обработка исключений
Настройте OtherExcepetion, а затем используйте аннотацию @RestControllerAdvice, чтобы написать глобальный обработчик исключений.
@RestControllerAdvice
public class OtherExceptionHandler {
// 这里只处理 OtherException 异常类型
@ExceptionHandler(value = OtherException.class)
public Response<String> otherExceptionHandler(HttpServletRequest req, OtherException e){
Response response = new Response();
response.setError(e.getMessage());
return response;
}
// 当然你也可以定义处理其他异常的 @ExceptionHandler
}
Этот метод не может обрабатывать исключения в фильтре, а только исключения, создаваемые в контроллере.
резюме
В этой статье в основном описывается, как гарантировать, что исключение, созданное в фильтре, может быть возвращено в объекте того же типа, что и бизнес в SpringBoot, и кратко представлена обработка захвата исключений на основе контроллера, предоставляемая в SpringBoot. Два метода обработки исключений различны:
- BasicErrorController: принять обработку запроса на исключение из /error, исключение, созданное в фильтре, сначала перенаправляется в /error, а затем обрабатывается.
- @RestControllerAdvice: выполняя перехват AOP для всех классов, отмеченных аннотациями @Controller, можно сопоставить определенные обработчики исключений для обработки в соответствии с типами исключений.
Уровень ограничен, если статья неверна, надеюсь, вы меня поправите~