задний план
В процессе разработки программного обеспечения неизбежно приходится иметь дело с различными исключениями, насколько я понимаю, как минимум больше половины времени приходится на различные исключения, поэтому ошибок в коде будет очень много.try {...} catch {...} finally {...}
Блоки кода не только содержат много избыточного кода, но и влияют на читабельность кода. Сравните два изображения ниже, чтобы увидеть, какой стиль кода вы сейчас пишете? И какой стиль кодирования вы предпочитаете?
уродливая попытка поймать блок
Элегантный контроллер
Приведенный выше пример все еще толькоController
слой, если он находится вService
слоев, может быть большеtry catch
кодовый блок. Это серьезно повлияет на читабельность кода, «эстетичность».
Так что если бы это был я, то я бы определенно предпочел второй вариант, я могу больше сосредоточиться на разработке бизнес-кода, и в то же время код станет более лаконичным.
Поскольку бизнес-код явно не захватывает и не обрабатывает исключения, а исключения все равно должны обрабатываться, иначе система будет падать на каждом шагу, поэтому должны быть другие места для захвата и обработки этих исключений.
Итак, вопрос в том, как изящно обрабатывать различные исключения?
Что такое унифицированная обработка исключений
Spring
Добавлена аннотация в версии 3.2@ControllerAdvice
,С участием@ExceptionHandler
,@InitBinder
,@ModelAttribute
Другие аннотации аннотаций используются вместе.Что касается функций этих аннотаций, я не буду здесь вдаваться в подробности.Если вы не понимаете, вы можете обратиться к новой аннотации @ControllerAdvice в Spring 3.2, чтобы сначала получить общее представление.
Однако только аннотации связаны с обработкой исключений.@ExceptionHandler
, буквально, естьобработчик исключенийзначит, его действительное действие таково: если в определенномController
Класс определяет метод обработки исключений и добавляет аннотацию к методу, затем, когда возникает указанное исключение, будет выполнен метод обработки исключений, который может использовать привязку данных, предоставляемую springmvc, например, внедрение HttpServletRequest и т. д., и может также примите текущий брошенный объект Throwable.
Однако таким образом необходимоController
Классы определяют набор таких методов обработки исключений, потому что исключения могут быть разных типов. Это приведет к большому количеству избыточного кода, и если вам нужно добавить логику обработки исключений, вы должны изменить всеController
Класс, очень неэлегантно.
Конечно, вы можете сказать, а затем определить что-то вродеBaseController
Базовый класс, так что все в порядке.
Хотя это так, это все же не идеально, потому что такой код является навязчивым и связанным. простоController
, Почему я должен наследовать такой класс, если другие базовые классы уже унаследованы. каждый знаетJava
Только один класс может быть унаследован.
Есть ли решение, которое не требуетController
сцепление, также можно определитьобработчик исключенийприменим ко всем контроллерам? Так что обратите внимание@ControllerAdvice
Проще говоря, эта аннотация может применять обработчики исключений ко всем контроллерам, а не к одному контроллеру. С помощью этой аннотации мы можем добиться: в отдельном месте, например отдельном классе, определить набор механизмов обработки различных исключений, а затем добавить аннотации в сигнатуру класса@ControllerAdvice
, единая пара不同阶段的
,不同异常
для обработки. Это принцип единой обработки исключений.
Обратите внимание, что вышеуказанные исключения классифицируются по этапам, которые можно условно разделить на: введитеController
предыдущее исключение иService
Исключение слоя, пожалуйста, обратитесь к следующему рисунку для получения подробной информации:
Исключения на разных этапах
Цель
Уничтожить более 95%try catch
кодовые блоки с элегантнымиAssert
(Утверждение) для проверки бизнес-исключений, фокусируясь только на бизнес-логике, не тратя много энергии на запись избыточных данных.try catch
кодовый блок.
Единая практика обработки исключений
Прежде чем определить унифицированный класс обработки исключений, давайте сначала представим, как изящно определять исключения и генерировать исключения.
Замените исключение throw на Assert
предположительноAssert(断言)
Всем знакомы такие, какSpring
семьяorg.springframework.util.Assert
, который часто используется, когда мы пишем тестовые примеры.Использование утверждений может дать нам необычное шелковистое ощущение при кодировании, например:
@Test
public void test1() {
...
User user = userDao.selectById(userId);
Assert.notNull(user, "用户不存在.");
...
}
@Test
public void test2() {
// 另一种写法
User user = userDao.selectById(userId);
if (user == null) {
throw new IllegalArgumentException("用户不存在.");
}
}
Считаете ли вы, что первый способ записи непустого суждения очень элегантен, а второй способ записи относительно безобразен?if {...}
кодовый блок. такой волшебныйAssert.notNull()
Что происходит за кулисами? НижеAssert
Часть исходного кода:
public abstract class Assert {
public Assert() {
}
public static void notNull(@Nullable Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}}
можно увидеть,Assert
На самом деле, чтобы помочь намif {...}
Упаковка, разве это не удивительно? Несмотря на простоту, нельзя отрицать, что опыт кодирования, по крайней мере, на высоте. Так можем ли мы подражатьorg.springframework.util.Assert
, также напишите класс утверждения, но исключение, выдаваемое после сбоя утверждения, неIllegalArgumentException
Эти встроенные исключения, но наши собственные определенные исключения. Давайте попробуем.
Assertpublic interface Assert {
/**
* 创建异常
* @param args
* @return
*/
BaseException newException(Object... args);
/**
* 创建异常
* @param t
* @param args
* @return
*/
BaseException newException(Throwable t, Object... args);
/**
* <p>断言对象<code>obj</code>非空。如果对象<code>obj</code>为空,则抛出异常
*
* @param obj 待判断对象
*/
default void assertNotNull(Object obj) {
if (obj == null) {
throw newException(obj);
}
}
/**
* <p>断言对象<code>obj</code>非空。如果对象<code>obj</code>为空,则抛出异常
* <p>异常信息<code>message</code>支持传递参数方式,避免在判断之前进行字符串拼接操作
*
* @param obj 待判断对象
* @param args message占位符对应的参数列表
*/
default void assertNotNull(Object obj, Object... args) {
if (obj == null) {
throw newException(args);
}
}}
надAssert
Метод утверждения определяется с использованием метода интерфейса по умолчанию, а затем вы обнаружили, что когда утверждение терпит неудачу, выбрасываемое исключение не является конкретным исключением, а передается двумnewException
Предусмотрены методы интерфейса. Поскольку исключения, которые появляются в бизнес-логике, в основном соответствуют конкретным сценариям, таким как получение информации о пользователе на основе идентификатора пользователя, результат запросаnull
, исключение, сгенерированное в это время, может бытьUserNotFoundException
, и имеет определенный код исключения (например, 7001) и информацию об исключении «пользователь не существует». Итак, какое конкретное исключение выброшено, естьAssert
Решает класс реализации.
Увидев это, у вас могут возникнуть такие сомнения.Согласно приведенному выше утверждению, если есть много нештатных ситуаций, необходимо определить одинаковое количество классов утверждений и классов исключений.Это явно античеловечно, и это также невообразимо. Ну мудро. Не волнуйтесь, просто выслушайте меня подробно.
Эмпатическое перечисление
пользовательское исключениеBaseException
Есть 2 свойства, а именноcode
,message
, такая пара свойств, вы когда-нибудь думали о каком-нибудь классе, который вообще определяет эти два свойства? Да, класс перечисления. посмотрим как я будуEnum
а такжеAssert
В сочетании, я верю, я заставлю ваши глаза сиять. следующее:
public interface IResponseEnum {
int getCode();
String getMessage();}
/**
* <p>业务异常</p>
* <p>业务处理时,出现异常,可以抛出该异常</p>
*/
public class BusinessException extends BaseException {
private static final long serialVersionUID = 1L;
public BusinessException(IResponseEnum responseEnum, Object[] args, String message) {
super(responseEnum, args, message);
}
public BusinessException(IResponseEnum responseEnum, Object[] args, String message, Throwable cause) {
super(responseEnum, args, message, cause);
}}public interface BusinessExceptionAssert extends IResponseEnum, Assert {
@Override
default BaseException newException(Object... args) {
String msg = MessageFormat.format(this.getMessage(), args);
return new BusinessException(this, args, msg);
}
@Override
default BaseException newException(Throwable t, Object... args) {
String msg = MessageFormat.format(this.getMessage(), args);
return new BusinessException(this, args, msg, t);
}}@Getter
@AllArgsConstructor
public enum ResponseEnum implements BusinessExceptionAssert {
/**
* Bad licence type
*/
BAD_LICENCE_TYPE(7001, "Bad licence type."),
/**
* Licence not found
*/
LICENCE_NOT_FOUND(7002, "Licence not found.")
;
/**
* 返回码
*/
private int code;
/**
* 返回消息
*/
private String message;
}
Глядя на это, есть ли какое-то чувство блеска?В примере кода определены два экземпляра перечисления:BAD_LICENCE_TYPE
,LICENCE_NOT_FOUND
, соответствующийBadLicenceTypeException
,LicenceNotFoundException
Два исключения. Каждый раз, когда в будущем будет добавлено исключение, вам нужно будет добавить только экземпляр перечисления, и нет необходимости определять класс исключения для каждого исключения. Затем посмотрите, как его использовать, предполагая, чтоLicenceService
с проверкойLicence
Есть ли метод, как показано ниже:
/**
* 校验{@link Licence}存在
* @param licence
*/
private void checkNotNull(Licence licence) {
ResponseEnum.LICENCE_NOT_FOUND.assertNotNull(licence);
}
Без утверждений код может выглядеть так:
private void checkNotNull(Licence licence) {
if (licence == null) {
throw new LicenceNotFoundException();
// 或者这样
throw new BusinessException(7001, "Bad licence type.");
}
}
Объединение (наследование) с использованием классов enumAssert
, просто определите разные экземпляры перечисления в соответствии с конкретными исключениями, как указано выше.BAD_LICENCE_TYPE
,LICENCE_NOT_FOUND
, вы можете генерировать определенные исключения для различных ситуаций (здесь имеется в виду наличие определенных кодов исключений и сообщений об исключениях), так что вам не нужно определять большое количество классов исключений, но также иметь хорошую читаемость утверждений.Конечно, преимущества этой схемы Это гораздо больше, пожалуйста, продолжайте читать следующий текст и медленно прочувствуйте его.
Примечание. Приведенные выше примеры относятся к конкретным службам, а некоторые ненормальные ситуации являются общими, например: занят сервер, сбой в сети, сбой в работе сервера, сбой при проверке параметров, ошибка 404 и т. д.
CommonResponseEnum
,ArgumentResponseEnum
,ServletResponseEnum
,вServletResponseEnum
Это будет подробно объяснено позже.
Определить унифицированный класс обработчика исключений
@Slf4j
@Component
@ControllerAdvice
@ConditionalOnWebApplication
@ConditionalOnMissingBean(UnifiedExceptionHandler.class)
public class UnifiedExceptionHandler {
/**
* 生产环境
*/
private final static String ENV_PROD = "prod";
@Autowired
private UnifiedMessageSource unifiedMessageSource;
/**
* 当前环境
*/
@Value("${spring.profiles.active}")
private String profile;
/**
* 获取国际化消息
*
* @param e 异常
* @return
*/
public String getMessage(BaseException e) {
String code = "response." + e.getResponseEnum().toString();
String message = unifiedMessageSource.getMessage(code, e.getArgs());
if (message == null || message.isEmpty()) {
return e.getMessage();
}
return message;
}
/**
* 业务异常
*
* @param e 异常
* @return 异常结果
*/
@ExceptionHandler(value = BusinessException.class)
@ResponseBody
public ErrorResponse handleBusinessException(BaseException e) {
log.error(e.getMessage(), e);
return new ErrorResponse(e.getResponseEnum().getCode(), getMessage(e));
}
/**
* 自定义异常
*
* @param e 异常
* @return 异常结果
*/
@ExceptionHandler(value = BaseException.class)
@ResponseBody
public ErrorResponse handleBaseException(BaseException e) {
log.error(e.getMessage(), e);
return new ErrorResponse(e.getResponseEnum().getCode(), getMessage(e));
}
/**
* Controller上一层相关异常
*
* @param e 异常
* @return 异常结果
*/
@ExceptionHandler({
NoHandlerFoundException.class,
HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
// BindException.class,
// MethodArgumentNotValidException.class
HttpMediaTypeNotAcceptableException.class,
ServletRequestBindingException.class,
ConversionNotSupportedException.class,
MissingServletRequestPartException.class,
AsyncRequestTimeoutException.class
})
@ResponseBody
public ErrorResponse handleServletException(Exception e) {
log.error(e.getMessage(), e);
int code = CommonResponseEnum.SERVER_ERROR.getCode();
try {
ServletResponseEnum servletExceptionEnum = ServletResponseEnum.valueOf(e.getClass().getSimpleName());
code = servletExceptionEnum.getCode();
} catch (IllegalArgumentException e1) {
log.error("class [{}] not defined in enum {}", e.getClass().getName(), ServletResponseEnum.class.getName());
}
if (ENV_PROD.equals(profile)) {
// 当为生产环境, 不适合把具体的异常信息展示给用户, 比如404.
code = CommonResponseEnum.SERVER_ERROR.getCode();
BaseException baseException = new BaseException(CommonResponseEnum.SERVER_ERROR);
String message = getMessage(baseException);
return new ErrorResponse(code, message);
}
return new ErrorResponse(code, e.getMessage());
}
/**
* 参数绑定异常
*
* @param e 异常
* @return 异常结果
*/
@ExceptionHandler(value = BindException.class)
@ResponseBody
public ErrorResponse handleBindException(BindException e) {
log.error("参数绑定校验异常", e);
return wrapperBindingResult(e.getBindingResult());
}
/**
* 参数校验异常,将校验失败的所有异常组合成一条错误信息
*
* @param e 异常
* @return 异常结果
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseBody
public ErrorResponse handleValidException(MethodArgumentNotValidException e) {
log.error("参数绑定校验异常", e);
return wrapperBindingResult(e.getBindingResult());
}
/**
* 包装绑定异常结果
*
* @param bindingResult 绑定结果
* @return 异常结果
*/
private ErrorResponse wrapperBindingResult(BindingResult bindingResult) {
StringBuilder msg = new StringBuilder();
for (ObjectError error : bindingResult.getAllErrors()) {
msg.append(", ");
if (error instanceof FieldError) {
msg.append(((FieldError) error).getField()).append(": ");
}
msg.append(error.getDefaultMessage() == null ? "" : error.getDefaultMessage());
}
return new ErrorResponse(ArgumentResponseEnum.VALID_ERROR.getCode(), msg.substring(2));
}
/**
* 未定义异常
*
* @param e 异常
* @return 异常结果
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ErrorResponse handleException(Exception e) {
log.error(e.getMessage(), e);
if (ENV_PROD.equals(profile)) {
// 当为生产环境, 不适合把具体的异常信息展示给用户, 比如数据库异常信息.
int code = CommonResponseEnum.SERVER_ERROR.getCode();
BaseException baseException = new BaseException(CommonResponseEnum.SERVER_ERROR);
String message = getMessage(baseException);
return new ErrorResponse(code, message);
}
return new ErrorResponse(CommonResponseEnum.SERVER_ERROR.getCode(), e.getMessage());
}}
Видно, что исключения разделены на несколько категорий выше, но на самом деле категорий всего две, однаServletException
,ServiceException
, помните упомянутое вышепоэтапно, что соответствует вводуController
предыдущее исключение иService
исключение слоя; затемServiceException
Далее он делится на пользовательские исключения и неизвестные исключения. Соответствующее соотношение выглядит следующим образом:
-
Исключение перед входом в «Контроллер»: handleServletException, handleBindException, handleValidException
-
Пользовательское исключение: handleBusinessException, handleBaseException
-
Неизвестное исключение: handleException
В следующих разделах подробно описаны эти обработчики исключений.
Описание обработчика исключений
handleServletException
Одинhttp
запрос, по прибытииController
Перед запросом будет выполнена серия проверок между информацией о запросе и информацией о целевом контроллере. Вот краткое описание:
NoHandlerFoundException: первый за запросUrl
Узнать, есть ли соответствующий контроллер, если нет, то будет выброшено исключение, которое всем очень знакомо404
аномальный;
HttpRequestMethodNotSupportedException: при совпадении (результатом совпадения является список, разницаhttp
различными методами, такими как: Get, Post и т. д.), попробуйтеhttp
Метод сопоставляется с контроллером списка, если нет соответствующегоhttp
Контроллер метода выдает исключение;
HttpMediaTypeNotSupportedException: затем сравните заголовок запроса с тем, что поддерживает контроллер, напримерcontent-type
заголовок запроса, если подпись параметра контроллера содержит аннотации@RequestBody
, но запрошенныйcontent-type
Значение заголовка запроса не содержитapplication/json
, то будет выброшено исключение (разумеется, это исключение будет выброшено не только в этом случае);
MissingPathVariableException: параметр пути не обнаружен. Например, URL-адрес:/licence/{licenceId}
, подпись параметра содержит@PathVariable("licenceId")
, когда запрошенный URL/licence
, без явного определения URL как/licence
В случае , это будет оцениваться как: параметр пути отсутствует;
MissingServletRequestParameterException: Отсутствует параметр запроса. Например, если параметр @RequestParam("licenceId") String licenceId определен, но этот параметр не передается при инициации запроса, будет выдано это исключение;
TypeMismatchException: ошибка сопоставления типа параметра. Например: если получен параметр Long, но входящее значение действительно является строкой, то преобразование типа завершится ошибкой, и будет выброшено исключение;
HttpMessageNotReadableException: с указанным вышеHttpMediaTypeNotSupportedException
Приведенный пример полностью противоположен, то есть заголовок запроса несет"content-type: application/json;charset=UTF-8"
, но полученный параметр не аннотируется@RequestBody
, или процесс десериализации строки json, переносимой телом запроса, в pojo завершится неудачно, это исключение также будет выдано;
HttpMessageNotWritableException: возвращаемый pojo не удалось сериализовать в json, затем выдать это исключение;
handleBindException
Проверка параметра является ненормальной, что будет подробно объяснено позже.
handleValidException
Проверка параметра является ненормальной, что будет подробно объяснено позже.
обработчикBusinessException, обработчикBaseException
Обрабатывайте пользовательские бизнес-исключения, простоhandleBaseException
рассматривается в дополнение кBusinessException
Неожиданные все бизнес-исключения. На данный момент эти два могут быть объединены в один.
handleException
Обрабатывать все неизвестные исключения, например исключения, которые не позволяют работать с базой данных.
Примечание: выше
handleServletException
,handleException
Информация об исключении, возвращаемая этими двумя процессорами, может различаться в разных средах.Считается, что эта информация об исключении является информацией об исключении, которая поставляется с платформой и обычно представлена на английском языке.SERVER_ERROR
Представляет информацию об исключении.
Необычное 404
Как упоминалось выше, когда запрос не соответствует контроллеру, он выдаетNoHandlerFoundException
Исключение, но по умолчанию это не так, по умолчанию появится страница, похожая на следующую:
Whitelabel Error Page
Как появилась эта страница? На самом деле, когда появляется 404, по умолчанию не выбрасывается исключение, аforward
перенаправить на/error
контроллер,spring
также предоставляет значение по умолчаниюerror
контроллер следующим образом:
Итак, как сделать так, чтобы 404 тоже выбрасывал исключение, просто вproperties
Добавьте в файл следующую конфигурацию:
spring.mvc.throw-exception-if-no-handler-found=truespring.resources.add-mappings=false
Таким образом, вы можете зафиксировать его в обработчике исключений, а затем внешний интерфейс немедленно перейдет на страницу 404, если он захватит определенный код состояния.
Поймать исключение, соответствующее 404
Равномерно вернуть результат
Прежде чем проверять юниформ-обработчик исключений, кстати, юниформа возвращает результат. Грубо говоря, это фактически унификация структуры данных возвращаемого результата.code
,message
Это обязательное поле во всех возвращаемых результатах, и когда данные должны быть возвращены, требуется еще одно поле.data
Представлять.
Итак, сначала определитеBaseResponse
как базовый класс для всех возвращаемых результатов;
Затем определите общий класс возвращаемого результатаCommonResponse
,наследоватьBaseResponse
и другие поляdata
;
Чтобы различать успешные и неудачные результаты возврата, определите другойErrorResponse
Наконец, существует общий возвращаемый результат, т. е. возвращаемые данные содержат информацию о подкачке.Поскольку этот интерфейс относительно распространен, необходимо отдельно определить класс возвращаемого результата.QueryDataResponse
, который наследуется отCommonResponse
, просто поставьdata
Тип поля ограниченQueryDdata
,QueryDdata
Соответствующие поля пейджинговой информации определены вtotalCount
,pageNo
,pageSize
,records
.
Среди наиболее часто используемых толькоCommonResponse
а такжеQueryDataResponse
, но имя слишком длинное, почему бы вместо этого не определить 2 класса с очень простыми именами? тогдаR
а такжеQR
Он родился, и когда вы возвращаете результат в будущем, вам нужно только написать его так:new R<>(data)
,new QR<>(queryData)
.
Определения всех классов возвращаемых результатов не будут здесь публиковаться.
Проверить унифицированную обработку исключений
Поскольку этот набор унифицированной обработки исключений можно назвать универсальным, все они могут быть спроектированы как одно целое.common
package, и каждый новый проект/модуль в будущем должен импортировать только этот package. Итак, чтобы проверить, вам нужно создать новый проект и ввестиcommon
Мешок.
основной код
Вот основной исходный код для проверки:
@Service
public class LicenceService extends ServiceImpl<LicenceMapper, Licence> {
@Autowired
private OrganizationClient organizationClient;
/**
* 查询{@link Licence} 详情
* @param licenceId
* @return
*/
public LicenceDTO queryDetail(Long licenceId) {
Licence licence = this.getById(licenceId);
checkNotNull(licence);
OrganizationDTO org = ClientUtil.execute(() -> organizationClient.getOrganization(licence.getOrganizationId()));
return toLicenceDTO(licence, org); }
/**
* 分页获取
* @param licenceParam 分页查询参数
* @return
*/
public QueryData<SimpleLicenceDTO> getLicences(LicenceParam licenceParam) {
String licenceType = licenceParam.getLicenceType();
LicenceTypeEnum licenceTypeEnum = LicenceTypeEnum.parseOfNullable(licenceType);
// 断言, 非空
ResponseEnum.BAD_LICENCE_TYPE.assertNotNull(licenceTypeEnum);
LambdaQueryWrapper<Licence> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Licence::getLicenceType, licenceType);
IPage<Licence> page = this.page(new QueryPage<>(licenceParam), wrapper);
return new QueryData<>(page, this::toSimpleLicenceDTO);
}
/**
* 新增{@link Licence}
* @param request 请求体
* @return
*/
@Transactional(rollbackFor = Throwable.class)
public LicenceAddRespData addLicence(LicenceAddRequest request) {
Licence licence = new Licence();
licence.setOrganizationId(request.getOrganizationId());
licence.setLicenceType(request.getLicenceType());
licence.setProductName(request.getProductName());
licence.setLicenceMax(request.getLicenceMax());
licence.setLicenceAllocated(request.getLicenceAllocated());
licence.setComment(request.getComment());
this.save(licence);
return new LicenceAddRespData(licence.getLicenceId());
}
/**
* entity -> simple dto
* @param licence {@link Licence} entity
* @return {@link SimpleLicenceDTO}
*/
private SimpleLicenceDTO toSimpleLicenceDTO(Licence licence) {
// 省略
}
/**
* entity -> dto
* @param licence {@link Licence} entity
* @param org {@link OrganizationDTO}
* @return {@link LicenceDTO}
*/
private LicenceDTO toLicenceDTO(Licence licence, OrganizationDTO org) {
// 省略
}
/**
* 校验{@link Licence}存在
* @param licence
*/
private void checkNotNull(Licence licence) {
ResponseEnum.LICENCE_NOT_FOUND.assertNotNull(licence);
}
}
PS: Здесь используется структура DAO.mybatis-plus
. При запуске автоматически вставляются данные:
-- licenceINSERT INTO licence (licence_id, organization_id, licence_type, product_name, licence_max, licence_allocated)
VALUES (1, 1, 'user','CustomerPro', 100,5);
INSERT INTO licence (licence_id, organization_id, licence_type, product_name, licence_max, licence_allocated)
VALUES (2, 1, 'user','suitability-plus', 200,189);
INSERT INTO licence (licence_id, organization_id, licence_type, product_name, licence_max, licence_allocated)
VALUES (3, 2, 'user','HR-PowerSuite', 100,4);
INSERT INTO licence (licence_id, organization_id, licence_type, product_name, licence_max, licence_allocated)
VALUES (4, 2, 'core-prod','WildCat Application Gateway', 16,16);
-- organizationsINSERT INTO organization (id, name, contact_name, contact_email, contact_phone)
VALUES (1, 'customer-crm-co', 'Mark Balster', 'mark.balster@custcrmco.com', '823-555-1212');
INSERT INTO organization (id, name, contact_name, contact_email, contact_phone)
VALUES (2, 'HR-PowerSuite', 'Doug Drewry','doug.drewry@hr.com', '920-555-1212');
начать проверку
Перехватить пользовательское исключение
\1. Получить несуществующийlicence
Подробности:http://локальный:10000/лицензия/5. Успешно ответил на запросы: licenseId=1
проверить не пусто
Исключение Catch License not found
Licence not found
2. По несуществующемуlicence type
Получатьlicence
Список:http://localhost:10000/licence/list?licenceType=ddd. по желанию licence type
Для: пользователя, core-prod.
проверить не пусто
Поймать исключение «Плохой тип лицензии»
Bad licence type
Перехватите исключение перед входом в контроллер
\1. Доступ к несуществующему интерфейсу:http://localhost:10000/licence/list/ddd
Перехватить исключение 404
\ 2. Метод http не поддерживает:http://localhost:10000/licence
PostMapping
Метод запроса перехвата не поддерживается, исключение
Request method not supported
\3.Проверить исключение 1:http://localhost:10000/licence/list?licenceType=
getLicences
LicenceParam
Исключения проверки привязки параметров перехвата
licence type cannot be empty
4. Исключение проверки 2: почтовый запрос с использованием имитации почтальона здесь.
addLicence
LicenceAddRequest
URL-адрес запроса является результатом
Исключения проверки привязки параметров перехвата
Примечание. Поскольку метод получения информации об исключении для исключения проверки привязки параметра отличается от метода получения других исключений, исключения в этих двух случаяхИсключение перед входом в контроллерВынесенная отдельно, следующая логика сбора информации об исключении:
Сбор аномальной информации
Поймать неизвестное исключение
Допустим, мы прямо сейчасLicence
добавить полеtest
, но без изменения структуры таблицы базы данных, затем получите доступ:http://локальный:10000/лицензия/1.
Добавить тестовое поле
Перехватывать исключения базы данных
Error querying database
резюме
Как видите, тестируемые исключения можно перехватывать, а затемcode
,message
вернулся в виде . Для каждого проекта/модуля при определении бизнес-исключений необходимо только определить класс перечисления, а затем реализовать интерфейс.BusinessExceptionAssert
и, наконец, определите соответствующий экземпляр перечисления для каждого бизнес-исключения вместо определения множества классов исключений. Это также очень удобно в использовании, и его использование похоже на утверждение.
расширять
В производственной среде при захватеНеизвестное исключениеилиServletException
, потому что это длинная серия ненормальной информации, если она отображается непосредственно пользователю, она недостаточно профессиональна. Поэтому мы можем сделать следующее: когда будет обнаружено, что текущая среда является производственной средой, а затем напрямую вернуться " Сетевое исключение».
Рабочая среда возвращает «Сетевое исключение»
Текущую среду можно изменить следующими способами:
Изменить текущую среду на рабочую среду
Суммировать
использоватьутверждениеа такжеперечисляемый классВ сочетании с унифицированной обработкой исключений можно перехватить большинство исключений. Зачем говорить большинство исключений, потому что когда введениеspring cloud security
После этого будут исключения аутентификации/авторизации, исключения деградации службы шлюза, исключения вызовов между модулями, исключения удаленных сторонних служб и т. д. Методы захвата этих исключений не такие, как описанные в этой статье, но должны к ограничениям по объему, они не будут подробно описываться здесь, учтите, что в будущем будет отдельная статья-предисловие.
Кроме того, когда необходимо учитывать интернационализацию, информация об исключении после перехвата исключения, как правило, не может быть возвращена напрямую и должна быть преобразована в соответствующий язык.Однако в этой статье это учтено, и сопоставление интернационализации было выполнено. когда сообщение получено Логика следующая:
Получайте международные новости
В конце концов, глобальная аномалия относится к теме, о которой говорят пожилые люди.Я надеюсь, что на этот раз проект мобильного телефона даст вам некоторые поучительные знания. Каждый может изменить его в соответствии с реальной ситуацией.
Вы также можете использовать следующий объект jsonResult для обработки, а также опубликовать код.
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 没有登录
* @param request
* @param response
* @param e
* @return
*/
@ExceptionHandler(NoLoginException.class)
public Object noLoginExceptionHandler(HttpServletRequest request,HttpServletResponse response,Exception e)
{
log.error("[GlobalExceptionHandler][noLoginExceptionHandler] exception",e);
JsonResult jsonResult = new JsonResult();
jsonResult.setCode(JsonResultCode.NO_LOGIN);
jsonResult.setMessage("用户登录失效或者登录超时,请先登录");
return jsonResult;
}
/**
* 业务异常
* @param request
* @param response
* @param e
* @return
*/
@ExceptionHandler(ServiceException.class)
public Object businessExceptionHandler(HttpServletRequest request,HttpServletResponse response,Exception e)
{
log.error("[GlobalExceptionHandler][businessExceptionHandler] exception",e);
JsonResult jsonResult = new JsonResult();
jsonResult.setCode(JsonResultCode.FAILURE);
jsonResult.setMessage("业务异常,请联系管理员");
return jsonResult;
}
/**
* 全局异常处理
* @param request
* @param response
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
public Object exceptionHandler(HttpServletRequest request,HttpServletResponse response,Exception e)
{
log.error("[GlobalExceptionHandler][exceptionHandler] exception",e);
JsonResult jsonResult = new JsonResult();
jsonResult.setCode(JsonResultCode.FAILURE);
jsonResult.setMessage("系统错误,请联系管理员");
return jsonResult;
}
}
Источник этой статьи: cnblogs.com/jurendage/p/11255197.html.
Для получения дополнительной информации, пожалуйста, обратите внимание на публичный номер: Java Advanced Journey!