您的位置:

jsr303代码校验(jsr303自定义校验参数)

jsr303代码校验(jsr303自定义校验参数)

更新:

本文目录一览:

JSR303校验的使用

SpringBoot中如果使用此数据检验,需添加依赖:

常用的校验

使用例子

错误提示

name不是邮箱格式

Spring JSR303验证,为什么在页面上使用 form:error显示不了错误信息

说明你对jsr的自定义类型转化器ConversionService没有了解,你虽然model里面已经添加了jsp中的modelAttribute的模型,但是你modelAttribute中的名字没有对应你的PortalUser类中的名字,应该jsp中modelAttribute=“”portalUser“”,你就可以解决你的问题

备注:在jsp中的modelAttribute必须要modelAttribute="POJO类名小写",原理和springmvc.xml中配置conversionService 的默认bean一样意思

javax 是如何实现依赖

参数校验是我们程序开发中必不可少的过程。用户在前端页面上填写表单时,前端js程序会校验参数的合法性,当数据到了后端,为了防止恶意操作,保持程序的健壮性,后端同样需要对数据进行校验。后端参数校验最简单的做法是直接在业务方法里面进行判断,当判断成功之后再继续往下执行。但这样带给我们的是代码的耦合,冗余。当我们多个地方需要校验时,我们就需要在每一个地方调用校验程序,导致代码很冗余,且不美观。

那么如何优雅的对参数进行校验呢?JSR303就是为了解决这个问题出现的,本篇文章主要是介绍 JSR303,Hibernate Validator 等校验工具的使用,以及自定义校验注解的使用。

校验框架介绍

JSR303 是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面,就可以在需要校验的时候进行校验了。注解如下:

Hibernate validator 在JSR303的基础上对校验注解进行了扩展,扩展注解如下:

Spring validtor 同样扩展了jsr303,并实现了方法参数和返回值的校验

Spring 提供了MethodValidationPostProcessor类,用于对方法的校验

代码实现

添加JAR包依赖

在pom.xml中添加如下依赖:

!--jsr 303--

dependency

groupIdjavax.validation/groupId

artifactIdvalidation-api/artifactId

version1.1.0.Final/version

/dependency

!-- hibernate validator--

dependency

groupIdorg.hibernate/groupId

artifactIdhibernate-validator/artifactId

version5.2.0.Final/version

/dependency

最简单的参数校验

1、Model 中添加校验注解

public class Book {    private long id;    /**

    * 书名     */

   @NotEmpty(message = "书名不能为空")    private String bookName;    /**

    * ISBN号     */

   @NotNull(message = "ISBN号不能为空")    private String bookIsbn;    /**

    * 单价     */

   @DecimalMin(value = "0.1",message = "单价最低为0.1")

private double price; // getter setter .......  }

2、在controller中使用此校验

/**

* 添加Book对象

* @param book     */

@RequestMapping(value = "/book", method = RequestMethod.POST)    public void addBook(@RequestBody @Valid Book book) {

System.out.println(book.toString());

}

当访问这个post接口时,如果参数不符合Model中定义的话,程序中就回抛出400异常,并提示错误信息。

自定义校验注解

虽然jSR303和Hibernate Validtor 已经提供了很多校验注解,但是当面对复杂参数校验时,还是不能满足我们的要求,这时候我们就需要 自定义校验注解。

下面以“List数组中不能含有null元素”为实例自定义校验注解

1、注解定义如下:

package com.beiyan.validate.annotation;import javax.validation.Constraint;import javax.validation.Payload;import java.lang.annotation.*;import static java.lang.annotation.ElementType.*;import static java.lang.annotation.ElementType.PARAMETER;import static java.lang.annotation.RetentionPolicy.RUNTIME;/**

* 自定义参数校验注解

* 校验 List 集合中是否有null 元素 */@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Constraint(validatedBy = ListNotHasNullValidatorImpl.class)////此处指定了注解的实现类为ListNotHasNullValidatorImplpublic @interface ListNotHasNull {    /**

* 添加value属性,可以作为校验时的条件,若不需要,可去掉此处定义     */

int value() default 0;

String message() default "List集合中不能含有null元素";

Class?[] groups() default {};

Class? extends Payload[] payload() default {};    /**

* 定义List,为了让Bean的一个属性上可以添加多套规则     */

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})

@Retention(RUNTIME)

@Documented

@interface List {

ListNotHasNull[] value();

}

}

2、注解实现类:

