Адрес фактического центра электронной коммерции SpringBoot (30k+star):GitHub.com/macro-positive/…
Резюме
Обычно при разработке интерфейса часто требуется верификация параметров, вот два способа обработки логики верификации. Один из них — использовать Hibernate Validator для обработки, другой — использовать глобальные исключения для обработки, давайте поговорим об использовании этих двух способов.
Hibernate Validator
Hibernate Validator — это встроенная среда проверки SpringBoot. Пока SpringBoot интегрирован, он интегрируется автоматически. Мы можем завершить проверку параметров, используя аннотации, которые он предоставляет для объекта.
Общие примечания
Давайте сначала разберемся с часто используемыми аннотациями и получим представление о функции проверки, предоставляемой Hibernate Validator.
- @Null: аннотированное свойство должно быть нулевым;
- @NotNull: аннотированное свойство не может иметь значение null;
- @AssertTrue: аннотированное свойство должно быть истинным;
- @AssertFalse: аннотированное свойство должно быть ложным;
- @Min: аннотированное свойство должно быть больше или равно его значению;
- @Max: аннотированное свойство должно быть меньше или равно его значению;
- @Size: аннотированное свойство должно быть между минимальным и максимальным значениями;
- @Pattern: аннотированный атрибут должен соответствовать регулярному выражению, определенному его regexp;
- @NotBlank: аннотированная строка не может быть пустой строкой;
- @NotEmpty: аннотированное свойство не может быть пустым;
- @Email: аннотированное свойство должно соответствовать формату электронной почты.
Как пользоваться
Далее, давайте возьмем проверку параметров интерфейса бренда в качестве примера, чтобы объяснить использование Hibernate Validator, что требует некоторых знаний АОП.Друзья, которые не знают, могут обратиться к следующему«Использование AOP для записи журналов доступа к интерфейсу в приложениях SpringBoot».
- Для начала нам нужно добавить параметры интерфейса бренда
PmsBrandParam
Добавьте аннотации проверки в атрибут, чтобы определить правила проверки атрибута и информацию, которая будет возвращена после сбоя проверки;
/**
* 品牌传递参数
* Created by macro on 2018/4/26.
*/
public class PmsBrandParam {
@ApiModelProperty(value = "品牌名称",required = true)
@NotEmpty(message = "名称不能为空")
private String name;
@ApiModelProperty(value = "品牌首字母")
private String firstLetter;
@ApiModelProperty(value = "排序字段")
@Min(value = 0, message = "排序最小为0")
private Integer sort;
@ApiModelProperty(value = "是否为厂家制造商")
@FlagValidator(value = {"0","1"}, message = "厂家状态不正确")
private Integer factoryStatus;
@ApiModelProperty(value = "是否进行显示")
@FlagValidator(value = {"0","1"}, message = "显示状态不正确")
private Integer showStatus;
@ApiModelProperty(value = "品牌logo",required = true)
@NotEmpty(message = "品牌logo不能为空")
private String logo;
@ApiModelProperty(value = "品牌大图")
private String bigPic;
@ApiModelProperty(value = "品牌故事")
private String brandStory;
//省略若干Getter和Setter方法...
}
- Затем добавьте в интерфейс аннотацию @Validated для добавления бренда и введите параметр BindingResult;
/**
* 品牌功能Controller
* Created by macro on 2018/4/26.
*/
@Controller
@Api(tags = "PmsBrandController", description = "商品品牌管理")
@RequestMapping("/brand")
public class PmsBrandController {
@Autowired
private PmsBrandService brandService;
@ApiOperation(value = "添加品牌")
@RequestMapping(value = "/create", method = RequestMethod.POST)
@ResponseBody
public CommonResult create(@Validated @RequestBody PmsBrandParam pmsBrand, BindingResult result) {
CommonResult commonResult;
int count = brandService.createBrand(pmsBrand);
if (count == 1) {
commonResult = CommonResult.success(count);
} else {
commonResult = CommonResult.failed();
}
return commonResult;
}
}
- Затем создайте аспект на всем уровне контроллера, получите введенный объект BindingResult в его объемном уведомлении и используйте метод hasErrors, чтобы определить, прошла ли проверка.
/**
* HibernateValidator错误结果处理切面
* Created by macro on 2018/4/26.
*/
@Aspect
@Component
@Order(2)
public class BindingResultAspect {
@Pointcut("execution(public * com.macro.mall.controller.*.*(..))")
public void BindingResult() {
}
@Around("BindingResult()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
if (arg instanceof BindingResult) {
BindingResult result = (BindingResult) arg;
if (result.hasErrors()) {
FieldError fieldError = result.getFieldError();
if(fieldError!=null){
return CommonResult.validateFailed(fieldError.getDefaultMessage());
}else{
return CommonResult.validateFailed();
}
}
}
}
return joinPoint.proceed();
}
}
- На этом этапе мы получаем доступ к интерфейсу для добавления брендов, не проходя
name
поле, оно вернется名称不能为空
сообщение об ошибке;
пользовательская аннотация
Иногда аннотации проверки, предоставляемые фреймворком, не соответствуют нашим потребностям, в этом случае нам необходимо настроить аннотации проверки. Например, вышеупомянутая марка все еще добавляется, и в это время есть параметр.
showStatus
, мы надеемся, что это может быть только 0 или 1, а не другие числа.В этом случае вы можете использовать пользовательские аннотации для реализации этой функции.
- Сначала настройте класс аннотации проверки FlagValidator, затем добавьте аннотацию @Constraint и используйте ее атрибут validatedBy, чтобы указать конкретный класс реализации логики проверки;
/**
* 用户验证状态是否在指定范围内的注解
* Created by macro on 2018/4/26.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Constraint(validatedBy = FlagValidatorClass.class)
public @interface FlagValidator {
String[] value() default {};
String message() default "flag is not found";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
- Затем создайте FlagValidatorClass как конкретный класс реализации логики проверки, реализуйте интерфейс ConstraintValidator, здесь вам нужно указать два общих параметра, первый должен быть указан как ваш собственный класс аннотаций проверки, а второй указан как атрибут, который вы хотите для проверки Тип метода isValid представляет собой конкретную логику проверки.
/**
* 状态标记校验器
* Created by macro on 2018/4/26.
*/
public class FlagValidatorClass implements ConstraintValidator<FlagValidator,Integer> {
private String[] values;
@Override
public void initialize(FlagValidator flagValidator) {
this.values = flagValidator.value();
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
boolean isValid = false;
if(value==null){
//当状态为空时使用默认值
return true;
}
for(int i=0;i<values.length;i++){
if(values[i].equals(String.valueOf(value))){
isValid = true;
break;
}
}
return isValid;
}
}
- Далее мы можем использовать эту аннотацию в объекте параметра;
/**
* 品牌传递参数
* Created by macro on 2018/4/26.
*/
public class PmsBrandParam {
@ApiModelProperty(value = "是否进行显示")
@FlagValidator(value = {"0","1"}, message = "显示状态不正确")
private Integer showStatus;
//省略若干Getter和Setter方法...
}
- Наконец, мы тестируем аннотацию, вызывающий интерфейс — это входящий
showStatus=3
, вернусь显示状态不正确
сообщение об ошибке.
Преимущества и недостатки
Преимущество этого метода заключается в том, что аннотации можно использовать для реализации проверки параметров, что не требует какой-либо повторяющейся логики проверки, но есть и некоторые недостатки, такие как необходимость внедрения дополнительного объекта BindingResult в метод Controller и поддержка только некоторые простые проверки.Проверка, которая включает запрос к базе данных, не может быть удовлетворена.
глобальная обработка исключений
Идея использования глобальной обработки исключений для обработки логики проверки очень проста.Во-первых, нам нужно определить глобальный класс обработки исключений через аннотацию @ControllerAdvice, а затем настроить исключение проверки.Когда мы не можем выполнить проверку в контроллере, бросить его напрямую. Это исключение может достичь цели возврата сообщения об ошибке, если проверка не удалась.
Используемые аннотации
@ControllerAdvice: Подобно аннотации @Component, вы можете указать компонент, который в основном используется для улучшения функций класса, измененного аннотацией @Controller, таких как глобальная обработка исключений.
@ExceptionHandler: метод, используемый для изменения глобальной обработки исключений, вы можете указать тип исключения.
Как пользоваться
- Сначала нам нужно настроить класс исключения
ApiException
, который выбрасывается, когда мы не проходим проверку:
/**
* 自定义API异常
* Created by macro on 2020/2/27.
*/
public class ApiException extends RuntimeException {
private IErrorCode errorCode;
public ApiException(IErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
public ApiException(String message) {
super(message);
}
public ApiException(Throwable cause) {
super(cause);
}
public ApiException(String message, Throwable cause) {
super(message, cause);
}
public IErrorCode getErrorCode() {
return errorCode;
}
}
- Затем создайте класс обработчика утверждений
Asserts
, для метания различныхApiException
;
/**
* 断言处理类,用于抛出各种API异常
* Created by macro on 2020/2/27.
*/
public class Asserts {
public static void fail(String message) {
throw new ApiException(message);
}
public static void fail(IErrorCode errorCode) {
throw new ApiException(errorCode);
}
}
- Затем создайте наш глобальный класс обработчика исключений.
GlobalExceptionHandler
, используемый для обработки глобальных исключений и возврата инкапсулированного объекта CommonResult;
/**
* 全局异常处理
* Created by macro on 2020/2/27.
*/
@ControllerAdvice
public class GlobalExceptionHandler {
@ResponseBody
@ExceptionHandler(value = ApiException.class)
public CommonResult handle(ApiException e) {
if (e.getErrorCode() != null) {
return CommonResult.failed(e.getErrorCode());
}
return CommonResult.failed(e.getMessage());
}
}
- В качестве примера возьмем код для получения пользователем купона.Давайте сравним код до и после улучшения, и сначала посмотрим на код слоя Controller. После улучшения, пока метод в Сервисе выполняется успешно, это означает, что сбор купонов прошел успешно, потому что, если сбор не удался, будет напрямую выброшено исключение ApiException и будет возвращено сообщение об ошибке;
/**
* 用户优惠券管理Controller
* Created by macro on 2018/8/29.
*/
@Controller
@Api(tags = "UmsMemberCouponController", description = "用户优惠券管理")
@RequestMapping("/member/coupon")
public class UmsMemberCouponController {
@Autowired
private UmsMemberCouponService memberCouponService;
//改进前
@ApiOperation("领取指定优惠券")
@RequestMapping(value = "/add/{couponId}", method = RequestMethod.POST)
@ResponseBody
public CommonResult add(@PathVariable Long couponId) {
return memberCouponService.add(couponId);
}
//改进后
@ApiOperation("领取指定优惠券")
@RequestMapping(value = "/add/{couponId}", method = RequestMethod.POST)
@ResponseBody
public CommonResult add(@PathVariable Long couponId) {
memberCouponService.add(couponId);
return CommonResult.success(null,"领取成功");
}
}
- Посмотрите еще раз на код в интерфейсе Сервиса, разница заключается в возвращаемом результате, который после улучшения возвращает void. На самом деле функция CommonResult состоит в том, чтобы инкапсулировать данные, полученные в Сервисе, в единый возвращаемый результат Практика до улучшения нарушает этот принцип, и улучшенный метод решает эту проблему;
/**
* 用户优惠券管理Service
* Created by macro on 2018/8/29.
*/
public interface UmsMemberCouponService {
/**
* 会员添加优惠券(改进前)
*/
@Transactional
CommonResult add(Long couponId);
/**
* 会员添加优惠券(改进后)
*/
@Transactional
void add(Long couponId);
}
- Глядя на код в классе реализации службы, вы можете видеть, что логика возврата CommonResult в исходной логике проверки была изменена, чтобы вызвать метод отказа Asserts для достижения;
/**
* 会员优惠券管理Service实现类
* Created by macro on 2018/8/29.
*/
@Service
public class UmsMemberCouponServiceImpl implements UmsMemberCouponService {
@Autowired
private UmsMemberService memberService;
@Autowired
private SmsCouponMapper couponMapper;
@Autowired
private SmsCouponHistoryMapper couponHistoryMapper;
@Autowired
private SmsCouponHistoryDao couponHistoryDao;
//改进前
@Override
public CommonResult add(Long couponId) {
UmsMember currentMember = memberService.getCurrentMember();
//获取优惠券信息,判断数量
SmsCoupon coupon = couponMapper.selectByPrimaryKey(couponId);
if(coupon==null){
return CommonResult.failed("优惠券不存在");
}
if(coupon.getCount()<=0){
return CommonResult.failed("优惠券已经领完了");
}
Date now = new Date();
if(now.before(coupon.getEnableTime())){
return CommonResult.failed("优惠券还没到领取时间");
}
//判断用户领取的优惠券数量是否超过限制
SmsCouponHistoryExample couponHistoryExample = new SmsCouponHistoryExample();
couponHistoryExample.createCriteria().andCouponIdEqualTo(couponId).andMemberIdEqualTo(currentMember.getId());
long count = couponHistoryMapper.countByExample(couponHistoryExample);
if(count>=coupon.getPerLimit()){
return CommonResult.failed("您已经领取过该优惠券");
}
//省略领取优惠券逻辑...
return CommonResult.success(null,"领取成功");
}
//改进后
@Override
public void add(Long couponId) {
UmsMember currentMember = memberService.getCurrentMember();
//获取优惠券信息,判断数量
SmsCoupon coupon = couponMapper.selectByPrimaryKey(couponId);
if(coupon==null){
Asserts.fail("优惠券不存在");
}
if(coupon.getCount()<=0){
Asserts.fail("优惠券已经领完了");
}
Date now = new Date();
if(now.before(coupon.getEnableTime())){
Asserts.fail("优惠券还没到领取时间");
}
//判断用户领取的优惠券数量是否超过限制
SmsCouponHistoryExample couponHistoryExample = new SmsCouponHistoryExample();
couponHistoryExample.createCriteria().andCouponIdEqualTo(couponId).andMemberIdEqualTo(currentMember.getId());
long count = couponHistoryMapper.countByExample(couponHistoryExample);
if(count>=coupon.getPerLimit()){
Asserts.fail("您已经领取过该优惠券");
}
//省略领取优惠券逻辑...
}
}
- Здесь мы вводим идентификатор купона, который не существует, чтобы протестировать функцию, и он вернет
优惠券不存在
сообщение об ошибке.
Преимущества и недостатки
Преимущество использования глобальных исключений для обработки логики проверки заключается в том, что она более гибкая и может обрабатывать сложную логику проверки. Недостатком является то, что нам нужно повторно писать код проверки, в отличие от использования Hibernate Validator, пока вы используете аннотации. Но мы можемAsserts
Добавьте в класс несколько инструментальных методов, чтобы улучшить его функции, например, определить, пуст ли он, определить длину и т. д., которые можно реализовать самостоятельно.
Суммировать
Мы можем использовать два метода вместе, например, простую проверку параметров с помощью Hibernate Validator и некоторую сложную проверку, включающую операции базы данных с использованием глобальной обработки исключений.
Адрес исходного кода проекта
публика
проект торгового центраПолный набор учебных пособий сериализуется,Обратите внимание на публичный аккаунтПолучите это прямо сейчас.