1010cc时时彩标准版 > 三分时时彩1010CC > 数量校验,参数验证

原标题:数量校验,参数验证

浏览次数:135 时间:2019-09-23

Spring Validation验证框架对参数的证实机制提供了@Validated(Spring's JS昂Cora-303 标准,是专门的学问 JSEscort-303 的三个变种),javax提供了@Valid(标准JS中华V-303标准),同盟 BindingResult 能够直接提供参数验证结果。个中对于字段的一定验证注脚比方 @NotNull 等网络到处都有,这里不详述

都会将破产的音讯填充到 BinddingResult 对象中。

待验证的JavaBean

SpringMVC——类型转变和格式化、数据校验、客户端展示错误音讯,springmvc校验

在介绍类型调换和格式化以前,小编首先来介绍 <mvc:annotation-driven />。

亟需导入的 schema:

xmlns:mvc="http://www.springframework.org/schema/mvc"

一、作用:

1.会自动注册 RequestMappingHandlerMapping、RequestMappingHandlerAdapter 以及 ExceptionHandlerExceptionResolver 八个 Bean。

若配置该表明后,对于一般的 springmvc 须要来说,不再接纳未安插以前的逾期的 AnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter。

而是使用 RequestMappingHandlerMapping、RequestMappingHandlerAdatapter,作为对 AnnotationHandlerMapping 和 AnnotationHandler艾达pter 的一种代替。。

由此 DispatcherServlet 中的 HandlerAdapter 的 handler() 方法爆发了改换,相当于一套新的逻辑。

明晰内容,请参见:

AnnotationMethodHandlerAdapter 下的 springmvc 运转流程剖判。

RequestMappingHandlerAdapter 下的 springmvc 运转流程剖析。

  1. 匡助使用 ConversionService实例对表单参数举办类型转变。详细内容请参见:类型调换和格式化

3.协助使用 @Valid 对 java bean 举办 JSEvoque-303 校验。详细内容请参见:数据校验

4.帮忙使用 @RequestBody 和 @ResponseBody 评释。详细内容请参见:springmvc 对 Ajax 的辅助。

二、详细分析

添加 <mvc:annotation-driven /> 配置后:

暗许意况下:

HandlerMapping 注册了 RequestMappingHandlerMapping 和 BeanNameUrlHandlerMapping。

HandlerAdapter 注册了 RequestMappingHandlerAdapter 和 HttpReqestHandlerAdapter 和 SimpleControllerHandlerAdapter。

 

org.springframework.beans.factory.xml.BeanDefinitionParser 用来解析<beans/> 标签的。

<mvc:annotation-driven /> 属于 <beans/> 的子节点,由 org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser进行深入分析。

其一类注册了上面七个 HandlerMapping:RequestMappingHandlerMapping 和 BeanNameUrlHandlerMapping。

别的,也得以透过 <mvc:resources mapping="" location=""/>或 <mvc:view-controller path=""/> 来钦点必要被注册的 HandlerMapping。

还要登记了三个Handler艾达tper:RequestMappingHandler艾达pter,HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter。

并且登记了多个HandlerExceptionResolver:ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver和DefaultHandlerExceptionResolver。

 

RequestMappingHandlerAdapter 和 ExceptionHandlerExceptionResolver 作为贰个暗中同意的布置,由下列实例钦定:

ContentNegotiationManager

DefaultFormattingConversionService

LocalValidatorFactoryBean——支持 JSR303

HttpMessageConverter

 

并在 org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser#parse 这里打开的挂号。

再就是开掘 RequestMappingHandlerMapping 的 order 为 0,BeanNameUrlHandlerMapping 的 order 为2。所以恳请先去映射 RequestMappingHandlerMapping。

 

地点介绍了 <mvc:annotation-driven /> 。上面介绍类型转变和格式化、数据校验。

一、在介绍每种模块在此之前,首先对总体流程有个显著的认知。

SpringMVC 通过反射机制对目的措施的签字举办剖判,将呼吁音讯绑定到拍卖方法入参中。

SpringMVC 将 ServletReqeust 对象以及管理方法入参对象实例传递给 DataBinder,DataBinder 调用装配在 springmvc 上下文的 ConversionService举办类型转变和格式化,

将央求消息填充到入参对象中,然后调用 Validator 组件对已入参的目的开展多少合法性校验,并生成数据绑定结果 BindingResult 对象,若数据转变或数额格式化失利,或申明战败,

都会将倒闭的新闻填充到 BinddingResult 对象中。

二、SpringMVC 的类型转换和格式化:

ConversionService 是 spring 类型调换体系的着力接口。位于 org.springframework.core.convert 包下,能够选拔 org.springframework.context.support.ConversionServiceFactoryBean 

在 springmvc 上下文中配置叁个 ConversionService 的实例。SpringMVC 配置文件会自动识别上下文中的 Conversion瑟维斯,并在类型调换的时候利用它。

使用<mvc:annotation-driven /> 注解后:

默许会注册贰个 ConversionService ,即 FromattingConversionServiceFactoryBean, 生产的 ConversionService能够开展项目(日期和数字)的格式化。

若增添自定义类型转变器后,要求对其装配自定义的 ConversionService 。如:

<bean id="customizeConversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="com.nucsoft.springmvc.converter.PersonConversionService"/>
            <bean class="com.nucsoft.springmvc.converter.String2MapConversionService"/>
        </set>
    </property>
</bean>
<mvc:annotation-driven conversion-service="customizeConversionService"/>

此处装配的自定义 ConversionService 为 customerConversion瑟维斯, 是由 Conversion瑟维斯FactoryBean 生产的。

此间会有贰个主题素材,由 ConversionServiceFactoryBean 生产的 ConversionService会覆盖默许的 FormattingConversionServiceFactoryBean,会形成品种(日期和数字)的给石油化学工业出错。

Spring 中定义了二个 ConversionService 达成类 FormattingConversionService,该类扩大于 GenericConversionService ,它既有类型调换也可能有格式化的成效。

FormattingConversion瑟维斯 也富有一个FormattingConversionServiceFactoryBean 。通过在 Spring 上下文中布局三个 FormattingConversionService,

既可以够登记自定义类型调换器,也能够登记自定义声明格式化。NumberFormatAnnotationFormatterFactoryBean、JodaDateTimeFormatAnnotationFormatFactory 

会自定注册到 FormattingConversion瑟维斯FactoryBean 中。由此配置 FormattingConversionServiceFactoryBean 后,能很好的缓慢解决上述难题。如:

<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean" id="FormattingConversionService">
    <property name="converters">
        <set>
            <bean class="com.nucsoft.springmvc.converter.PersonConverster"/>
            <bean class="com.nucsoft.springmvc.converter.String2MapConverter"/>
        </set>
    </property>
</bean>
<mvc:annotation-driven conversion-service="FormattingConversionService"/>

来看一个切实可行的转变器:

/**
 * @author solverpeng
 * @create 2016-08-15-14:50
 */
public class PersonConverter implements Converter<String, Person>{
    Person person = null;
    @Override
    public Person convert(String s) {
        try {
            if(s != null && s.length() > 0) {
                String[] strings = s.split("\|");
                person = Person.class.newInstance();
                for(String str : strings) {
                    String[] properties = str.split(":");
                    Field field = Person.class.getDeclaredField(properties[0]);
                    field.setAccessible(true);
                    Class<?> type = field.getType();
                    if(type.equals(Integer.class)) {
                        field.set(person, Integer.parseInt(properties[1]));
                        continue;
                    }
                    field.set(person, properties[1]);
                }
            }
        } catch(InstantiationException | IllegalAccessException | NoSuchFieldException e) {
            e.printStackTrace();
        }
        return person;
    }
}

能够看看,具体消除转变难题的是:三个个的 Converter。

在 core.convert.support 包下提供了许多私下认可的品种转化器,为类型转变提供和巨大的实惠。

这个项目调换器纵然带有了绝大大多常用类型的转变,不过有的时候大家略微相当要求,就须要自定义类型转变器。

1.自定义类型调换器:

(1)实现 Converter 接口

package org.springframework.core.convert.converter;
public interface Converter<S, T> {
      T convert(S source);
}

成立自定义类型调换器,只须要贯彻该接口,参数 S 表示须求改变的品类,T 代表转变后的类型。对于每一遍调用 convert() 方法,必得有限支撑参数 source 不可能为 null。

一旦调换败北,也许会抛出特别。特别的,多个 IllegalArgumentException 会被抛出来指明无效的 source 值。

请留神,须求保险调换器是线程安全的。

e1: 须求将 person=name:lily|age:23 调换为对应的 person 对象

自定义的连串转变器:

/**
 * @author solverpeng
 * @create 2016-08-15-14:50
 */
public class PersonConverter implements Converter<String, Person>{
    Person person = null;
    @Override
    public Person convert(String s) {
        try {
            if(s != null && s.length() > 0) {
                String[] strings = s.split("\|");
                person = Person.class.newInstance();
                for(String str : strings) {
                    String[] properties = str.split(":");
                    Field field = Person.class.getDeclaredField(properties[0]);
                    field.setAccessible(true);
                    Class<?> type = field.getType();
                    if(type.equals(Integer.class)) {
                        field.set(person, Integer.parseInt(properties[1]));
                        continue;
                    }
                    field.set(person, properties[1]);
                }
            }
        } catch(InstantiationException | IllegalAccessException | NoSuchFieldException e) {
            e.printStackTrace();
        }
        return person;
    }
}

在 SpringMVC 配置文件中增加如下配置:

<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean" id="FormattingConversionService">
    <property name="converters">
        <set>
            <bean class="com.nucsoft.springmvc.converter.PersonConverter"/>
        </set>
    </property>
</bean>
<mvc:annotation-driven conversion-service="FormattingConversionService"/>

请求:

<a href="testConverter?person=name:lily|age:23">test converter</a>

目标 handler 方法:

@RequestMapping("/testConverter")
public String testSpring2Person(Person person) {
    System.out.println("persont:"   person);
    return "success";
}

调节台出口:

persont:Person{name='lily', age=23} 

e2:在介绍参数获取难点是,对 @RequestParam 的 “若是措施的入参类型是一个Map,不包罗泛型类型,况兼呼吁参数名称是被钦命” 这种情形并没有进展详尽表达,这里通过三个事例表明。

将 String 转换为 Map,将 params=a:1|b:2 转换为 Map 类型。

自定义类型转变器:

/**
 * @author solverpeng
 * @create 2016-08-15-15:40
 */
public class String2MapConverter implements Converter<String, Map<String, Object>>{
    @Override
    public Map<String, Object> convert(String s) {
        Map<String, Object> map = new HashMap<>();
        if(s != null & s.length() > 0) {
            String[] strings = s.split("\|");
            for(String string : strings) {
                String[] split = string.split(":");
                map.put(split[0], split[1]);
            }
        }
        return map;
    }
}

在 SpringMVC 配置文件中增多如下配置:

<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean" id="FormattingConversionService">
    <property name="converters">
        <set>
            <bean class="com.nucsoft.springmvc.converter.String2MapConverter"/>
        </set>
    </property>
</bean>
<mvc:annotation-driven conversion-service="FormattingConversionService"/>

请求:

<a href="testConverter2?params=a:1|b:2">test converter2</a>

目标 handler 方法:

@RequestMapping("/testConverter2")
public String testString2Map(@RequestParam("params") Map map) {
    System.out.println(map);
    return "success";
}

决定台出口:

{b=2, a=1}

(2)实现 ConverterFactory 

package org.springframework.core.convert.converter;
public interface ConverterFactory<S, R> {
     <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

万一期望将一连串型调换为另一种档期的顺序及其子类对象时,那么使用这几个接口。

e: num=23&num2=33.33 将 num 转换为对应的 Integer 类型,将 num2 调换为相应的 Double 类型。

种类转变器:org.springframework.core.convert.support.StringToNumberConverterFactory

final class StringToNumberConverterFactory implements ConverterFactory<String, Number> {

    @Override
    public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToNumber<T>(targetType);
    }

    private static final class StringToNumber<T extends Number> implements Converter<String, T> {

        private final Class<T> targetType;

        public StringToNumber(Class<T> targetType) {
            this.targetType = targetType;
        }

        @Override
        public T convert(String source) {
            if (source.length() == 0) {
                return null;
            }
            return NumberUtils.parseNumber(source, this.targetType);
        }
    }

}

请求:

<a href="testString2Number?num=23&num2=33.33">test String to Number</a>

目标 handler 方法:

@RequestMapping("/testString2Number")
public String testString2Number(@RequestParam("num") Integer num, @RequestParam("num2") Double num2) {
    System.out.println("num:"   num);
    System.out.println("num2:"   num2);
    return "success";
}

调控台出口:

num:23
num2:33.33

(3)还恐怕有一种 GenericConverter ,这里不对其展开认证。有意思味的童鞋,可自动钻研,用到的景况很少。

2.格式化

那边所说的格式化,紧要指的是日期和数字的格式化。SpringMVC 帮忙选用@DateTimeFormat 和 @NumberFormat 来造成数据类型的格式化。看贰个事例。

/**
 * @author solverpeng
 * @create 2016-08-16-11:14
 */
public class Employee {
    private String empName;
    private String email;
    private Date birth;
    private Double salary;

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    @Email
    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Past
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    @NumberFormat(pattern = "#,###,###.##")
    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee{"  
                "  empName='"   empName   '''  
                ", email='"   email   '''  
                ", birth="   birth  
                ", salary="   salary  
                '}';
    }
}

请求:

<a href="testFormat?empName=lily&[email protected]&birth=1992-12-23&salary=1,234,567.89">test Format</a>

决定台出口:

employee:Employee{  empName='null', email='[email protected]', birth=Wed Dec 23 00:00:00 CST 1992, salary=1234567.89}

请求:

<a href="testFormat?empName=lily&[email protected]&birth=2992-12-23&salary=1,234,567.89">test Format</a>

垄断(monopoly)台出口:

allError:Field error in object 'employee' on field 'birth': rejected value [Sun Dec 23 00:00:00 CST 2992]; codes [Past.employee.birth,Past.birth,Past.java.util.Date,Past];

arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [employee.birth,birth]; arguments []; default message [birth]]; default message [内需是二个千古的事件]

