springboot 数据验证( JSR-303 & 自定义验证器 )

在日常工作中,往往需要验证参数的合法性,因此,springMVC提供了验证参数的机制,一方面,他可以支持JSR-303注解验证;另一方面,因为业务的复杂性,需要自定义验证规则,本篇来探讨相关问题。立志工具人。一起干饭!


本章主要内容

  • JSR-303 验证

  • SpringMVC参数验证机制


1.JSR-303验证

JSR 303: Bean Validation官方文档

JSR-303验证主要是通过注解的方式进行的。

  • 引入相关依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
  • 给参数对象添加校验注解
@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个字母和数字组合")
    private String password;
    @Email
    private String email;
    private Integer gender;

}
  • Controller 中需要校验的参数Bean前添加 @Valid 开启校验功能
@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 , "非法参数 !" , map);
        }
        return Result.ok();
    }

}

  • 规范内嵌的约束注解
规范内嵌的约束注解
  • 规范内嵌的约束注解
Constraint 详细信息
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range 被注释的元素必须在合适的范围内

2.SpringMVC参数验证机制

  • SpringMVC中 的Validator (验证器)
//
// 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定义验证器接口,它定义了两个方法,其中supports方法参数为需要验证的POJO类型,如果该方法返回true,则Spring会使用当前验证其的validation方法去验证POJO,而validation方法包含需要的target对象和错误对象errors,其中target是参数绑定后的POJO,这样便可以通过这个参数对象进行业务逻辑的自定义验证。如果发现错误,则可以保存到errors对象中,然后返回给控制器。

  • 自定义用户验证器
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 {
    //验证器只支持User

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

    /**
     * 逻辑验证
     */
    @Override
    public void validate(Object target, Errors errors) {
        if (target == null) {
            errors.rejectValue("", null, "用户不能为空");
            return;
        }
        //强制转换
        User user = (User) target;
        //用户名非空串
        if (StringUtils.isEmpty(user.getUserName())) {
            //增加错误,可以进入控制器方法
            errors.rejectValue("userName", null, "用户名不能为空");
        }
    }
}

有了验证器,需要spring对象自动启动它。在springMVC中提供了一个注解@InitBinder,他的作用是在执行控制器方法前,处理器会先执行标@InitBinder标注的方法。这时将WebDataBinder对象作为参数传递到方法中,通过这层关系得到WebDataBinder对象,这个对象有一个setValidator方法,它可以绑定自定义验证器,这样就可以在获取参数之后,通过自定义的验证器去验证参数。

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 {
    /**
     * 调用控制器前先执行这个方法
     */
    @InitBinder
    public void initBinder(WebDataBinder webDataBinder) {
        //绑定验证器
        webDataBinder.setValidator(new UserValidator());
        //定义日期参数格式,参数不在需注解@DateTimeFormat,boolean参数表示是否允许为空
        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;
}

输出结果:

自定义校验结果

通过这样的自定义,在使用注解@Valid标注User参数后,SpringMVC就会去遍历对应的验证器,当遍历到UserValidator时,会去执行它的supports方法。因为该方法返回true,所以SpringMVC会用这个验证器去验证User类的数据。


不要以为每天把功能完成了就行了,这种思想是要不得的,互勉~!

若文章对您有用,请点赞支持哦。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容