package com.beiyan.validate.annotation;import org.springframework.stereotype.Service;import javax.validation.ConstraintValidator;import javax.validation.ConstraintValidatorContext;import java.util.List;/**

* 自定义注解ListNotHasNull 的实现类

* 用于判断List集合中是否含有null元素 */@Servicepublic class ListNotHasNullValidatorImpl implements ConstraintValidatorListNotHasNull, List {    private int value;

@Override    public void initialize(ListNotHasNull constraintAnnotation) {        //传入value 值,可以在校验中使用

this.value = constraintAnnotation.value();

}    public boolean isValid(List list, ConstraintValidatorContext constraintValidatorContext) {        for (Object object : list) {            if (object == null) {                //如果List集合中含有Null元素,校验失败

return false;

}

}        return true;

}

}

3、model添加注解:

public class User {

//其他参数 ......./**

    * 所拥有的书籍列表     */

   @NotEmpty(message = "所拥有书籍不能为空")

   @ListNotHasNull(message = "List 中不能含有null元素")

   @Valid    private ListBook books;    //getter setter 方法.......}

使用方法同上,在在需要校验的Model上面加上@Valid 即可

分组验证

对同一个Model,我们在增加和修改时对参数的校验也是不一样的,这个时候我们就需要定义分组验证,步骤如下

1、定义两个空接口,分别代表Person对象的增加校验规则和修改校验规则

/**

* 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型新增时的参数校验规则 */public interface PersonAddView {

}/**

* 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型修改时的参数校验规则 */public interface PersonModifyView {

}

2、Model上添加注解时使用指明所述的分组

public class Person {    private long id;    /**

* 添加groups 属性,说明只在特定的验证规则里面起作用,不加则表示在使用Deafault规则时起作用     */

@NotNull(groups = {PersonAddView.class, PersonModifyView.class}, message = "添加、修改用户时名字不能为空", payload = ValidateErrorLevel.Info.class)

@ListNotHasNull.List({

@ListNotHasNull(groups = {PersonAddView.class}, message = "添加上Name不能为空"),

@ListNotHasNull(groups = {PersonModifyView.class}, message = "修改时Name不能为空")})    private String name;

@NotNull(groups = {PersonAddView.class}, message = "添加用户时地址不能为空")    private String address;

@Min(value = 18, groups = {PersonAddView.class}, message = "姓名不能低于18岁")

@Max(value = 30, groups = {PersonModifyView.class}, message = "姓名不能超过30岁")    private int age;  //getter setter 方法......}

3、启用校验

此时启用校验和之前的不同,需要指明启用哪一组规则

/**

* 添加一个Person对象

* 此处启用PersonAddView 这个验证规则

* 备注:此处@Validated(PersonAddView.class) 表示使用PersonAndView这套校验规则,若使用@Valid 则表示使用默认校验规则,

* 若两个规则同时加上去,则只有第一套起作用     */

@RequestMapping(value = "/person", method = RequestMethod.POST)    public void addPerson(@RequestBody @Validated({PersonAddView.class, Default.class}) Person person) {

System.out.println(person.toString());

}    /**

* 修改Person对象

* 此处启用PersonModifyView 这个验证规则     */

@RequestMapping(value = "/person", method = RequestMethod.PUT)    public void modifyPerson(@RequestBody @Validated(value = {PersonModifyView.class}) Person person) {

System.out.println(person.toString());

}

Spring validator 方法级别的校验

JSR和Hibernate validator的校验只能对Object的属性进行校验,不能对单个的参数进行校验,spring 在此基础上进行了扩展,添加了MethodValidationPostProcessor拦截器,可以实现对方法参数的校验,实现如下:

1、实例化MethodValidationPostProcessor

@Bean    public MethodValidationPostProcessor methodValidationPostProcessor() {        return new MethodValidationPostProcessor();

}

2、在所要实现方法参数校验的类上面添加@Validated,如下

@RestController

@Validatedpublic class ValidateController {

}

3、在方法上面添加校验规则:

@RequestMapping(value = "/test", method = RequestMethod.GET)    public String paramCheck(@Length(min = 10) @RequestParam String name) {

System.out.println(name);        return null;

}

当方法上面的参数校验失败,spring 框架就回抛出异常

{  "timestamp": 1476108200558,  "status": 500,  "error": "Internal Server Error",  "exception": "javax.validation.ConstraintViolationException",  "message": "No message available",  "path": "/test"}

如何自定义JSR-303标准的validator

import java.lang.annotation.Documented;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

import javax.validation.Constraint;

import javax.validation.Payload;

import javax.validation.constraints.Pattern;

@Pattern.List({

  @Pattern(

      regexp = "[a-z0-9!#$%’*+/=?^_‘{|}~-]+(?:\\.[a-z0-9!#$%’*+/=?^_‘{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?") })

@Constraint(validatedBy = {})

@Documented

@Target({

    ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR,

    ElementType.PARAMETER })

@Retention(RetentionPolicy.RUNTIME)

public @interface Email {