employee:Employee{ empName='null', email='[email protected]', birth=Sun Dec 23 00:00:00 CST 2992, salary=1234567.89}

二、SpringMVC 使用 DataBinder 实行数量的绑定。在类型调换和格式化之后,会进展多少的绑定。

三、SpringMVC 的数据校验:

Spring 4.0 之后,帮助  Bean Validation 1.0(JSAMG ONE-303)和 Bean Validation 1.1(JSPRADO-349) 校验。相同的时间也支撑 Spring Validator 接口校验。

Spring 提供一个表明接口,你能够用它来证清热标。那几个 Validator 接口使用三个 Errors 对象来行事,验证器验证退步的时候向 Errors 对象填充验证退步消息。

1.用到 Spring Validator 接口校验

(1)二个大致对象的表明

实体类:

1010cc时时彩标准版 1/** * @author solverpeng * @create 2016-08-12-10:50 */ public class Person { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" "name='"

  • name ''' ", age=" age '}'; } } Person.java

创办验证器(即创制 Validator 的贯彻类)

/**
 * @author solverpeng
 * @create 2016-08-12-10:51
 */
public class PersonValidator implements Validator{
    /**
     * This Validator validates *just* for Person
     */
    @Override
    public boolean supports(Class<?> aClass) {
        return Person.class.equals(aClass);
    }

    @Override
    public void validate(Object o, Errors errors) {
        ValidationUtils.rejectIfEmpty(errors, "name", "name.empty", "人名不能为空.");
        Person person = (Person) o;
        if(person.getAge() < 0) {
            errors.rejectValue("age", "negativevalue", "年龄不能为负数.");
        } else if(person.getAge() > 110) {
            errors.rejectValue("age", "too.darn.old", "年龄不得超过110岁.");
        }
    }
}

使用:

/**
 * @author solverpeng
 * @create 2016-08-12-10:49
 */
@Controller
public class TargetHandler {

    @InitBinder
    public void initBinder(DataBinder binder) {
        binder.setValidator(new PersonValidator());
    }

    @RequestMapping("/testPersonValidator")
    public String testPersonValidator(@Valid Person person, BindingResult result) {
        if(result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            if(!CollectionUtils.isEmpty(allErrors)) {
                for(ObjectError allError : allErrors) {
                    System.out.println("error: "   allError.getDefaultMessage());
                }
            }
        }
        System.out.println(person);
        return "success";
    }

}

(2)一个叶影参差对象的辨证

实体类:

1010cc时时彩标准版 2/** * @author solverpeng * @create 2016-08-12-11:14 */ public class Customer { private String firstName; private String surname; private Address address; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } @Override public String toString() { return "Customer{" "firstName='" firstName ''' ", surname='" surname ''' ", address=" address '}'; } } Customer.java 1010cc时时彩标准版 3/** * @author solverpeng * @create 2016-08-12-11:15 */ public class Address { private String addressName; public String getAddressName() { return addressName; } public void setAddressName(String addressName) { this.addressName = addressName; } @Override public String toString() { return "Address{" "addressName='" addressName ''' '}'; } } Address.java

