JSR303參數(shù)校驗配置

一败徊、環(huán)境

  • jdk1.8
  • idea2018.2.2
  • maven3.5.3
  • mybatsi-plus3.4.1
  • mysql5.7

二、項目配置

  • pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.yxl</groupId>
    <artifactId>yxl</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0</version>
    <modules>
        <module>yxl_project01</module>
    </modules>
    <name>yxl</name>
    <description>公共項目</description>

    <properties>
        <druid-version>1.2.6</druid-version>
        <mybatis-plus-version>3.4.1</mybatis-plus-version>
        <mysql-connector-version>8.0.23</mysql-connector-version>
        <lombok-version>1.18.18</lombok-version>
        <hutool-version>5.6.5</hutool-version>
        <httpcore-version>4.4.12</httpcore-version>
        <validation-version>2.0.1.Final</validation-version>

    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.5.6</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>

            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>${druid-version}</version>
            </dependency>

            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus-version}</version>
            </dependency>

            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql-connector-version}</version>
            </dependency>

            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok-version}</version>
            </dependency>

            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>${hutool-version}</version>
            </dependency>

            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpcore</artifactId>
                <version>${httpcore-version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

注:
spirngboot升級到2.3之后李皇,hibernate-validator消失吓著,需要自己手動加入jar包

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
 </dependency>

或者是自己手動引入依賴

<dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.0.17.Final</version>
 </dependency>
  • 統(tǒng)一返回結(jié)果
package com.yxl.common.util;

import org.apache.http.HttpStatus;

import java.util.HashMap;
import java.util.Map;

/**
 * @Classname R
 * @Description TODO
 * @Date 2021-12-20 17:19
 * @Created by yxl
 */
public class R extends HashMap<String,Object> {
    private static final long serialVersionUID = 1L;

    public R(){
        put("code",0);
        put("msg","success");
    }

    public static R error() {
        return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知異常习贫,請聯(lián)系管理員");
    }

    public static R error(String msg) {
        return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
    }

    public static R error(int code, String msg) {
        R r = new R();
        r.put("code", code);
        r.put("msg", msg);
        return r;
    }

    public static R ok(String msg) {
        R r = new R();
        r.put("msg", msg);
        return r;
    }

    public static R ok(Map<String, Object> map) {
        R r = new R();
        r.putAll(map);
        return r;
    }

    public static R ok() {
        return new R();
    }

    public R put(String key, Object value) {
        super.put(key, value);
        return this;
    }
}
  • controller
package com.yxl.project01.controller;

import com.yxl.common.util.R;
import com.yxl.project01.entity.User;
import com.yxl.project01.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @Classname UserController
 * @Description TODO
 * @Date 2021-12-20 17:14
 * @Created by yxl
 */
@RestController
@RequestMapping("/user")
public class UserController {

}
  • dao
package com.yxl.project01.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yxl.project01.entity.User;
import org.apache.ibatis.annotations.Mapper;

/**
 * @Classname UserMapper
 * @Description TODO
 * @Date 2021-12-20 17:10
 * @Created by yxl
 */
@Mapper
public interface UserMapper extends BaseMapper<User> {

}
  • entity
package com.yxl.project01.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.util.Date;

/**
 * @Classname User
 * @Description TODO
 * @Date 2021-12-20 17:07
 * @Created by yxl
 */
@Data
@TableName("tb_user")
public class User {

    @TableId
    private Long id;

    private String name;

    private Integer age;

    private Date birthday;

    private String icon;

    private Integer status;

    private String description;
}
  • service
package com.yxl.project01.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.yxl.project01.entity.User;

/**
 * @Classname UserService
 * @Description TODO
 * @Date 2021-12-20 17:11
 * @Created by yxl
 */
public interface UserService extends IService<User> {

}
  • serviceImpl
package com.yxl.project01.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yxl.project01.dao.UserMapper;
import com.yxl.project01.entity.User;
import com.yxl.project01.service.UserService;
import org.springframework.stereotype.Service;

/**
 * @Classname UserServiceImpl
 * @Description TODO
 * @Date 2021-12-20 17:12
 * @Created by yxl
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
  • 啟動類
package com.yxl.project01;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @Classname yxlProject01Application
 * @Description TODO
 * @Date 2021-12-20 17:03
 * @Created by yxl
 */
@SpringBootApplication
@MapperScan("com.yxl.project01.dao")
public class yxlProject01Application {

    public static void main(String[] args) {
        SpringApplication.run(yxlProject01Application.class,args);
    }
}

三乳愉、JSR303參數(shù)校驗配置

1兄淫、在UserController中新增save接口用來添加用戶

/**
     * JSR303配置案例
     */
    @PostMapping("/save")
    public R save(@RequestBody User user){
        userService.save(user);
        return R.ok();
    }

2屯远、使用Postman對接口進行測試

測試數(shù)據(jù)

{
"name": "張三",
"age": 20,
"birthday": "1995-03-02",
"icon": "http://www.baidu.com",
"status": "0",
"description": "描述"
}
測試結(jié)果如下:


從上邊圖可以看出來執(zhí)行成功了,也新增到數(shù)據(jù)庫了捕虽。
那么我們現(xiàn)在把所有新增的字段都置空慨丐,看新增的時候會怎么樣?

{
"name": "",
"age": "",
"birthday": "",
"icon": "",
"status": "",
"description": ""
}



從執(zhí)行結(jié)果中可以看出泄私,新增依舊是執(zhí)行成功的房揭,但是數(shù)據(jù)庫加入的都是空數(shù)據(jù),這樣肯定是不行的挖滤,那么我們怎么樣能限制到新增的時候給每個屬性加限制呢崩溪?就比如說名稱不能為空浅役,url必須是合法的url地址等等斩松。這時候我們就用到了JSR303參數(shù)校驗注解了。演示如下:

1觉既、在User.Class中新增校驗注解如下:

package com.yxl.project01.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Date;

/**
 * @Classname User
 * @Description TODO
 * @Date 2021-12-20 17:07
 * @Created by yxl
 */
@Data
@TableName("tb_user")
public class User {

    @TableId
    private Long id;

    @NotBlank(message = "用戶名不能為空")
    private String name;

    @NotNull(message = "年齡不能為空")
    private Integer age;

    @NotNull(message = "出生日期不能為空")
    private Date birthday;

    @NotBlank(message = "頭像不能為空")
    private String icon;

    private Integer status;

    private String description;
}

再此對@NotBlank惧盹、@NotNull、@NotEmpty三個注解做一個解釋

@NotBlank:只用于String,不能為null且trim()之后size>0
@NotNull:用在基本數(shù)據(jù)類型上面瞪讼。不能為null钧椰,但可以為empty,沒有Size的約束
@NotEmpty:用在集合類上面。加了@NotEmpty的String類符欠、Collection嫡霞、Map、數(shù)組希柿,是不能為null或者長度為0的(String Collection Map的isEmpty()方法)

2诊沪、在Controller中使用校驗注解

/**
     * JSR303配置案例
     */
    @PostMapping("/save")
    public R save(@Valid @RequestBody User user){
        userService.save(user);
        return R.ok();
    }

3、重啟服務(wù)器曾撤,再次使用空數(shù)值測試結(jié)果如下:

{
    "timestamp": "2021-12-22T07:19:17.464+0000",
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "NotNull.user.age",
                "NotNull.age",
                "NotNull.java.lang.Integer",
                "NotNull"
            ],
            "arguments": [
                {
                    "codes": [
                        "user.age",
                        "age"
                    ],
                    "arguments": null,
                    "defaultMessage": "age",
                    "code": "age"
                }
            ],
            "defaultMessage": "年齡不能為空",
            "objectName": "user",
            "field": "age",
            "rejectedValue": null,
            "bindingFailure": false,
            "code": "NotNull"
        },
        {
            "codes": [
                "NotNull.user.birthday",
                "NotNull.birthday",
                "NotNull.java.util.Date",
                "NotNull"
            ],
            "arguments": [
                {
                    "codes": [
                        "user.birthday",
                        "birthday"
                    ],
                    "arguments": null,
                    "defaultMessage": "birthday",
                    "code": "birthday"
                }
            ],
            "defaultMessage": "出生日期不能為空",
            "objectName": "user",
            "field": "birthday",
            "rejectedValue": null,
            "bindingFailure": false,
            "code": "NotNull"
        },
        {
            "codes": [
                "NotBlank.user.icon",
                "NotBlank.icon",
                "NotBlank.java.lang.String",
                "NotBlank"
            ],
            "arguments": [
                {
                    "codes": [
                        "user.icon",
                        "icon"
                    ],
                    "arguments": null,
                    "defaultMessage": "icon",
                    "code": "icon"
                }
            ],
            "defaultMessage": "頭像不能為空",
            "objectName": "user",
            "field": "icon",
            "rejectedValue": "",
            "bindingFailure": false,
            "code": "NotBlank"
        },
        {
            "codes": [
                "NotBlank.user.name",
                "NotBlank.name",
                "NotBlank.java.lang.String",
                "NotBlank"
            ],
            "arguments": [
                {
                    "codes": [
                        "user.name",
                        "name"
                    ],
                    "arguments": null,
                    "defaultMessage": "name",
                    "code": "name"
                }
            ],
            "defaultMessage": "用戶名不能為空",
            "objectName": "user",
            "field": "name",
            "rejectedValue": "",
            "bindingFailure": false,
            "code": "NotBlank"
        }
    ],
    "message": "Validation failed for object='user'. Error count: 4",
    "path": "/user/save"
}

