Резюме:Обработка исключений SpringBoot.
FundebugПерепечатано с разрешения, авторские права принадлежат оригинальному автору.
предисловие
существуетWeb
В разработке нам часто приходится иметь дело с различными исключениями, что является непростой задачей, у многих людей могут возникнуть следующие проблемы с обработкой исключений:
- Когда нужно снимать(
try-catch
) исключение, когда его нужно выбросить (throws
) исключение для верхнего слоя. - существует
dao
Захват слоя все еще выполняетсяservice
захват, ещеcontroller
Захват слоя. - Что делать после возникновения исключения Как вернуться на страницу с сообщением об ошибке.
пример счетчика обработки исключений
Теперь, когда мы говорим об исключениях, давайте поговорим о контрпримере обработки исключений, который также является ошибкой, которую склонны совершать многие люди.Здесь мы говорим о внешней и внутренней обработке одновременно:
Выводить на консоль только после перехвата исключения
интерфейсный код
$.ajax({
type: "GET",
url: "/user/add",
dataType: "json",
success: function(data){
alert("添加成功");
}
});
внутренний код
try {
// do something
} catch (Exception e) {
e.printStackTrace();
}
Это самый метод обработки исключений, который я видел.Если это метод добавления товаров, фронтенд отправляет запрос на бэкенд через ajax, ожидая вернуть json-информацию для указания результата добавления.Но если в этом коде возникает исключение :
-
Затем сцена, которую видит пользователь, это нажатие кнопки добавления, но ничего не происходит (на самом деле возвращается страница ошибки 500, но здесь интерфейс не слушает событие ошибки, только событие успеха. Но даже если добавлено
error: function(data) {alert("添加失败");}
) и что? Пользователь не знает, почему это не удалось. -
За кулисами
e.printStackTrace()
Выведенный в консоль лог так же будет похоронен в длинном логе, и вполне вероятно, что исключение вывода не будет видно.Но это не самый худший случай, даже хужеe.printStackTrace()
ничего такого,catch
Блок пустой, так что в бэкенд-консоли ничего не видно, и этот код всегда будет лежать в системе в засаде, как невидимая бомба.
запутанный путь назад
интерфейсный код
$.ajax({
type: "GET",
url: "/goods/add",
dataType: "json",
success: function(data) {
if (data.flag) {
alert("添加成功");
} else {
alert(data.message);
}
},
error: function(data){
alert("添加失败");
}
});
внутренний код
@RequestMapping("/goods/add")
@ResponseBody
public Map add(Goods goods) {
Map map = new HashMap();
try {
// do something
map.put(flag, true);
} catch (Exception e) {
e.printStackTrace();
map.put("flag", false);
map.put("message", e.getMessage());
}
reutrn map;
}
После перехвата исключения таким образом возвращается сообщение об ошибке, а передний план выполнил некоторую обработку, что выглядит идеально?HashMap
серединаflag
иmessage
С такой строкой легко обращаться как с ключом, например, вы вызываете здесьmessage
, Другие люди называютmsg
,даже иногда руки дрожат и ошибаются,что делать?Сменить ресепшн наmsg
Или другие персонажи? Передняя часть и задняя часть были изменены туда и обратно вот так?
Более того, в случае кейса А возвращать json, в случае кейса Б перенаправлять на определенную страницу, что еще более запутанно, очень хлопотно иметь дело с такой неоднородной структурой.
Спецификация обработки исключений
Так как это собираетсяОбъединитьОбработка исключений, то должна быть спецификация, которую нельзя перепутать.Эта спецификация включает в себя front-end и back-end.
не ловить никаких исключений
да, не вв бизнес-кодеОтлавливать исключения, то есть все исключения в слоях dao, service и controller кидают на верхний слой.Это не вызовет кучу бизнес-кодовtry-catch
Это загромождает бизнес-код.
Единый набор возвращаемых результатов
Не используйте Map для возврата результатов, Map нелегко контролировать и легко ошибиться, вы должны определить класс сущности Java.Чтобы представить унифицированные возвращаемые результаты, например, определить класс сущности:
public class ResultBean<T> {
private int code;
private String message;
private Collection<T> data;
private ResultBean() {
}
public static ResultBean error(int code, String message) {
ResultBean resultBean = new ResultBean();
resultBean.setCode(code);
resultBean.setMessage(message);
return resultBean;
}
public static ResultBean success() {
ResultBean resultBean = new ResultBean();
resultBean.setCode(0);
resultBean.setMessage("success");
return resultBean;
}
public static <V> ResultBean<V> success(Collection<V> data) {
ResultBean resultBean = new ResultBean();
resultBean.setCode(0);
resultBean.setMessage("success");
resultBean.setData(data);
return resultBean;
}
// getter / setter 略
}
- Обычный случай: звонок
ResultBean.success()
илиResultBean.success(Collection<V> data)
, не нужно возвращать данные, то есть вызывать первое, нужно возвращать данные, вызывать второе.Например:
@RequestMapping("/goods/add")
@ResponseBody
public ResultBean<Goods> getAllGoods() {
List<Goods> goods = goodsService.findAll();
return ResultBean.success(goods);
}
@RequestMapping("/goods/update")
@ResponseBody
public ResultBean updateGoods(Goods goods) {
goodsService.update(goods);
return ResultBean.success();
}
Обычно нужно вызывать только метод запросаResultBean.success(Collection<V> data)
чтобы вернуть N фрагментов данных, должны быть вызваны другие методы, такие как удаление, изменение и т. д.ResultBean.success()
, то есть в бизнес-коде обрабатывается только правильная функция, а по исключению не выносится никакого суждения.Нет необходимости судить о количестве обновлений обновления или удаления (личное предложение, на самом деле нужно основываться на бизнес). Пока не выдается исключение, мы считаем, что операция пользователя выполнена успешно. И оперативная информация об успешной операции обрабатывается во внешнем интерфейсе, не возвращайте такие поля, как «операция выполнена успешно», в фоновом режиме.
Информация, полученная на стойке регистрации:
{
"code": 0,
"message": "success",
"data": [
{
"name": "商品1",
"price": 50.00,
},
{
"name": "商品2",
"price": 99.99,
}
]
}
- Генерация исключения: после генерации исключения мы должны вызвать
ResultBean.error(int code, String message)
, чтобы вернуть код состояния и сообщение об ошибке, мы согласилисьcode
0 означает, что операция прошла успешно,1
или2
Одинаковые положительные числа указывают на ошибки пользовательского ввода,-1
,-2
Такие отрицательные числа указывают на системные ошибки.
Информация, полученная на стойке регистрации:
{
"code": -1,
"message": "XXX 参数有问题, 请重新填写",
"data": null
}
Интерфейсная унифицированная обработка:
После того, как возвращаемый набор результатов нормализован, интерфейс обрабатывает его нормально:
/**
* 显示错误信息
* @param result: 错误信息
*/
function showError(s) {
alert(s);
}
/**
* 处理 ajax 请求结果
* @param result: ajax 返回的结果
* @param fn: 成功的处理函数 ( 传入data: fn(result.data) )
*/
function handlerResult(result, fn) {
// 成功执行操作,失败提示原因
if (result.code == 0) {
fn(result.data);
}
// 用户操作异常, 这里可以对 1 或 2 等错误码进行单独处理, 也可以 result.code > 0 来粗粒度的处理, 根据业务而定.
else if (result.code == 1) {
showError(result.message);
}
// 系统异常, 这里可以对 -1 或 -2 等错误码进行单独处理, 也可以 result.code > 0 来粗粒度的处理, 根据业务而定.
else if (result.code == -1) {
showError(result.message);
}
// 如果进行细粒度的状态码判断, 那么就应该重点注意这里没出现过的状态码. 这个判断仅建议在开发阶段保留用来发现未定义的状态码.
else {
showError("出现未定义的状态码:" + result.code);
}
}
/**
* 根据 id 删除商品
*/
function deleteGoods(id) {
$.ajax({
type: "DELETE",
url: "/goods/delete",
dataType: "json",
success: function(result){
handlerResult(result, deleteDone);
}
});
}
function deleteDone(data) {
alert("删除成功");
}
showError
иhandlerResult
являются общедоступными методами, используемыми для отображения ошибок и унифицированной обработки результирующего набора соответственно.
Затем сосредоточьтесь на методе отправки запроса и обработки правильного результата, например здесь функция deleteDone, которая используется для обработки подсказки для пользователя, когда операция выполнена успешно.Разумно, и сообщение об ошибке известно только фон, поэтому фон нужно вернуть.
Серверная часть обрабатывает исключения единообразно
Сказав так много, я не упомянул о том, что бэкенд не перехватывает никаких исключений на бизнес-уровне, поскольку все бизнес-уровни не перехватывают исключения, все исключения будут выброшены на уровень контроллера, метод может с этим справиться.
К счастью, Spring предоставляет нам аннотацию для унифицированной обработки исключений:
@ControllerAdvice
@ResponseBody
public class WebExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(WebExceptionHandler.class);
@ExceptionHandler
public ResultBean unknownAccount(UnknownAccountException e) {
log.error("账号不存在", e);
return ResultBean.error(1, "账号不存在");
}
@ExceptionHandler
public ResultBean incorrectCredentials(IncorrectCredentialsException e) {
log.error("密码错误", e);
return ResultBean.error(-2, "密码错误");
}
@ExceptionHandler
public ResultBean unknownException(Exception e) {
log.error("发生了未知异常", e);
// 发送邮件通知技术人员.
return ResultBean.error(-99, "系统出现错误, 请联系网站管理员!");
}
}
Исключения, которые необходимо обрабатывать, настраиваются здесь единообразно. Точно так же неизвестные исключения должны быть обнаружены вовремя и обработаны. Рекомендуется отправлять электронное письмо при возникновении неизвестного исключения, чтобы предупредить технического специалиста.
Суммировать
Кратко опишите метод унифицированной обработки исключений:
- Не используйте случайный возврат различных типов данных и унифицируйте спецификацию возвращаемого значения.
- Не ловите никаких исключений в бизнес-коде, оставьте все как есть
@ControllerAdvice
обрабатывать.
Простой демонстрационный проект:GitHub.com/Чжао, июнь 1998 г.…
Автор этой статьи:Чжао ЦзюньСсылка на эту статью: Woohoo, Чжао Цзюнь, IM/spring boot-… Уведомление об авторских правах:Все статьи в этом блоге, если не указано иное, используютBY-NC-SAсоглашение. Пожалуйста, укажите источник!