表明器类:

/**
 * @author solverpeng
 * @create 2016-08-12-11:16
 */
public class AddressValidator implements Validator{
    @Override
    public boolean supports(Class<?> clazz) {
        return Address.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "addressName", "field.required", "地址名称必须输入.");
    }
}

/**
 * @author solverpeng
 * @create 2016-08-12-11:18
 */
public class CustomerValidator implements Validator{
    private final Validator addressValidator;

    public CustomerValidator(Validator addressValidator) {
        if(addressValidator == null) {
            throw new IllegalArgumentException("the validator is required and must be null.");
        }
        if(!addressValidator.supports(Address.class)) {
            throw new IllegalArgumentException("the validator must be [Address] instance.");
        }

        this.addressValidator = addressValidator;
    }

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

    @Override
    public void validate(Object target, Errors errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required", "firstName 必须输入.");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required", "surname 必须输入.");
        Customer customer = (Customer) target;

        try {
            errors.pushNestedPath("address");
            ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
        } finally {
            errors.popNestedPath();
        }
    }
}

使用

/**
 * @author solverpeng
 * @create 2016-08-12-10:49
 */
@Controller
public class TargetHandler {

    @InitBinder
    public void initBinder(DataBinder binder) {
        binder.setValidator(new CustomerValidator(new AddressValidator()));
    }

    @RequestMapping("/testCustomerValidator")
    public String testCustomerValidator(@Valid Customer customer, BindingResult result) {
        if(result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            if(!CollectionUtils.isEmpty(allErrors)) {
                for(ObjectError error : allErrors) {
                    System.out.println("error: "   error.getDefaultMessage());
                }
            }
        }
        return "success";
    }

}

证实:对于复杂对象的话,成立验证器的时候,能够利用嵌套的办法

经过达成 Validator 接口的法门开创的验证器,不是透过配备吗,亦不是因而注明来调用,而是经过 @InitBinder 标记的艺术,进而去调每种验证器进行验证。

 

Spring 未有对 @Valid 增添某个属性来内定使用哪个验证器实行验证。所以上边这种场所会报分外。

/**
 * @author solverpeng
 * @create 2016-08-12-10:49
 */
@Controller
public class TargetHandler {

    @InitBinder
    public void initBinder(DataBinder binder) {
        binder.setValidator(new PersonValidator());
        binder.setValidator(new CustomerValidator(new AddressValidator()));
    }

    @RequestMapping("/testCustomerValidator")
    public String testCustomerValidator(@Valid Customer customer, BindingResult result) {
        if(result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            if(!CollectionUtils.isEmpty(allErrors)) {
                for(ObjectError error : allErrors) {
                    System.out.println("error: "   error.getDefaultMessage());
                }
            }
        }
        return "success";
    }

    @RequestMapping("/testPersonValidator")
    public String testPersonValidator(@Valid Person person, BindingResult result) {
        if(result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            if(!CollectionUtils.isEmpty(allErrors)) {
                for(ObjectError allError : allErrors) {
                    System.out.println("error: "   allError.getDefaultMessage());
                }
            }
        }
        System.out.println(person);
        return "success";
    }

}

何况钦点了七个验证器,不论是呼吁 testCustomerValidator,仍然央浼testPersonValidator 都会报相当,因为在 initBinder() 中设置的七个validator 是多少个且的涉及,只借使验证,必需同有的时候间满意这多少个 validator 的辨证法规。

 

暗中同意情状下,假使不对 @InitBinder 申明钦点 value 属性值,那么那个点子对每一种入参存在 model 参数的靶子 handler 方法起功用,在对象措施调用前,对 入参处的 model 试行校验。

假诺对 @InitBinder 申明钦命 value 属性值,那么它只会对相应的靶子 handler 方法的入参 model 推行校验.

如:

/**
 * @author solverpeng
 * @create 2016-08-12-10:49
 */
@Controller
public class TargetHandler {

    @InitBinder("customer")
    public void initBinder(DataBinder binder) {
        binder.setValidator(new CustomerValidator(new AddressValidator()));
    }

    @RequestMapping("/testCustomerValidator")
    public String testCustomerValidator(@Valid Customer customer, BindingResult result) {
        if(result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            if(!CollectionUtils.isEmpty(allErrors)) {
                for(ObjectError error : allErrors) {
                    System.out.println("error: "   error.getDefaultMessage());
                }
            }
        }
        return "success";
    }

    @RequestMapping("/test")
    public String test(@Valid Person person) {
        System.out.println(person);
        return "success";
    }

}

那般时,只会对 testCustomerValidator() 方法处的 customer 参数实施校验,而不会对 test() 方法处的 person 参数实行校验。

 

目的 handler 方法处的 BindingResult 类型的参数,它是 Errors 的子接口,验证失败未来的错误新闻会存放到 Erros 中,在指标措施处,验证出错的新闻能够透过 BindingResult 来收获。

急需专一的是,如若在指标措施处设有五个必要校验的靶子,要求在每一种对象后增加BindingResult 来收获验证战败的错误音讯,而不能够只经过多少个 BindingResult 来获得具备的错误新闻,即 BindignResult 必须紧挨着要表达的参数后。

 

2.运用 JS传祺-303 进行校验

JSXC90-303 校验是多少个数量校验标准,Spring 未有对其打开落到实处,所以采用的时候,须要加上贰个达成,这里增添 Hibernate Validator 作为 JS陆风X8-303 的实现.

亟待额外增加的 jar 包:

validation-api-1.1.0.CR1.jar
hibernate-validator-5.0.0.CR2.jar
hibernate-validator-annotation-processor-5.0.0.CR2.jar
jboss-logging-3.1.1.GA.jar
classmate-0.8.0.jar

在 Bean 的 getXxx() 或属性名上增添验证法规阐明.

(1)多少个简练对象的证实

实体类:

public class Person {
    private String name;
    private int age;

    @NotBlank(message = "人名不能为空")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Min(value = 10, message = "年龄最小为10.")
    @Max(value = 110, message = "年龄最大不得超过110.")
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{"  
                "name='"   name   '''  
                ", age="   age  
                '}';
    }
}

使用:

@RequestMapping("/testPersonValidator")
public String testPersonValidator(@Valid Person person, BindingResult result) {
    if(result.hasErrors()) {
        List<ObjectError> allErrors = result.getAllErrors();
        if(!CollectionUtils.isEmpty(allErrors)) {
            for(ObjectError allError : allErrors) {
                System.out.println("error: "   allError.getDefaultMessage());
            }
        }
    }
    System.out.println(person);
    return "success";
}

没有须求通过 @InitBinder 来钦定验证器,只供给在指标 handler 方法处对必要证实的指标标记 @Valid 评释。能够对二个属性标有七个表达法规的笺注。

(2)二个叶影参差对象的验证

实体:

public class Address {
    private String addressName;

    @NotBlank
    public String getAddressName() {
        return addressName;
    }

    public void setAddressName(String addressName) {
        this.addressName = addressName;
    }

    @Override
    public String toString() {
        return "Address{"  
                "addressName='"   addressName   '''  
                '}';
    }
}

public class Customer {
    private String firstName;
    private String surname;
    private Address address;

    @NotBlank(message = "firstName must be not null.")
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    @Valid
    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @NotBlank(message = "surname must be not null.")
    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    @Override
    public String toString() {
        return "Customer{"  
                "firstName='"   firstName   '''  
                ", surname='"   surname   '''  
                ", address="   address  
                '}';
    }
}

证实:对复杂对象的辨证和简易对象的求证使用方法同样,对内嵌对象的求证使用 @Valid 评释就行。

和兑现 Validator 接口不相同,在 Spring 中选拔 JSOdyssey-303 能够在多个 handler 中表达多少个例外的的 bean 。如:

@Controller
public class TargetHandler {