從結(jié)果看參數(shù)校驗注解已生效端姚。
但是回過頭來香,如果是前后端分離項目的話挤悉,返回給前端這種格式是不行的渐裸,那么我們就可以利用全局異常處理,統(tǒng)一來返回結(jié)果給前端了装悲。

4昏鹃、在全局異常處理類中增加異常處理方法專門對MethodArgumentNotValidException.Class異常類進行處理,如下:

package com.yxl.project01.exception;

import com.yxl.common.exception.BizCodeEnume;
import com.yxl.common.util.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

/**
 * @Classname GlobalExceptionHandler
 * @Description TODO
 * @Date 2021-12-22 11:13
 * @Created by yxl
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(ArithmeticException.class)
    public R arithmeticExceptionHandler(ArithmeticException e){
        return R.error(BizCodeEnume.ARITHMETIC_EXCEPTION.getCode(),BizCodeEnume.ARITHMETIC_EXCEPTION.getMsg());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e){
        log.error("數(shù)據(jù)校驗出現(xiàn)問題{}诀诊,異常類型:{}",e.getMessage().getClass());
        BindingResult result = e.getBindingResult();
        Map<String,String> map = new HashMap<>();
        result.getFieldErrors().forEach(fieldError -> {
            map.put(fieldError.getField(),fieldError.getDefaultMessage());
        });
        return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",map);
    }
}

其中BizCodeEnume枚舉類如下:

package com.yxl.common.exception;

/**
 * @Classname BizCodeEnume
 * @Description TODO
 * @Date 2021-12-22 11:29
 * @Created by yxl
 */
