springboot 數(shù)據(jù)驗(yàn)證( JSR-303 & 自定義驗(yàn)證器 )

在日常工作中队橙,往往需要驗(yàn)證參數(shù)的合法性华弓,因此,springMVC提供了驗(yàn)證參數(shù)的機(jī)制冠句,一方面迷捧,他可以支持JSR-303注解驗(yàn)證织咧;另一方面,因?yàn)闃I(yè)務(wù)的復(fù)雜性漠秋,需要自定義驗(yàn)證規(guī)則笙蒙,本篇來探討相關(guān)問題。立志工具人膛堤。一起干飯手趣!


本章主要內(nèi)容

  • JSR-303 驗(yàn)證

  • SpringMVC參數(shù)驗(yàn)證機(jī)制


1.JSR-303驗(yàn)證

JSR 303: Bean Validation官方文檔

JSR-303驗(yàn)證主要是通過注解的方式進(jìn)行的。

  • 引入相關(guān)依賴
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
  • 給參數(shù)對象添加校驗(yàn)注解
@Data
public class User {
    
    private Integer id;
    @NotBlank(message = "用戶名不能為空")
    private String username;
    @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密碼必須為8~16個字母和數(shù)字組合")
    private String password;
    @Email
    private String email;
    private Integer gender;

}
  • Controller 中需要校驗(yàn)的參數(shù)Bean前添加 @Valid 開啟校驗(yàn)功能
@RestController
@RequestMapping("/user")
public class UserController {

    @PostMapping("")
    public Result save (@Valid User user , BindingResult bindingResult)  {
        if (bindingResult.hasErrors()) {
            Map<String , String> map = new HashMap<>();
            bindingResult.getFieldErrors().forEach( (item) -> {
                String message = item.getDefaultMessage();
                String field = item.getField();
                map.put( field , message );
            } );
            return Result.build( 400 , "非法參數(shù) !" , map);
        }
        return Result.ok();
    }

}

  • 規(guī)范內(nèi)嵌的約束注解
規(guī)范內(nèi)嵌的約束注解
  • 規(guī)范內(nèi)嵌的約束注解
Constraint 詳細(xì)信息
@Email 被注釋的元素必須是電子郵箱地址
@Length 被注釋的字符串的大小必須在指定的范圍內(nèi)
@NotEmpty 被注釋的字符串的必須非空
@Range 被注釋的元素必須在合適的范圍內(nèi)

2.SpringMVC參數(shù)驗(yàn)證機(jī)制

  • SpringMVC中 的Validator (驗(yàn)證器)
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.validation;

public interface Validator {
    boolean supports(Class<?> var1);

    void validate(Object var1, Errors var2);
}

spring定義驗(yàn)證器接口肥荔,它定義了兩個方法绿渣,其中supports方法參數(shù)為需要驗(yàn)證的POJO類型,如果該方法返回true燕耿,則Spring會使用當(dāng)前驗(yàn)證其的validation方法去驗(yàn)證POJO中符,而validation方法包含需要的target對象和錯誤對象errors,其中target是參數(shù)綁定后的POJO誉帅,這樣便可以通過這個參數(shù)對象進(jìn)行業(yè)務(wù)邏輯的自定義驗(yàn)證淀散。如果發(fā)現(xiàn)錯誤,則可以保存到errors對象中蚜锨,然后返回給控制器档插。

  • 自定義用戶驗(yàn)證器
package com.dylan.mall.validation;

import com.dylan.mall.component.Student;
import com.dylan.mall.component.User;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

/**
 * @author Administrator
 */
public class UserValidator implements Validator {
    //驗(yàn)證器只支持User

    @Override
    public boolean supports(Class<?> clazz) {
        return clazz.equals(User.class);
    }

    /**
     * 邏輯驗(yàn)證
     */
    @Override
    public void validate(Object target, Errors errors) {
        if (target == null) {
            errors.rejectValue("", null, "用戶不能為空");
            return;
        }
        //強(qiáng)制轉(zhuǎn)換
        User user = (User) target;
        //用戶名非空串
        if (StringUtils.isEmpty(user.getUserName())) {
            //增加錯誤,可以進(jìn)入控制器方法
            errors.rejectValue("userName", null, "用戶名不能為空");
        }
    }
}