    @RequestMapping("/testCustomerValidator")
    public String testCustomerValidator(@Valid Customer customer, BindingResult result) {
        if(result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            if(!CollectionUtils.isEmpty(allErrors)) {
                for(ObjectError error : allErrors) {
                    System.out.println("error: "   error.getDefaultMessage());
                }
            }
        }
        return "success";
    }

    @RequestMapping("/testPersonValidator")
    public String testPersonValidator(@Valid Person person, BindingResult result) {
        if(result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            if(!CollectionUtils.isEmpty(allErrors)) {
                for(ObjectError allError : allErrors) {
                    System.out.println("error: "   allError.getDefaultMessage());
                }
            }
        }
        System.out.println(person);
        return "success";
    }

}

注明:不论是必要 testCustomerValidator 对 Customer 举行验证依旧诉求testPersonValidator 对 Person 实行验证,不会报非常

3.自定义表达准绳约束表明

(1)参照已经落到实处的认证准则。自定义的注脚准则表明供给使用 @Constraint 注明,同一时候钦命其性子 validatedBy 的值,即选用哪个验证器类进行校验。

(2)必得对验证器注解类增多七个天性:message、groups和payload。

@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = NumberValidator.class)
public @interface Number {
    String message() default "must be a number.";

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

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

}

概念表达器类,必得达成  public interface ConstraintValidator<A extends java.lang.annotation.Annotation, T> 这几个接口,如:

public class NumberValidator implements ConstraintValidator<Number, String> {
    private Number number;

    @Override
    public void initialize(Number number) {
        this.number = number;
    }

    @Override
    public boolean isValid(String str, ConstraintValidatorContext constraintValidatorContext) {
        try {
            Integer.parseInt(str);
            return true;
        } catch(NumberFormatException e) {
            return false;
        }
    }
}

能够看出,需求贯彻五个点子,initialize() 方法举办早先化,isValid() 方法开展表达,当中 A 为验证器表明类名,也是initialize() 方法的入参,T 为要验证的指标,也是 isValid() 方法的的率先个参数。

(3)对认证准绳评释进行分组

难点:通过验证接口的方式,可以为二个 bean 定义多少个验证器,能够适用于不一样的情景。那么使用 JSCRUISER-303 该怎样到达这种成效?

<1>定义分组接口(普通接口就行),用于标记分组

<2>对要申明的 bean 标明的注释举行分组。

public class Person {
    private String name;
    private int age;

