<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot中實(shí)現(xiàn)業(yè)務(wù)校驗(yàn),這種方式才叫優(yōu)雅!

          共 1998字,需瀏覽 4分鐘

           ·

          2022-03-10 08:52

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

          • 參數(shù)校驗(yàn)
          • 業(yè)務(wù)規(guī)則校驗(yàn)
          • 代碼實(shí)戰(zhàn)
            • 自定義注解
            • 實(shí)現(xiàn)業(yè)務(wù)校驗(yàn)規(guī)則
            • 使用
            • 測(cè)試
            • 小結(jié)


          在日常的接口開發(fā)中,為了保證接口的穩(wěn)定安全,我們一般需要在接口邏輯中處理兩種校驗(yàn):

          1. 參數(shù)校驗(yàn)
          2. 業(yè)務(wù)規(guī)則校驗(yàn)

          首先我們先看看參數(shù)校驗(yàn)。

          參數(shù)校驗(yàn)

          參數(shù)校驗(yàn)很好理解,比如登錄的時(shí)候需要校驗(yàn)用戶名密碼是否為空,創(chuàng)建用戶的時(shí)候需要校驗(yàn)郵件、手機(jī)號(hào)碼格式是否準(zhǔn)確。

          而實(shí)現(xiàn)參數(shù)校驗(yàn)也非常簡(jiǎn)單,我們只需要使用Bean Validation校驗(yàn)框架即可,借助它提供的校驗(yàn)注解我們可以非常方便的完成參數(shù)校驗(yàn)。

          常見的校驗(yàn)注解有:

          @Null、@NotNull、@AssertTrue@AssertFalse、@Min、@Max、@DecimalMin、@DecimalMax@Negative、@NegativeOrZero、@Positive、@PositiveOrZero、@Size@Digits、@Past、@PastOrPresent@Future、@FutureOrPresent、@Pattern@NotEmpty、@NotBlank、@Email

          接下來我們?cè)倏纯礃I(yè)務(wù)規(guī)則校驗(yàn)。

          基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能。

          項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro

          業(yè)務(wù)規(guī)則校驗(yàn)

          業(yè)務(wù)規(guī)則校驗(yàn)指接口需要滿足某些特定的業(yè)務(wù)規(guī)則,舉個(gè)例子:業(yè)務(wù)系統(tǒng)的用戶需要保證其唯一性,用戶屬性不能與其他用戶產(chǎn)生沖突,不允許與數(shù)據(jù)庫中任何已有用戶的用戶名稱、手機(jī)號(hào)碼、郵箱產(chǎn)生重復(fù)。

          這就要求在創(chuàng)建用戶時(shí)需要校驗(yàn)用戶名稱、手機(jī)號(hào)碼、郵箱是否被注冊(cè) ;編輯用戶時(shí)不能將信息修改成已有用戶的屬性 。

          95%的程序員當(dāng)面對(duì)這種業(yè)務(wù)規(guī)則校驗(yàn)時(shí)往往選擇寫在service邏輯中,常見的代碼邏輯如下:

          public?void?create(User?user)?{
          ????Account?account?=?accountDao.queryByUserNameOrPhoneOrEmail(user.getName(),user.getPhone(),user.getEmail());
          ????if?(account?!=?null)?{
          ????????throw?new?IllegalArgumentException("用戶已存在,請(qǐng)重新輸入");
          ????}
          }

          雖然我在上一篇文章中介紹了使用Assert來優(yōu)化代碼可以使其看上去更簡(jiǎn)潔,但是將簡(jiǎn)單的校驗(yàn)交給 Bean Validation,而把復(fù)雜的校驗(yàn)留給自己,這簡(jiǎn)直是買櫝還珠故事的程序員版本。

          最優(yōu)雅的實(shí)現(xiàn)方法應(yīng)該是參考 Bean Validation 的標(biāo)準(zhǔn)方式,借助自定義校驗(yàn)注解完成業(yè)務(wù)規(guī)則校驗(yàn)。

          接下來我們通過上面提到的用戶接口案例,通過自定義注解完成業(yè)務(wù)規(guī)則校驗(yàn)。

          基于微服務(wù)的思想,構(gòu)建在 B2C 電商場(chǎng)景下的項(xiàng)目實(shí)戰(zhàn)。核心技術(shù)棧,是 Spring Boot + Dubbo 。未來,會(huì)重構(gòu)成 Spring Cloud Alibaba 。

          項(xiàng)目地址:https://github.com/YunaiV/onemall

          代碼實(shí)戰(zhàn)

          需求很容易理解,注冊(cè)新用戶時(shí),應(yīng)約束不與任何已有用戶的關(guān)鍵信息重復(fù);而修改自己的信息時(shí),只能與自己的信息重復(fù),不允許修改成已有用戶的信息。

          這些約束規(guī)則不僅僅為這兩個(gè)方法服務(wù),它們可能會(huì)在用戶資源中的其他入口被使用到,乃至在其他分層的代碼中被使用到,在 Bean 上做校驗(yàn)就能全部覆蓋上述這些使用場(chǎng)景。

          自定義注解

          首先我們需要?jiǎng)?chuàng)建兩個(gè)自定義注解,用于業(yè)務(wù)規(guī)則校驗(yàn):

          • UniqueUser:表示一個(gè)用戶是唯一的,唯一性包含:用戶名,手機(jī)號(hào)碼、郵箱
          @Documented
          @Retention(RUNTIME)
          @Target({FIELD,?METHOD,?PARAMETER,?TYPE})
          @Constraint(validatedBy?=?UserValidation.UniqueUserValidator.class)
          public?@interface?UniqueUser?
          {

          ????String?message()?default?"用戶名、手機(jī)號(hào)碼、郵箱不允許與現(xiàn)存用戶重復(fù)";

          ????Class[]?groups()?default?{};

          ????Class[]?payload()?default?{};
          }
          • NotConflictUser:表示一個(gè)用戶的信息是無沖突的,無沖突是指該用戶的敏感信息與其他用戶不重合
          @Documented
          @Retention(RUNTIME)
          @Target({FIELD,?METHOD,?PARAMETER,?TYPE})
          @Constraint(validatedBy?=?UserValidation.NotConflictUserValidator.class)
          public?@interface?NotConflictUser?
          {
          ????String?message()?default?"用戶名稱、郵箱、手機(jī)號(hào)碼與現(xiàn)存用戶產(chǎn)生重復(fù)";

          ????Class[]?groups()?default?{};

          ????Class[]?payload()?default?{};
          }

          實(shí)現(xiàn)業(yè)務(wù)校驗(yàn)規(guī)則

          想讓自定義驗(yàn)證注解生效,需要實(shí)現(xiàn) ConstraintValidator 接口。接口的第一個(gè)參數(shù)是 自定義注解類型 ,第二個(gè)參數(shù)是 被注解字段的類 ,因?yàn)樾枰r?yàn)多個(gè)參數(shù),我們直接傳入用戶對(duì)象。需要提到的一點(diǎn)是 ConstraintValidator 接口的實(shí)現(xiàn)類無需添加 @Component 它在啟動(dòng)的時(shí)候就已經(jīng)被加載到容器中了。

          @Slf4j
          public?class?UserValidation<T?extends?Annotation>?implements?ConstraintValidator<T,?User>?{

          ????protected?Predicate?predicate?=?c?->?true;

          ????@Resource
          ????protected?UserRepository?userRepository;

          ????@Override
          ????public?boolean?isValid(User?user,?ConstraintValidatorContext?constraintValidatorContext)?{
          ????????return?userRepository?==?null?||?predicate.test(user);
          ????}

          ????/**
          ?????*?校驗(yàn)用戶是否唯一
          ?????*?即判斷數(shù)據(jù)庫是否存在當(dāng)前新用戶的信息,如用戶名,手機(jī),郵箱
          ?????*/

          ????public?static?class?UniqueUserValidator?extends?UserValidation<UniqueUser>{
          ????????@Override
          ????????public?void?initialize(UniqueUser?uniqueUser)?{
          ????????????predicate?=?c?->?!userRepository.existsByUserNameOrEmailOrTelphone(c.getUserName(),c.getEmail(),c.getTelphone());
          ????????}
          ????}

          ????/**
          ?????*?校驗(yàn)是否與其他用戶沖突
          ?????*?將用戶名、郵件、電話改成與現(xiàn)有完全不重復(fù)的,或者只與自己重復(fù)的,就不算沖突
          ?????*/

          ????public?static?class?NotConflictUserValidator?extends?UserValidation<NotConflictUser>{
          ????????@Override
          ????????public?void?initialize(NotConflictUser?notConflictUser)?{
          ????????????predicate?=?c?->?{
          ????????????????log.info("user?detail?is?{}",c);
          ????????????????Collection?collection?=?userRepository.findByUserNameOrEmailOrTelphone(c.getUserName(),?c.getEmail(),?c.getTelphone());
          ????????????????//?將用戶名、郵件、電話改成與現(xiàn)有完全不重復(fù)的,或者只與自己重復(fù)的,就不算沖突
          ????????????????return?collection.isEmpty()?||?(collection.size()?==?1?&&?collection.iterator().next().getId().equals(c.getId()));
          ????????????};
          ????????}
          ????}

          }

          這里使用Predicate函數(shù)式接口對(duì)業(yè)務(wù)規(guī)則進(jìn)行判斷。

          使用

          @RestController
          @RequestMapping("/senior/user")
          @Slf4j
          @Validated
          public?class?UserController?{
          ????@Autowired
          ????private?UserRepository?userRepository;
          ????

          ????@PostMapping
          ????public?User?createUser(@UniqueUser?@Valid?User?user){
          ????????User?savedUser?=?userRepository.save(user);
          ????????log.info("save?user?id?is?{}",savedUser.getId());
          ????????return?savedUser;
          ????}

          ????@SneakyThrows
          ????@PutMapping
          ????public?User?updateUser(@NotConflictUser?@Valid?@RequestBody?User?user){
          ????????User?editUser?=?userRepository.save(user);
          ????????log.info("update?user?is?{}",editUser);
          ????????return?editUser;
          ????}
          }

          使用很簡(jiǎn)單,只需要在方法上加入自定義注解即可,業(yè)務(wù)邏輯中不需要添加任何業(yè)務(wù)規(guī)則的代碼。

          測(cè)試

          調(diào)用接口后出現(xiàn)如下錯(cuò)誤,說明業(yè)務(wù)規(guī)則校驗(yàn)生效。

          {
          ??"status":?400,
          ??"message":?"用戶名、手機(jī)號(hào)碼、郵箱不允許與現(xiàn)存用戶重復(fù)",
          ??"data":?null,
          ??"timestamp":?1644309081037
          }

          小結(jié)

          通過上面幾步操作,業(yè)務(wù)校驗(yàn)便和業(yè)務(wù)邏輯就完全分離開來,在需要校驗(yàn)時(shí)用@Validated注解自動(dòng)觸發(fā),或者通過代碼手動(dòng)觸發(fā)執(zhí)行,可根據(jù)你們項(xiàng)目的要求,將這些注解應(yīng)用于控制器、服務(wù)層、持久層等任何層次的代碼之中。

          這種方式比任何業(yè)務(wù)規(guī)則校驗(yàn)的方法都優(yōu)雅,推薦大家在項(xiàng)目中使用。在開發(fā)時(shí)可以將不帶業(yè)務(wù)含義的格式校驗(yàn)注解放到 Bean 的類定義之上,將帶業(yè)務(wù)邏輯的校驗(yàn)放到 Bean 的類定義的外面。這兩者的區(qū)別是放在類定義中的注解能夠自動(dòng)運(yùn)行,而放到類外面則需要像前面代碼那樣,明確標(biāo)出注解時(shí)才會(huì)運(yùn)行。

          項(xiàng)目源碼地址 : https://github.com/jianzh5/cloud-blog/

          ???

          1、發(fā)SQL,

          2、?Chrome會(huì)個(gè)?

          3、個(gè)SpringBoot44Java

          4QQ個(gè),!

          5、SpringBoot?發(fā),?

          點(diǎn)

          點(diǎn)

          點(diǎn)點(diǎn)

          點(diǎn)

          瀏覽 53
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品久久久久久精 | 国产又粗又长又硬免费视频 | 久操B| 中文字幕嫩性淫 | 欧美逼逼逼 |