有了驗(yàn)證器亚再,需要spring對象自動啟動它郭膛。在springMVC中提供了一個注解@InitBinder,他的作用是在執(zhí)行控制器方法前氛悬,處理器會先執(zhí)行標(biāo)@InitBinder標(biāo)注的方法则剃。這時將WebDataBinder對象作為參數(shù)傳遞到方法中,通過這層關(guān)系得到WebDataBinder對象如捅,這個對象有一個setValidator方法棍现,它可以綁定自定義驗(yàn)證器,這樣就可以在獲取參數(shù)之后镜遣,通過自定義的驗(yàn)證器去驗(yàn)證參數(shù)己肮。

package com.dylan.mall.controller;

import com.dylan.mall.component.User;
import com.dylan.mall.validation.UserValidator;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.validation.Valid;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Administrator
 */
@Controller
@RequestMapping("/user")
public class UserController {
    /**
     * 調(diào)用控制器前先執(zhí)行這個方法
     */
    @InitBinder
    public void initBinder(WebDataBinder webDataBinder) {
        //綁定驗(yàn)證器
        webDataBinder.setValidator(new UserValidator());
        //定義日期參數(shù)格式,參數(shù)不在需注解@DateTimeFormat,boolean參數(shù)表示是否允許為空
        webDataBinder.registerCustomEditor(Date.class,
                new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false)
        );
    }

    @GetMapping("/validator")
    @ResponseBody
    public Map<String, Object> validator(@Valid User user, Errors errors,Date date) {
        Map<String, Object> map = new HashMap<>();
        map.put("user", user);
        map.put("date", date);
        if (errors.hasErrors()) {
            List<ObjectError> oes= errors.getAllErrors();
            for (ObjectError oe : oes) {
                if (oe instanceof FieldError) {
                    FieldError fe = (FieldError) oe;
                    map.put(fe.getField(), fe.getDefaultMessage());
                } else {
                    map.put(oe.getObjectName(), oe.getDefaultMessage());
                }
            }
        }
        return map;
    }

}

user類

package com.dylan.mall.component;

import lombok.Data;

/**
 * @author Administrator
 */
@Data
public class User {
    private String userName;
    private Integer age;
}

輸出結(jié)果:

自定義校驗(yàn)結(jié)果

通過這樣的自定義朴肺,在使用注解@Valid標(biāo)注User參數(shù)后窖剑,SpringMVC就會去遍歷對應(yīng)的驗(yàn)證器,當(dāng)遍歷到UserValidator時戈稿,會去執(zhí)行它的supports方法。因?yàn)樵摲椒ǚ祷豻rue讶舰,所以SpringMVC會用這個驗(yàn)證器去驗(yàn)證User類的數(shù)據(jù)鞍盗。


不要以為每天把功能完成了就行了,這種思想是要不得的跳昼,互勉~般甲!

若文章對您有用,請點(diǎn)贊支持哦鹅颊。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敷存,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子堪伍,更是在濱河造成了極大的恐慌锚烦,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帝雇,死亡現(xiàn)場離奇詭異涮俄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)尸闸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門彻亲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吮廉,你說我怎么就攤上這事苞尝。” “怎么了宦芦?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵宙址,是天一觀的道長。 經(jīng)常有香客問我踪旷,道長曼氛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任令野,我火速辦了婚禮舀患,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘气破。我一直安慰自己聊浅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著低匙,像睡著了一般旷痕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上顽冶,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天欺抗,我揣著相機(jī)與錄音,去河邊找鬼强重。 笑死绞呈,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的间景。 我是一名探鬼主播佃声,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼倘要!你這毒婦竟也來了圾亏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤封拧,失蹤者是張志新(化名)和其女友劉穎志鹃,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哮缺,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡弄跌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了尝苇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铛只。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖糠溜,靈堂內(nèi)的尸體忽然破棺而出淳玩,到底是詐尸還是另有隱情,我是刑警寧澤非竿,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布蜕着,位于F島的核電站,受9級特大地震影響红柱,放射性物質(zhì)發(fā)生泄漏承匣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一锤悄、第九天 我趴在偏房一處隱蔽的房頂上張望韧骗。 院中可真熱鬧,春花似錦零聚、人聲如沸袍暴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽政模。三九已至岗宣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間淋样,已是汗流浹背耗式。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留习蓬,地道東北人纽什。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像躲叼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子企巢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容