public enum BizCodeEnume {

    UNKNOW_EXCEPTION(10000,"系統(tǒng)未知異常"),
    VAILD_EXCEPTION(10001,"參數(shù)格式校驗失敗"),
    ARITHMETIC_EXCEPTION(10001,"算術(shù)異常"),;

    private int code;
    private String msg;

    private BizCodeEnume(int code, String msg){
        this.code = code;
        this.msg = msg;
    }

    public int getCode(){
        return code;
    }

    public String getMsg(){
        return msg;
    }
}

新增全局異常處理后我們再測試一下盆顾,結(jié)果如下:

{
    "msg": "參數(shù)格式校驗失敗",
    "code": 10001,
    "data": {
        "birthday": "出生日期不能為空",
        "icon": "頭像不能為空",
        "name": "用戶名不能為空",
        "age": "年齡不能為空"
    }
}

這就達到了我們想要的結(jié)果。

三畏梆、參數(shù)校驗分組功能

參數(shù)校驗分組功能主要是針對哪些方法使用哪些參數(shù)校驗的您宪,比如:新增用戶前端不需要傳id等字段奈懒,修改的時候前端必須傳id,新增和修改都必須要傳name等宪巨。這種場景的話磷杏,我們就需要用到分組功能了。
1捏卓、我們在UserController.class中新增修改接口极祸,如下:

