二つの方法SpringBoot処理検証ロジック、本当に非常に機知に!

SpringBoot実際の電力供給のアイテムモール(30K +スター)住所:github.com/macrozheng / ...

概要

開発インタフェースは、多くの場合、パラメータをチェックする必要がある場合通常、検証処理ロジックは、ここに二つの方法を提供します。一つは、他のは、ハンドルにグローバルな例外を使用してこれらの2つの方法の使用についての話を聞かせすることがある、ハンドルにHibernateバリを使用することです。

Hibernateバリ

HibernateバリはSpringBootは、内蔵された検証フレームワーク、統合SpringBootが自動的にそれを統合する限り、我々はそれの上のオブジェクトに提供されたアノテーションを使って検証パラメータを完了することができますよう。

共通アノテーション

のは、チェック機能にHibernate Validatorが提供している印象があるが、一般的なノートを学びましょう。

  • @Null:注釈付きプロパティはnullでなければなりません。
  • @NotNull:注釈付きプロパティはnullにすることはできません。
  • @AssertTrue:注釈付きプロパティがtrueでなければなりません。
  • @AssertFalse:注釈付きプロパティがfalseでなければなりません。
  • @Min:その値よりも大きいか等しい値でなければならない注釈付き属性。
  • @max:その値より小さいまたは等しい値でなければならない注釈付き属性。
  • @size:属性が最小値と最大値の間で注釈を付けなければなりません。
  • @Pattern:注釈付きプロパティはその定義された正規表現正規表現に準拠する必要があります。
  • @NotBlank:注釈付きの文字列が空の文字列にすることはできません。
  • @NotEmpty:注釈付きプロパティは空にすることはできません。
  • @email:注釈付きプロパティは、メールボックス形式に準拠する必要があります。

使用

次に我々は、Hibernateバリデータを使用するようにインターフェイスパラメータの観点から例えば、小切手にブランドを追加し、AOPのいくつかの知識を伴う、離陸した友人が理解していない参照できる「アクセスログレコードに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は、そのvalidatedBy属性を指定ロジックをチェックする実装クラスを使用して、注釈@Constraintを追加します。
/**
 * 用户验证状态是否在指定范围内的注解
 * 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 {};
}
复制代码
  • そして、ConstraintValidatorインタフェースを実装し、カスタムバリデーションのアノテーションクラスを指定する最初の必要性は、一般的な2つのパラメータを指定することが必要である、チェックロジックとしてFlagValidatorClass実装クラスを作成し、検証するための第2のプロパティは、あなたに指定されていますタイプ、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コントローラオブジェクトのメソッドの必要性など、いくつかの欠点を持っている、唯一の、いくつかの簡単なチェックサムをサポート満足することはできませんデータベースを照会するために関与して確認してください。

グローバル例外ハンドラ

ハンドルの検証ロジックの考えにグローバル例外ハンドラを使用して、@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());
    }
}
复制代码
  • ここでは例として、クーポンコードを受信するユーザーを取る、聞かせてのは、初見コントローラ層のコードの下に改善前と後のコードを比較します。改良された方法限り、サービスの実装を成功に失敗し、エラーメッセージを返すように直接スローに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,"领取成功");
    }    
}
复制代码
  • 返された結果、改善戻り無効ことを除いて、サービス・インターフェースのコード、見てください。実際にCommonResultの役割は、常に統一パッケージに取り込まれたデータにサービスに行った結果を返し、この原則に改善前の練習に反して、実際には、この問題に対する解決策を改善しました。
/**
 * 用户优惠券管理Service
 * Created by macro on 2018/8/29.
 */
public interface UmsMemberCouponService {
    /**
     * 会员添加优惠券(改进前)
     */
    @Transactional
    CommonResult add(Long couponId);

    /**
     * 会员添加优惠券(改进后)
     */
    @Transactional
    void add(Long couponId);
}
复制代码
  • コードのクラスのサービスの実装を見て、検証ロジックは、元の論理が戻るに達成するために失敗するアサーションにメソッド呼び出しをCommonResult見ることができます。
/**
 * 会员优惠券管理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("您已经领取过该优惠券");
         }
         //省略领取优惠券逻辑...
     }
}
复制代码
  • ここでは、テストに何もこの機能をクーポンIDを入力し、それを返していない优惠券不存在エラーメッセージを。

長所と短所

ハンドルにグローバル例外チェック・ロジックを使用することの利点は、比較的柔軟性があり、複雑な検証ロジックを処理することができます。欠点は、我々は長い間それを使用してHibernateバリデータアノテーションを使用していないなどとして、コードを書くためのチェックを繰り返す必要があるということです。しかし、我々は、上記の中ですることができAsserts、空気とは、長さを決定するかどうかを決定するなど、その機能を強化するために、いくつかのクラスのユーティリティメソッドを追加するので、自分自身を達成することができます。

概要

我々は2つのアプローチ、このようなHibernateバリを実装するために使用されるパラメータの簡単なチェックとして、一緒に使用すると、データベース操作に関連するいくつかのグローバルな例外ハンドラを達成するために、複雑なパリティを使用を組み合わせることができます。

プロジェクトのソースアドレス

github.com/macrozheng/...

いいえ公共ありません

モールは、プロジェクトで連載フルチュートリアル国民の関心番号取得する最初の時間を。

いいえ公共絵ません

おすすめ

転載: juejin.im/post/5e6636da6fb9a07cb24aaf00