    @NotBlank(message = "人名不能为空")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Min(value = 10, message = "年龄最小为10.", groups = First.class)
    @Max(value = 110, message = "年龄最大不得超过110.", groups = Second.class)
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{"  
                "name='"   name   '''  
                ", age="   age  
                '}';
    }
}

表明时钦命分组

@RequestMapping("/testPersonValidator")
public String testPersonValidator(@Validated({First.class}) Person person, BindingResult result) {
    if(result.hasErrors()) {
        List<ObjectError> allErrors = result.getAllErrors();
        if(!CollectionUtils.isEmpty(allErrors)) {
            for(ObjectError allError : allErrors) {
                System.out.println("error: "   allError.getDefaultMessage());
            }
        }
    }
    System.out.println(person);
    return "success";
}

说明:

在应用时,能够钦赐同期选拔四个分组,依据前后相继顺序进行求证,若首先个不经过,则不表明第二个。

为同八个 Bean 定义的三个验证器注脚名称无法再一次,若想加多同样的准则,须要自定义声明。

 

四、客商端错误音信的突显:

地方已经介绍过,不论是类型转变和格式化失利,还是多少校验失利,都会将错误新闻填充到 BindingResult 中,那么在顾客端怎么样赢得呢?

动用 SpringMVC 标签呈现错误音讯。当使用 springmvc 标签展现错误新闻时,SpringMVC 会查看 Web 上下文是还是不是装配了对应的国际化财富文件,在国际化能源文件中追寻对应的国际化音信。若无找到,则显得暗中同意的荒谬消息。

各类属性在数额绑定或数额校验产生错误的时候,都会变动叁个 田野先生Error 对象。当三个属性校验退步后,校验框架会为该属性生成4个消息代码。

如 User 的 password 属性申明了八个 @Pattern 注明,当不满意 @Pattern 定义的条条框框时,就能够发生以下4个错误代码。

Pattern.user.password
Pattern.password
Pattern.java.lang.String
Pattern

若类型转变或数额格式化出错时,或该有的参数官样文章时,或调用管理措施产生错误时,都会在含蓄模型中创立错误音讯。

错误代码前缀:

required:需求的参数不设有。如 @RequestParam("param1") 表明了三个入参,可是该参数空头支票。

typeMismatch: 在数额绑定期,发生数据类型不协作的难点。

methodInvocation: 调用管理办法时发出了不当。

 

注册国际化财富文件:

在SpringMVC配置文件中加多 ResourceBundleMessageSource 类型的 Bean ,同一时间内定其 id 为 messageSource。设置其国际化基名。如:

<bean class="org.springframework.context.support.ResourceBundleMessageSource" id="messageSource">
  <property name="basename" value="i18n"/>
</bean>

 

五、总结

本篇文章从 <mvc:annotation-driven /> 提起,重要表明了 SpringMVC 的类型转变、格式化以及数额校验。只是对用法开展了表明,未有越来越深档案的次序的心向往之。今后深切的时候再写作品来介绍吧。

 

六、外传

(1)

在 idea 下,不能对国际化能源文件中的粤语转为 Unicode,这里提供贰个普通话汉字 | ASCII | Unicode相互转变工具 在线工具:

(2)

@InitBinder 详解

注脚有 @InitBinder 评释的方法。能够对 WebDataBinder 对象开展初步化,WebDataBinder 是 DataBinder 的子类,用于实现表单属性对 Bean 属性的绑定。

@InitBinder 标记的法门不能够有再次回到值,参数一般为 WebDataBinder 。

如不自动绑定对象中的 name 值。

@InitBinder
public void initBinder(WebDataBinder dataBinder) {
    dataBinder.setDisAllowedField("name");
}

 

在介绍类型调换和格式化在此以前,作者首先来介绍 mvc:annotation-d...

2. 批注地点

@Validated:能够用在档案的次序、方法和情势参数上。不过不能够用在成员属性上

@Valid:能够用在措施、构造函数、方法参数和成员属性上

两岸是还是不是能用于成员属性上一直影响能或不可能提供嵌套验证的功力。

在 SpringMVC 配置文件中增添如下配置:

《Spring 验证、数据绑定和类型转变》那篇Spring官方Doc文书档案的翻译并不曾关联具体应用的内部情形,本篇结合Spring MVC表单数据上传那几个通用应用场景写一下小编的实施。

1. 分组

@Validated:提供了二个分组作用,能够在入参验证时,依据差别的分组接纳区别的证实机制,这么些英特网也许有资料,不详述。@Valid:作为专门的学问JS奥迪Q5-303标准,还从未接受分组的效应。

实体类:

  1. POST格局新扩大业务模型类,Spring MVC通过 @ModelAttribute 注明绑定接收参数,使用Spring Validator格局表明。
  2. PUT方式更新职业模型类,Spring MVC通过 @ModelAttribute 申明绑定接收参数,使用Spring Validator格局表明。
  3. PATCH格局更新工作模型类,Spring MVC通过 @ModelAttribute 证明绑定接收参数,使用Spring Validator情势评释。
  4. GET/POST name/value/pk参数更新职业模型类,Spring MVC 接收基本项目参数,使用JS君越 303 API手动验证。

在调查 Controller 的入参是不是符合标准时,使用 @Validated 或然 @Valid 在宗旨表明功用上从未有过太多差异。不过在分组、注明地点、嵌套验证等效果上四个天地之别:

 

Bean Validation约束保留属性

  • groups,用于分组验证,接济同二个待验证目的在分化验证场景下的例外验证法规
  • message,验证错误后的提醒音信
  • payload,验证负载

3. 嵌套验证

在相比两者嵌套验证时,先证实下何以叫做嵌套验证。举个例子大家今后有个实体叫做Item:

public class Item {    @NotNull(message = "id不能为空")    @Min(value = 1, message = "id必须为正整数")    private Long id;    @NotNull(message = "props不能为空")    @Size(min = 1, message = "至少要有一个属性")    private List<Prop> props;}

Item带有非常多天性,属性之中有品质id,属性值id,属性名和属性值,如下所示:

public class Prop {    @NotNull(message = "pid不能为空")    @Min(value = 1, message = "pid必须为正整数")    private Long pid;    @NotNull(message = "vid不能为空")    @Min(value = 1, message = "vid必须为正整数")    private Long vid;    @NotBlank(message = "pidName不能为空")    private String pidName;    @NotBlank(message = "vidName不能为空")    private String vidName;}

品质那一个实体也是有谈得来的求证机制,例如属性和属性值id不能为空,属性名和品质值无法为空等。

现行反革命我们有个 ItemController 接受四个Item的入参,想要对Item进行验证,如下所示:

@RestControllerpublic class ItemController {    @RequestMapping("/item/add")    public void addItem(@Validated Item item, BindingResult bindingResult) {        doSomething();    }}

在上图中,要是Item实体的props属性不额外加注释,独有@NotNull和@Size,无论入参选取@Validated如故@Valid验证,Spring Validation框架只会对Item的id和props做非空和数目验证,不会对props字段里的Prop实体实行字段验证,也等于@Validated和@Valid加在方法参数前,都不会活动对参数举行嵌套验证。也正是说假设传的List中有Prop的pid为空可能是负数,入参验证不会检验出来。

为了能够举行嵌套验证,必需手动在Item实体的props字段上显明提出那些字段里面包车型地铁实体也要开展认证。由于@Validated不可能用在成员属性上,然而@Valid能加在成员属性上,而且@Valid类表明上也证实了它援助嵌套验证功效,那么大家能够揣摸出:@Valid加在方法参数时并不可见自动进行嵌套验证,而是用在急需嵌套验证类的对应字段上,来协作方法参数上@Validated或@Valid来开展嵌套验证。

我们修改Item类如下所示:

public class Item {    @NotNull(message = "id不能为空")    @Min(value = 1, message = "id必须为正整数")    private Long id;    @Valid // 嵌套验证必须用@Valid    @NotNull(message = "props不能为空")    @Size(min = 1, message = "props至少要有一个自定义属性")    private List<Prop> props;}

接下来大家在ItemController的addItem函数上再选拔@Validated或许@Valid,就能够对Item的入参实行嵌套验证。此时Item里面的props假如含有Prop的对应字段为空的动静,Spring Validation框架就能够检查实验出来,bindingResult就能够记录相应的谬误。

小结一下 @Validated 和 @Valid 在嵌套验证功效上的分别:

@Validated: 用在措施入参上无法单独提供嵌套验证成效。不能够用在成员属性上,也无力回天唤起框架举行嵌套验证。能相称嵌套验证阐明@Valid举行嵌套验证。

@Valid: 用在形式入参上不能单独提供嵌套验证功用。能够用在成员属性上,提示验证框架举办嵌套验证。能相称嵌套验证申明@Valid举办嵌套验证。

除此以外,栈长已经收拾了 Java 连串基本知识点文章,关心Java工夫栈微信徒人号,在后台回恢复关贸总协定缔约国地位键字:java,就可以取得。

关注大伙儿号,天天推送一篇干货。

<a href="testConverter?person=name:lily|age:23">test converter</a>

Spring碰到布置

在Spring MVC配置文件中流入validator,在validator中流入全局验证器和验证新闻类。
局地validator在具体Controller类的DataBinder中加多就可以。

<!-- 打开Spring MVC注解驱动 -->
<mvc:annotation-driven validator="validator" />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
        <list>              
            <!--对应classpath下的messages文件夹下的messages.properties和messages_zh_CN.properties以及其它国际化文件-->
            <!--如果有多个消息国际化文件,在下面添加即可,value不需要添加文件扩展名-->
            <value>classpath:messages/messages</value>
            <value>classpath:org/hibernate/validator/ValidationMessages</value>
        </list>
    </property>
    <!-- 默认值为none, 使用java.utils.properties -->
    <property name="defaultEncoding" value="UTF-8" />
    <!-- 默认值为-1, 缓存永远不刷新 -->
    <property name="cacheSeconds" value="60" />
    <!-- 父类AbstractMessageSource该属性默认值为false -->
    <!-- 
    <property name="useCodeAsDefaultMessage" value="false" />
     -->
</bean>
<!-- mvc:annotation-driven默认会注入该类,但是如果需要定制,显式注入-->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <!-- 指定自定义的Spring MessageSource对象,用来解析验证消息,替换JSR-303默认的classpath根路径下的ValidationMessages.properties文件-->
    <property name="validationMessageSource" ref="messageSource" />
    <!-- 指定Validation实现类,若不指定,JSR-303按照默认机制查找,即在classpath中查找 -->
    <!-- 注意,这里并没有使用ref, 因为参数是类名称,而不是对象-->
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
</bean>    

arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [employee.birth,birth]; arguments []; default message [birth]]; default message [亟需是一个过去的事件]

GET/POST name/value/pk

这种方式是为了适应越来越高效的改观单个属性,上传表单时,参数为属性名(name),属性值(value),主键ID(pk)。

先是通过Java反射机制,为实体类赋值,然后手动调用javax.validation.validator进行验证。这里选用java规范validator的原委是,Spring validator并未提供分组验证的参数。

Spring validator的API为 validator.validate(Object obj, Errors errors),而java标准validator API为validator.validate(Object obj, Class class)。

不过在注入validator时,使用Spring配置文件中的LocalValidatorFactoryBean就可以。能够透过 @Autowired 注入,javax.validation.Validator validator;

@RequestMapping(value="/categorytype/updatefield", method=RequestMethod.POST, params={"name","value","pk"})
@ResponseBody
public RestfulResult updateCategoryTypeField(
        @RequestParam(value="name", required = true) String name,
        @RequestParam(value = "value", required = true) String value,
        @RequestParam(value = "pk", required = true) int pk,
        WebRequest webRequest){
    //验证
    try {
        Class<CategoryTypeDetail> categoryTypeDetailClass =(Class<CategoryTypeDetail>) Class.forName("com.vimisky.dms.entity.backend.CategoryTypeDetail");
        CategoryTypeDetail categoryTypeDetail = categoryTypeDetailClass.newInstance();
        Field field = categoryTypeDetailClass.getDeclaredField(name);
        field.setAccessible(true);
        field.set(categoryTypeDetail, value);
        categoryTypeDetail.setId(pk);

        Set<javax.validation.ConstraintViolation<CategoryTypeDetail>> constraintViolations = validator.validate(categoryTypeDetail, PatchEntity.class);
        if(constraintViolations.size()>0) {
            Iterator<javax.validation.ConstraintViolation<CategoryTypeDetail>> iterator = constraintViolations.iterator();
            StringBuffer errorString = new StringBuffer();
            while(iterator.hasNext()){
                javax.validation.ConstraintViolation<CategoryTypeDetail> constraintViolation = iterator.next();
                logger.warn(constraintViolation.getMessage());
                errorString.append(constraintViolation.getMessage());
            }
            return new RestfulResult(false, "表单提交数据错误:" errorString);
        }


    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        return new RestfulResult(false,"服务器后台出错");
    } catch (InstantiationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        return new RestfulResult(false,"服务器后台出错");
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        return new RestfulResult(false,"服务器后台出错");
    } catch (SecurityException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        return new RestfulResult(false,"服务器后台出错");
    } catch (NoSuchFieldException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        return new RestfulResult(false,"服务器后台出错");
    }

    OperationServiceResult osr = 
    backendCategoryService.updateCategoryTypePart(pk, name, value);
    return new RestfulResult(osr);

}

对象 handler 方法处的 BindingResult 类型的参数,它是 Errors 的子接口,验证退步之后的错误消息会贮存到 Erros 中,在对象措施处,验证出错的音讯方可经过 BindingResult 来获得。

本子包容性

  • JDK1.6、Spring 3、JSR-303、Hibernate Validaiton 4

尽恐怕合作,小编使用Spring 3.2.4 Hibernate Validation 5.1.3.Final,报错,回落为Hibernate Validation 4.2.0.Final,平常。

本篇重要利用JS讴歌RDX-303。

暗中同意处境下:

Java Bean Validation规范中,有3类验证申明。

  • 对象申明,证明在类定义上,值为指标验证器。
  • 字段/属性申明,能够声明在字段上,也能够注明在getter方法上。
  • 图注明,小编认为能够清楚为级联评释,待验证类中反复满含基本类型(String、Integer等)属性,还隐含类品种。使用级联申明来对认证实行解耦,验证只在本级实施,对象属性的表达交给对象自身的辨证准则试行。

本篇使用Field表明。

/**
 * @author solverpeng
 * @create 2016-08-16-11:14
 */
public class Employee {
    private String empName;
    private String email;
    private Date birth;
    private Double salary;

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    @Email
    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Past
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    @NumberFormat(pattern = "#,###,###.##")
    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee{"  
                "  empName='"   empName   '''  
                ", email='"   email   '''  
                ", birth="   birth  
                ", salary="   salary  
                '}';
    }
}

Bean Validation内嵌约束

注解 支持的数据类型 说明 版本
@AssertFalse Boolean, boolean 检查被注解的元素是否为false 1.0
@AssertTrue Boolean, boolean 检查被注解的元素是否为true 1.0
@DecimalMax BigDecimal, BigInteger, String, byte, short, int, long及封装对象,Number and CharSequence的子类型. 检查被注解的元素是否为数字,且小于等于注解内的值 1.0
@DecimalMin BigDecimal, BigInteger, String, byte, short, int, long及封装对象,Number and CharSequence的子类型. 检查被注解的元素是否为数字,且大于等于注解内的值 1.0
@Digits(integer=, fraction=) BigDecimal, BigInteger, String, byte, short, int, long及封装对象,Number and CharSequence的子类型. 检查被注解的元素是否为数字,且符合要求范围,integer参数表示整数位数,fraction表示小数位数. 1.0
@Future java.util.Date, java.util.Calendar, ReadablePartial和ReadableInstant的实现类;如果classpath中有joda time库,支持; 检查被注解的时间是否是将来时间 1.0
@Max BigDecimal, BigInteger, String, byte, short, int, long及封装对象,Number and CharSequence的子类型. 检查被注解的元素是否小于等于注解内的值 1.0
@Min BigDecimal, BigInteger, String, byte, short, int, long及封装对象,Number and CharSequence的子类型. 检查被注解的元素是否大于等于注解内的值 1.0
@NotNull 任何类型 检查被注解的元素是否不为null 1.0
@Null 任何类型 检查被注解的元素是否为null 1.0
@Past java.util.Date, java.util.Calendar, ReadablePartial和ReadableInstant的实现类;如果classpath中有joda time库,支持; 检查被注解的时间是否是过去时间 1.0
@Pattern(regex=, flag=) String, CharSequence的子类型. 检查被注解的元素是否符合正则表达式 1.0
@Size(min=, max=) String, Collection, Map, arrays以及CharSequence的子类型. 检查被注解元素的Size是否在min和max范围内 1.0
@Valid 任意非基本类型 对被注解元素对象执行验证。如果对象是集合类collection或者array,对集合类中的元素进行逐一验证。如果对象是map,则对value进行验证 1.0
<a href="testFormat?empName=lily&email=lily@bb.com&birth=1992-12-23&salary=1,234,567.89">test Format</a>

(转发请表明出处,多谢)

还要发掘 RequestMappingHandlerMapping 的 order 为 0,BeanNameUrlHandlerMapping 的 order 为2。所以恳请先去映射 RequestMappingHandlerMapping。

1010cc时时彩标准版,Spring中选拔Validation的两种情势:

  • Bean Validation1.0/1.1 (JS中华V-303/JS大切诺基-349),官方文书档案请查阅JSR303-spec,JSR303-Javadoc;JSR349-spec,JSR349-Javadoc
  • Spring Validation

上述八个均不提供验证达成,Bean Validation默许达成由Hibernate Validation(官方文书档案请查阅Hibernate Validation - Reference)提供。

/**
 * @author solverpeng
 * @create 2016-08-15-15:40
 */
public class String2MapConverter implements Converter<String, Map<String, Object>>{
    @Override
    public Map<String, Object> convert(String s) {
        Map<String, Object> map = new HashMap<>();
        if(s != null & s.length() > 0) {
            String[] strings = s.split("\|");
            for(String string : strings) {
                String[] split = string.split(":");
                map.put(split[0], split[1]);
            }
        }
        return map;
    }
}

POST ModelAttribute

Spring MVC使用WebDataBinder类来保管属性编辑器PropertyEditor和验证器validator等,用来进行多少绑定、数据类型转变和数目印证。借使供给在Spring MVC中扩充一些验证器,能够因而webDataBinder.addValidator(...)方法加入。本章中由于只利用Field/Property验证,Java Bean Validation就能够满意,无需再行新添类验证器。

利用Spring MVC的 @ModelAttribute 申明绑定表单数据到事情对象时,在头里扩展验证声明 @Valid (Java Bean Validation 典型) 也许 @Validated (Spring 特有)。

本篇使用了分组验证,而 @Valid 未有分组验证的参数,只好动用 @Validated 。在事情对象参数前边,要求紧跟叁个达成了Errors接口的靶子。在Spring MVC中,BindingResult接口为Errors子接口,由Spring MVC注入具体贯彻指标。

@RequestMapping(value="/categorytype", method=RequestMethod.POST)
@ResponseBody
public RestfulResult insertCategoryType(
        @Validated(value={NewEntity.class}) @ModelAttribute CategoryTypeDetail categoryTypeDetail,
        BindingResult bResult,
        HttpServletRequest webRequest){
    logger.debug("接收到的分类类型数据为:" categoryTypeDetail.toString());
    //在客户端的Request头中,增加Content-Type:x-www-form-urlencoded;charset=utf8;这里就能获取到,否则获取不到
    logger.debug(webRequest.getCharacterEncoding());
    try {
        webRequest.setCharacterEncoding("");
    } catch (UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    //补偿验证,避免用户上传ID
    categoryTypeDetail.setId(0);
    if(bResult.hasErrors()){
        StringBuffer errorString = new StringBuffer();
        logger.error("分类类型数据新增接口接收表单数据出现错误:");
        //对前端而言,不需要显示全部错误
        for (ObjectError objectError : bResult.getAllErrors()) {
            logger.error(objectError.getObjectName() "赋值验证出错:" objectError.getDefaultMessage() ",错误代码为:" objectError.getCode());
        }
        for (FieldError fieldError : bResult.getFieldErrors()){
            logger.error(fieldError.getObjectName() "的" fieldError.getField() "字段赋值验证出错:" fieldError.getDefaultMessage() ",错误代码为:" fieldError.getCode());               
            errorString.append(fieldError.getDefaultMessage() "rn");
        }
        return new RestfulResult(false, "表单提交数据错误:" errorString);
    }
    OperationServiceResult osr = this.backendCategoryService.insertCategoryType(categoryTypeDetail);
    return new RestfulResult(osr);
}

(2)五个目眩神摇对象的证实

类定义

public class CategoryTypeDetail {
    @NotNull(groups={UpdateEntity.class},message="{com.vimisky.dms.backend.restful.id.notnullerror}")
    @Min(groups={UpdateEntity.class,PatchEntity.class},value=1,message="{com.vimisky.dms.backend.restful.id.minerror}")
    @Max(groups={UpdateEntity.class,PatchEntity.class},value=10000000,message="{com.vimisky.dms.backend.restful.id.maxerror}")//本处验证不严谨
    private int id;

    @NotNull(groups={NewEntity.class}, message="{com.vimisky.dms.backend.restful.name.notnullerror}")
    @Size(groups={NewEntity.class,UpdateEntity.class,PatchEntity.class},min=1,max=255,message="{com.vimisky.dms.backend.restful.name.lengtherror}")
    private String name;
    @Size(groups={NewEntity.class,UpdateEntity.class,PatchEntity.class},min=0,max=255,message="{com.vimisky.dms.backend.restful.secondaryname.lengtherror}")
    private String secondaryName;
    //如果没有定义分组,而在应用validated时,指定了分组,则不会使用该条验证约束
    @Size(groups={NewEntity.class,UpdateEntity.class,PatchEntity.class},min=0,max=1024,message="{com.vimisky.dms.backend.restful.description.lengtherror}")
    private String description;

    @Size(groups={NewEntity.class,UpdateEntity.class,PatchEntity.class},min=0,max=255,message="{com.vimisky.dms.backend.restful.language.lengtherror}")
    private String language;
    @Size(groups={NewEntity.class,UpdateEntity.class,PatchEntity.class},min=0,max=255,message="{com.vimisky.dms.backend.restful.thumbnailurl.lengtherror}")
    private String thumbnailUrl;

    //如果没有注释不能为空,则为空时,不检验Size等
    @Size(groups={NewEntity.class,UpdateEntity.class,PatchEntity.class},min=1,max=255,message="{com.vimisky.dms.backend.restful.thumbnailuri.lengtherror}")
    private String thumbnailUri;

    @Size(groups={NewEntity.class,UpdateEntity.class,PatchEntity.class},min=0,max=255,message="{com.vimisky.dms.backend.restful.thumbnailicon.lengtherror}")
    private String thumbnailIcon;

    @Size(groups={NewEntity.class,UpdateEntity.class,PatchEntity.class},min=0,max=255,message="{com.vimisky.dms.backend.restful.code.lengtherror}")
    private String code;

    @Null(groups={NewEntity.class,UpdateEntity.class,PatchEntity.class}, message="{com.vimisky.dms.backend.restful.createtime.nullerror}")
    private Date createTime;

    @Null(groups={NewEntity.class,UpdateEntity.class,PatchEntity.class},message="{com.vimisky.dms.backend.restful.lastmodifytime.nullerror}")
    private Date lastModifyTime;

    public CategoryTypeDetail(){
        super();

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSecondaryName() {
        return secondaryName;
    }

    public void setSecondaryName(String secondaryName) {
        this.secondaryName = secondaryName;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public String getThumbnailUrl() {
        return thumbnailUrl;
    }

    public void setThumbnailUrl(String thumbnailUrl) {
        this.thumbnailUrl = thumbnailUrl;
    }

    public String getThumbnailUri() {
        return thumbnailUri;
    }

    public void setThumbnailUri(String thumbnailUri) {
        this.thumbnailUri = thumbnailUri;
    }

    public String getThumbnailIcon() {
        return thumbnailIcon;
    }

    public void setThumbnailIcon(String thumbnailIcon) {
        this.thumbnailIcon = thumbnailIcon;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    public Date getLastModifyTime() {
        return lastModifyTime;
    }

    public void setLastModifyTime(Date lastModifyTime) {
        this.lastModifyTime = lastModifyTime;
    }

    @Override
    public String toString() {
        return "CategoryTypeDetail [id="   id   ", name="   name
                  ", secondaryName="   secondaryName   ", description="
                  description   ", language="   language   ", thumbnailUrl="
                  thumbnailUrl   ", thumbnailUri="   thumbnailUri
                  ", thumbnailIcon="   thumbnailIcon   ", code="   code
                  ", createTime="   createTime   ", lastModifyTime="
                  lastModifyTime   "]";
    }
}

为支撑分组验证,新建八个接口

新建

public interface NewEntity {

}

有的更新

public interface PatchEntity {

}

全然更新

public interface UpdateEntity {

}

1010cc时时彩标准版 41010cc时时彩标准版 5

证明新闻

本篇使用的是Spring 的 LocalValidationFactoryBean对象,在该目的中注入了messageSource对象,使用国际化文件来依照客户Locale显示区别语种的新闻。

流入的落到实处MessageSource接口的现实类为org.springframework.context.support.ReloadableResourceBundleMessageSource,相比较于org.springframework.context.support.ResourceBundleMessageSource,能够安装刷新时间,在指定时间过期后,重新载入message配置文件,及时更新message字符串所表示的骨子里文本。

为message属性赋值国际化文件中的字符串,出席了EL表达式。在J2EE classpath中要求javax.el包。

五、总结

分组验证

使用约束的groups参数,类型为数组,数组中的成分类型为Class

二、详细深入分析

PATCH ModelAttribute

为支撑Method 为PATCH的HTTP Request,要求追加一个响应Method 为OPTIONS的接口。

@RequestMapping(value="/categorytype/{id}", method=RequestMethod.OPTIONS)
@ResponseBody
public RestfulResult updateCategoryTypeOPTIONS(@PathVariable int id){
    logger.info("响应HTTP OPTIONS Method");       
    return new RestfulResult(true);
}

除此以外,要求增添二个拦截器,修改HTTP Response报头,Accept-Method中追加PUT。(超过本篇范围,不再粘贴相关代码)

PATCH更新的认证法规见上面“待验证的JavaBean”,groups里带有PatchEntity.class的注释。

@RequestMapping(value="/categorytype/{id}", method=RequestMethod.PATCH)
@ResponseBody
public RestfulResult updateCategoryTypePart(
        @PathVariable int id, 
        @Validated(value={PatchEntity.class}) @ModelAttribute CategoryTypeDetail categoryTypeDetail,
        BindingResult bResult){
    logger.info("通过HTTP PATCH方法对分类类型进行部分升级");
    logger.debug("原始CategoryType为:" categoryTypeDetail.toString());
    if(bResult.hasErrors()){
        StringBuffer errorString = new StringBuffer();
        logger.error("分类类型部分数据更新接口接收表单数据出现错误:");
        //对前端而言,不需要显示全部错误
        for (ObjectError objectError : bResult.getAllErrors()) {
            logger.error(objectError.getObjectName() "赋值验证出错:" objectError.getDefaultMessage() ",错误代码为:" objectError.getCode());
        }
        for (FieldError fieldError : bResult.getFieldErrors()){
            logger.error(fieldError.getObjectName() "的" fieldError.getField() "字段赋值验证出错:" fieldError.getDefaultMessage() ",错误代码为:" fieldError.getCode());               
            errorString.append(fieldError.getDefaultMessage() "rn");
        }
        return new RestfulResult(false, "表单提交数据错误:" errorString);
    }
    //与完全更新不同,表单中无需提供ID,使用URL中的ID即可
    if(categoryTypeDetail.getId() == 0)
        categoryTypeDetail.setId(id);
    OperationServiceResult osr = backendCategoryService.updateCategoryTypePart(categoryTypeDetail);
    return new RestfulResult(osr);
}

<1>定义分组接口(普通接口就行),用于标志分组

PUT ModelAttribute

为援助Method 为PUT的HTTP Request,供给充实一个响应Method 为OPTIONS的接口。

@RequestMapping(value="/categorytype/{id}", method=RequestMethod.OPTIONS)
@ResponseBody
public RestfulResult updateCategoryTypeOPTIONS(@PathVariable int id){
    logger.info("响应HTTP OPTIONS Method");       
    return new RestfulResult(true);
}

别的,需求追加八个拦截器,修改HTTP Response报头,Accept-Method中扩大PUT。(超出本篇范围,不再粘贴相关代码)

PUT更新的申明准则见上边“待验证的JavaBean”,groups里包涵UpdateEntity.class的批注。

@RequestMapping(value="/categorytype/{ctid}", method=RequestMethod.PUT)
@ResponseBody
public RestfulResult updateCategoryType(
        @PathVariable int ctid, 
        @Validated(value={UpdateEntity.class}) @ModelAttribute CategoryTypeDetail categoryTypeDetail, 
        BindingResult bResult){
    logger.info("通过HTTP PUT方法对分类" categoryTypeDetail.getName() "进行全部升级");
    if(bResult.hasErrors()){
        StringBuffer errorString = new StringBuffer();
        logger.error("分类类型数据更新接口接收表单数据出现错误:");
        //对前端而言,不需要显示全部错误
        for (ObjectError objectError : bResult.getAllErrors()) {
            logger.error(objectError.getObjectName() "赋值验证出错:" objectError.getDefaultMessage() ",错误代码为:" objectError.getCode());
        }
        for (FieldError fieldError : bResult.getFieldErrors()){
            logger.error(fieldError.getObjectName() "的" fieldError.getField() "字段赋值验证出错:" fieldError.getDefaultMessage() ",错误代码为:" fieldError.getCode());               
            errorString.append(fieldError.getDefaultMessage() "rn");
        }
        return new RestfulResult(false, "表单提交数据错误:" errorString);
    }       
    //该判断补充验证框架中的值验证
    if(categoryTypeDetail.getId() != ctid){
        logger.warn("URL资源ID与表单ID数据不一致");
        return new RestfulResult(false, "URL资源ID与表单ID数据不一致");           
    }
    OperationServiceResult osr = this.backendCategoryService.updateCategoryType(categoryTypeDetail);
    return new RestfulResult(osr);
}

添加 <mvc:annotation-driven /> 配置后:

写在后边

源码中的RestfulResult类和OperationServiceResult类是为前端服务而自定义的类,相比较轻易,与具象达成的事体相关,与本文毫无干系,所以不贴了。

有关Bean Validation自定义约束,在随后的博文中再单写。

至于Bean ValidationValidator接口自定义完毕类,在其后的博文中再单写。能够在Spring MVC中经过给databinder增多validator的方法加入。

至于Bean Validation和Spring Validation的框架选择上,使用本身深谙的就好,可是遭逢的 @Valid 申明加不了分组,Spring validator validate方法手工验证加不了分组这三种境况,就不得不动用其余一种了。

能够观望,需求完结多个措施,initialize() 方法进行伊始化,isValid() 方法开展表明,当中 A 为验证器申明类名,也是initialize() 方法的入参,T 为要表明的对象,也是 isValid() 方法的的率先个参数。

写在前面

RequestMappingHandlerAdapter 下的 springmvc 运营流程剖析。

可以见见,具体消除调换难题的是:四个个的 Converter。

@Controller
public class TargetHandler {

    @RequestMapping("/testCustomerValidator")
    public String testCustomerValidator(@Valid Customer customer, BindingResult result) {
        if(result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            if(!CollectionUtils.isEmpty(allErrors)) {
                for(ObjectError error : allErrors) {
                    System.out.println("error: "   error.getDefaultMessage());
                }
            }
        }
        return "success";
    }

    @RequestMapping("/testPersonValidator")
    public String testPersonValidator(@Valid Person person, BindingResult result) {
        if(result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            if(!CollectionUtils.isEmpty(allErrors)) {
                for(ObjectError allError : allErrors) {
                    System.out.println("error: "   allError.getDefaultMessage());
                }
            }
        }
        System.out.println(person);
        return "success";
    }

}

如:

二、SpringMVC 使用 DataBinder 进行多少的绑定。在类型调换和格式化之后,会进展数据的绑定。

1.会自动注册 RequestMappingHandlerMapping、RequestMappingHandlerAdapter 以及 ExceptionHandlerExceptionResolver 七个 Bean。

创设自定义类型转换器,只须求达成该接口,参数 S 表示需求改换的品种,T 代表转变后的品种。对于每趟调用 convert() 方法,必得保证参数 source 不能够为 null。

六、外传

<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean" id="FormattingConversionService">
    <property name="converters">
        <set>
            <bean class="com.nucsoft.springmvc.converter.PersonConverster"/>
            <bean class="com.nucsoft.springmvc.converter.String2MapConverter"/>
        </set>
    </property>
</bean>
<mvc:annotation-driven conversion-service="FormattingConversionService"/>
/**
 * @author solverpeng
 * @create 2016-08-12-10:49
 */
@Controller
public class TargetHandler {

    @InitBinder
    public void initBinder(DataBinder binder) {
        binder.setValidator(new PersonValidator());
        binder.setValidator(new CustomerValidator(new AddressValidator()));
    }

    @RequestMapping("/testCustomerValidator")
    public String testCustomerValidator(@Valid Customer customer, BindingResult result) {
        if(result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            if(!CollectionUtils.isEmpty(allErrors)) {
                for(ObjectError error : allErrors) {
                    System.out.println("error: "   error.getDefaultMessage());
                }
            }
        }
        return "success";
    }

    @RequestMapping("/testPersonValidator")
    public String testPersonValidator(@Valid Person person, BindingResult result) {
        if(result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            if(!CollectionUtils.isEmpty(allErrors)) {
                for(ObjectError allError : allErrors) {
                    System.out.println("error: "   allError.getDefaultMessage());
                }
            }
        }
        System.out.println(person);
        return "success";
    }

}

num:23
num2:33.33

<bean class="org.springframework.context.support.ResourceBundleMessageSource" id="messageSource">
  <property name="basename" value="i18n"/>
</bean>
@RequestMapping("/testPersonValidator")
public String testPersonValidator(@Validated({First.class}) Person person, BindingResult result) {
    if(result.hasErrors()) {
        List<ObjectError> allErrors = result.getAllErrors();
        if(!CollectionUtils.isEmpty(allErrors)) {
            for(ObjectError allError : allErrors) {
                System.out.println("error: "   allError.getDefaultMessage());
            }
        }
    }
    System.out.println(person);
    return "success";
}

这一个类注册了下边八个 HandlerMapping:RequestMappingHandlerMapping 和 BeanNameUrlHandlerMapping。

e2:在介绍参数获取难题是,对 @RequestParam 的 “假诺方式的入参类型是三个Map,不分包泛型类型,况兼呼吁参数名称是被钦定” 这种情状并未进展详尽表明,这里透过二个事例表明。

/**
 * @author solverpeng
 * @create 2016-08-12-11:15
 */
public class Address {
    private String addressName;

    public String getAddressName() {
        return addressName;
    }

    public void setAddressName(String addressName) {
        this.addressName = addressName;
    }

    @Override
    public String toString() {
        return "Address{"  
                "addressName='"   addressName   '''  
                '}';
    }
}