/**
     * 參數(shù)校驗分組功能
     */
    @PostMapping("/update")
    public R update(@RequestBody User user){
        userService.updateById(user);
        return R.ok();
    }

2、配置User實體類

package com.yxl.project01.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.yxl.common.valid.SaveGroup;
import com.yxl.common.valid.UpdateGroup;
import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import java.util.Date;

/**
 * @Classname User
 * @Description TODO
 * @Date 2021-12-20 17:07
 * @Created by yxl
 */
@Data
@TableName("tb_user")
public class User {

    @TableId
    @NotNull(message = "修改需要傳遞id",groups = {UpdateGroup.class})
    @Null(message = "新增不需要傳遞id",groups = {SaveGroup.class})
    private Long id;

    @NotBlank(message = "用戶名不能為空",groups = {SaveGroup.class,UpdateGroup.class})
    private String name;

    @NotNull(message = "年齡不能為空")
    private Integer age;

    @NotNull(message = "出生日期不能為空")
    private Date birthday;

    @NotBlank(message = "頭像不能為空")
    private String icon;

    private Integer status;

    private String description;
}

其中SaveGroup.class和UpdateGroup.class為groups分組需要傳遞的標(biāo)識符怠晴,即一個空的接口遥金。如下:

package com.yxl.common.valid;

/**
 * @Classname SaveGroup
 * @Description TODO
 * @Date 2021-12-22 15:42
 * @Created by yxl
 */
public interface SaveGroup {
}
package com.yxl.common.valid;

/**
 * @Classname UpdateGroup
 * @Description TODO
 * @Date 2021-12-22 15:42
 * @Created by yxl
 */
public interface UpdateGroup {
}

3、在UserController中save接口和update接口增加注解

/**
     * JSR303配置案例
     */
    @PostMapping("/save")
    public R save(@Validated({SaveGroup.class}) @RequestBody User user){
        userService.save(user);
        return R.ok();
    }

    /**
     * 參數(shù)校驗分組功能
     */
    @PostMapping("/update")
    public R update(@Validated({UpdateGroup.class}) @RequestBody User user){
        userService.updateById(user);
        return R.ok();
    }

4蒜田、執(zhí)行修改接口稿械,提示修改需要傳遞id。如下:


我們傳一個id進去冲粤,修改一條數(shù)據(jù)試試:



數(shù)據(jù)庫數(shù)據(jù)已更改

5美莫、執(zhí)行新增接口,提示新增不需要傳id梯捕。如下:


我們把Id去掉在測試




數(shù)據(jù)庫已新增成功

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末厢呵,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子傀顾,更是在濱河造成了極大的恐慌襟铭,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件短曾,死亡現(xiàn)場離奇詭異寒砖,居然都是意外死亡,警方通過查閱死者的電腦和手機错英,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門入撒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人椭岩,你說我怎么就攤上這事茅逮。” “怎么了判哥?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵献雅,是天一觀的道長。 經(jīng)常有香客問我塌计,道長挺身,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任锌仅,我火速辦了婚禮章钾,結(jié)果婚禮上墙贱,老公的妹妹穿的比我還像新娘。我一直安慰自己贱傀,他們只是感情好惨撇,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著府寒,像睡著了一般魁衙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上株搔,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天剖淀,我揣著相機與錄音,去河邊找鬼纤房。 笑死纵隔,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的帆卓。 我是一名探鬼主播巨朦,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼米丘,長吁一口氣:“原來是場噩夢啊……” “哼剑令!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拄查,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤吁津,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后堕扶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碍脏,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年稍算,在試婚紗的時候發(fā)現(xiàn)自己被綠了典尾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡糊探,死狀恐怖钾埂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情科平,我是刑警寧澤岛心,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布磨隘,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏驶沼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧儡炼,春花似錦、人聲如沸查蓉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽楣责。三九已至聂沙,卻和暖如春秆麸,著一層夾襖步出監(jiān)牢的瞬間及汉,已是汗流浹背沮趣。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留坷随,地道東北人房铭。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓温眉,卻偏偏與公主長得像缸匪,于是被迫代替她去往敵國和親类溢。 傳聞我的和親對象是個殘疾皇子凌蔬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

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