  String message() default "{invalid.email}";

  Class?[] groups() default {};

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

  @Documented

  @Target({

      ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR,

      ElementType.PARAMETER })

  @Retention(RetentionPolicy.RUNTIME)

  @interface List {

    Email[] value();

  }

}

public interface PwdGroup {

}

import java.util.Set;

import javax.validation.ConstraintViolation;

import javax.validation.Validation;

import javax.validation.Validator;

import javax.validation.ValidatorFactory;

import javax.validation.constraints.Max;

import javax.validation.constraints.Min;

import javax.validation.constraints.NotNull;

import javax.validation.constraints.Size;

import javax.validation.groups.Default;

public class User {

  @Min(1)

  private int userId;

  @NotNull(message = "用户名不能为空!")

  @Size(min = 1, message = "用户名不能为空!")

  private String userName;

  @NotNull

  @Size(min = 6, max = 20, message = "密码长度必须在6-20之间!", groups = { PwdGroup.class })

  private String userPwd;

  @Min(value = 18, message = "{user.validation.age.Min.message}")

  @Max(value = 60, message = "年龄最大值必须是60")

  private int userAge;

  @Email

  private String email;

  public User(int userId, String userName, String userPwd, int userAge,

      String email) {

    super();

    this.userName = userName;

    this.userPwd = userPwd;

    this.userAge = userAge;

    this.email = email;

  }

  public String getEmail() {

    return email;

  }

  public void setEmail(String email) {

    this.email = email;

  }

  public static void main(String[] args) {

    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();

    Validator validator = factory.getValidator();

    User user = new User(1, "a", "b", 10, "1222");

    SetConstraintViolationUser constraintViolations = validator

        .validate(user);

    for (ConstraintViolationUser constraintViolation : constraintViolations) {

      System.out.println(constraintViolation.getPropertyPath() + " "

          + constraintViolation.getMessage());

    }

    System.out.println();

    constraintViolations = validator.validate(user, PwdGroup.class);

    for (ConstraintViolationUser constraintViolation : constraintViolations) {

      System.out.println(constraintViolation.getPropertyPath() + " "

          + constraintViolation.getMessage());

    }

    System.out.println();

    constraintViolations = validator.validate(user, Default.class,

        PwdGroup.class);

    for (ConstraintViolationUser constraintViolation : constraintViolations) {

      System.out.println(constraintViolation.getPropertyPath() + " "

          + constraintViolation.getMessage());

    }

  }

}

spring mvc为什么不能返回基本数据类型

们都是表现层框架,都是基于 MVC 模型编写的。

它们的底层都离不开原始 ServletAPI。

它们处理请求的机制都是一个核心控制器。

区别:

Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter。

Spring MVC 是基于方法设计的,而 Struts2 是基于类,Struts2 每次执行都会创建一个动作类。所以 Spring MVC 会稍微比 Struts2 快些。

Spring MVC 使用更加简洁,同时还支持 JSR303, 处理 ajax 的请求更方便。

(JSR303 是一套 JavaBean 参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们 JavaBean 的属性上面,就可以在需要校验的时候进行校验了。)

Struts2 的 OGNL 表达式使页面的开发效率相比 Spring MVC 更高些,但执行效率并没有比 JSTL 提升,尤其是 struts2 的表单标签,远没有 html 执行效率高。

2.SpringMVC不同返回值类型的响应

准备工作:在webapp下创建一个response.jsp页面

在WEB-INF/pages目录下创建一个success.jsp页面

创建一个User类(username,password,age三个属性)

创建一个UserController

2.1.返回值是String类型的响应

controller 方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。

使用这种方法会通过视图解析器解析出对应地址:WEB-INF/pages/xxx.jsp

2.1.1.在response.jsp页面写一个超链接

a href="user/testString" testString/a

2.1.2.在UserController写返回值是String类型的方法

复制代码

//指定逻辑视图名,经过视图解析器解析为 jsp 物理路径:/WEB-INF/pages/success.jsp

@RequestMapping("/testString")

public String testString(Model model){

System.out.println("testString方法执行了...");

// 模拟从数据库中查询出User对象

User user = new User();

user.setUsername("美美");

user.setPassword("123");

user.setAge(30);

// model对象

model.addAttribute("user",user);

return "success";

}

复制代码

2.1.3.在success.jsp页面通过el表达式取值

h3执行成功/h3

${user.username}

${user.password}

2.2.返回值是void类型的响应

2.2.1.在response.jsp页面写一个超链接

a href="user/testVoid" testVoid/a

2.2.2.在UserController写返回值是void类型的方法

因为是void类型,不能像上一个String类型一样直接返回一个字符串(一个jsp页面),所以需要用到servlet的原始API,在Controller方法的形参上定义