说明:

支配台出口:

在 core.convert.support 包下提供了大多默许的档案的次序转化器,为类型调换提供和高大的有利。

二、SpringMVC 的类型转变和格式化:

请稳重,需求确定保障转变器是线程安全的。

无需经过 @InitBinder 来钦点验证器,只必要在对象 handler 方法处对急需表达的靶子标明 @Valid 评释。能够对二个属性标有三个评释准则的注释。

应用 SpringMVC 标签显示错误新闻。当使用 springmvc 标签显示错误信息时,SpringMVC 会查看 Web 上下文是不是装配了相应的国际化财富文件,在国际化能源文件中探求对应的国际化音讯。若无找到,则显得私下认可的失实音信。

如此时,只会对 testCustomerValidator() 方法处的 customer 参数试行校验,而不会对 test() 方法处的 person 参数试行校验。

/**
 * @author solverpeng
 * @create 2016-08-12-10:51
 */
public class PersonValidator implements Validator{
    /**
     * This Validator validates *just* for Person
     */
    @Override
    public boolean supports(Class<?> aClass) {
        return Person.class.equals(aClass);
    }

    @Override
    public void validate(Object o, Errors errors) {
        ValidationUtils.rejectIfEmpty(errors, "name", "name.empty", "人名不能为空.");
        Person person = (Person) o;
        if(person.getAge() < 0) {
            errors.rejectValue("age", "negativevalue", "年龄不能为负数.");
        } else if(person.getAge() > 110) {
            errors.rejectValue("age", "too.darn.old", "年龄不得超过110岁.");
        }
    }
}
/**
 * @author solverpeng
 * @create 2016-08-12-11:16
 */
public class AddressValidator implements Validator{
    @Override
    public boolean supports(Class<?> clazz) {
        return Address.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "addressName", "field.required", "地址名称必须输入.");
    }
}

/**
 * @author solverpeng
 * @create 2016-08-12-11:18
 */
public class CustomerValidator implements Validator{
    private final Validator addressValidator;

    public CustomerValidator(Validator addressValidator) {
        if(addressValidator == null) {
            throw new IllegalArgumentException("the validator is required and must be null.");
        }
        if(!addressValidator.supports(Address.class)) {
            throw new IllegalArgumentException("the validator must be [Address] instance.");
        }

        this.addressValidator = addressValidator;
    }

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

    @Override
    public void validate(Object target, Errors errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required", "firstName 必须输入.");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required", "surname 必须输入.");
        Customer customer = (Customer) target;

        try {
            errors.pushNestedPath("address");
            ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
        } finally {
            errors.popNestedPath();
        }
    }
}

employee:Employee{ empName='null', email='lily@bb.com', birth=Sun Dec 23 00:00:00 CST 2992, salary=1234567.89}

若配置该注明后,对于一般的 springmvc 乞请来说,不再行使未配备此前的逾期的 AnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter。

Spring 未有对 @Valid 添加某些属性来内定使用哪个验证器举办求证。所以上边这种气象会报十分。

并且登记了八个HandlerExceptionResolver:ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver和DefaultHandlerExceptionResolver。

四、顾客端错误新闻的显得:

在介绍类型转变和格式化从前,小编首先来介绍 <mvc:annotation-driven />。

(1)三个简易对象的印证

HttpMessageConverter

将 String 转换为 Map,将 params=a:1|b:2 转换为 Map 类型。

这边所说的格式化,首要指的是日期和数字的格式化。SpringMVC 协理使用 @DateTimeFormat 和 @NumberFormat 来产生数据类型的格式化。看四个事例。

表达器类:

本文由1010cc时时彩标准版发布于三分时时彩1010CC,转载请注明出处:数量校验,参数验证

关键词:

上一篇:1010cc时时彩标准版:多租户的设计与实现,Iden

下一篇:没有了