`
Inmethetiger
  • 浏览: 108056 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Spring验证小结和问题

阅读更多

因为从云笔记中粘贴过来的,代码格式就没有了。格式稍好点的地址:Spring验证

目标:

  • Spring自带的验证方式
  • 基于JSR303的验证
  • 国际化显示错误信息

一:使用Spring自带的验证方式

       该种方式相对来说麻烦一点,需要为每个需要验证的实体类编写一个验证类,不过好处是可以处理多字段组合验证以及业务逻辑等复杂验证

 

1.1:定义实体类(User)

public class User {

 

    private String username;

 

    private String nickname;

 

    private String password;

 

   //省略getter /setter

}

 

1.2:定义实体类验证类(UserValidator)

需要继承Validator接口

public class UserValidator implements Validator {

 

    @Override

    public boolean supports(Class<?> clazz) {

        return User.class.equals(clazz); 

    }

 

  public void validate(Object target, Errors errors) {

       User user = (User)target;

        if(StringUtils.isEmpty(user.getUsername())){

            //1:使用这种方式,会报错(No message found under code 'empty.user.username.user.username' for locale 'zh_CN'.),因为rejectValue的第二个参数都是errorcode

            //errors.rejectValue("username","empty.user.username");

            //errors.rejectValue("username","用户名不能为空");

 

            //2:正确用法:rejectValue(String filed,String errorcode,Object[] errorArgs,String defaultMessage)

            errors.rejectValue("username","empty.user.username","用户名不能为空Default");

 

            //使用该种方式,不需要配置ResourceBundleMessageSource

           // ValidationUtils.rejectIfEmpty(errors,"username","empty.user.username","用户名不能为空");

        } else {

           int length =  user.getUsername().length();

            if(length<=3){

                errors.rejectValue("username","min.length.user.username","用户名长度不能小于3Default");

            }else if (length>=6){

                errors.rejectValue("username","max.length.user.username","用户名不能大于6Default");

            }

 

        }

        }

 

}

 

1.3:编写控制器代码:

 @RequestMapping(value = "validatorBySpring", method = RequestMethod.GET)

    public String validatorBySpringForm(@ModelAttribute("user") User user) {

        return "validator/validatorBySpring";

    }

 

    /**

     * 第一种,使用spring自带的validator

     */

 

    @RequestMapping(value = "validatorBySpring", method = RequestMethod.POST)

    public String validatorBySpring(@ModelAttribute("user") User user,

                                    BindingResult result) {

        userValidator.validate(user, result);

        if (result.hasErrors()) {

            return "validator/validatorBySpring";

        }

        return "validator/success";

    }

 

注意:在get方法中,需要加上@ModelAttribute,否则无法访问,或者可以抽出来,写一个以下方法:

@ModelAttribute(“user")

public void getUser(){

     return new User();

},这样的话,访问该控制器的时候都会调用该方法

 

 

1.4:前台

           <form:form action="${ctx}/validator/validatorBySpring" method="post" modelAttribute="user">

               <tr>

                   <td><form:label path="username" >Username</form:label></td>

                   <td><form:input path="username" ></form:input></td>

                   <td><form:errors path="username"></form:errors></td>

               </tr>

 

               <tr>

                   <td> <form:label path="password">password</form:label>  </td>

                   <td> <form:input path="password"></form:input>     </td>

                   <td><form:errors path="password"></form:errors> </td>

               </tr>

 

               <tr>

                   <td> <form:label path="email">email</form:label>  </td>

                   <td> <form:input path="email"></form:input>     </td>

                   <td><form:errors path="email"></form:errors> </td>

               </tr>

               <tr>

                   <td colspan="3">

                       <input type="submit" value="SUBMIT">

                   </td>

               </tr>

 

           </form:form>

       

 

1.5: 在上述代码已经可以完成Spring自带验证了,而且界面能正常显示错误信息,但是在实际过程中,错误信息采用的是硬编码,这是不可取的,所以,接下来需要国际化错误信息

    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">

        <property name="basename">

            <value>messages</value>

        </property>

    </bean>

 

messages_zh_CN.properties 在resources/下

empty.user.username=用户名不能为空

min.length.user.username=用户名长度不能小于3

max.length.user.username=用户名长度不能大于6

 

其中的errorcode可以随便写,只要在messages中对应上即可,在这种情况下,在实体验证类中,就可以使用第一种方式即:errors.rejectValue("username",”empty.user.username)。因为在message文件中有对应的errorcode,所以能正常显示

 

 

 

扩展:

1:不使用rejectValue,而是使用reject,界面该如何显示

eg:errors.reject(“empty.user.username”,”用户名不能为空");

直接在form中写如下:<form:errors />会显示全部错误

 

2:使用ValidationUtils

ValidationUtils.rejectIfEmpty(errors,"username","empty.user.username","用户名不能为空”);

 

 

 

 

 

 Errors主要使用方法:

 reject(String errorcode,Object[] errorArgs,String defaultMessage)

 

rejectValue(String filed,String errorcode,Object[] errorArgs,String defaultMessage)

 

BinderResult接口扩展了Erros接口,以便可以使用Spring的Validator对对象进行校验,同时获取绑定结果对象的信息。

 

二:使用基于jsr303的验证

在Spring3.x企业开发实战里面提到:通过binder.setValidator之后,Spring MVC将使用它对入参对象进行校验,将不再使用Spring框架装配的Validator对入参进行校验。换句话说:即使在入参上标注了@Valid注解,也不会再根据入参对象类中的jsr303注解进行校验了。

 

2.1:

1:使用jsr303注解,可以注解基于javax.validator或者基于hibernated 。之前觉得没有什么不同,但是今天发现还是有不同的

 

2.1.1使用之前的配置,不做任何修改,messages里面没有任何对应的errorcode

 

@NotEmpty

起作用了,提示:may not be empty ,为什么会显示这个会有解释

 

 @NotEmpty(message = "{username.not.empty}")

提示:{username.not.empty}

 

 

不起作用的原因是:NotNull和NotEmpty是不同滴。

 

@NotNull

并没有进行验证,而是直接通过,也就是验证没有起作用

 

@NotNull(message = "{username.not.empty}”)

仍然没有起作用

 

2.1.2:在资源文件中配置如下:

username.not.empty=用户名不能为空1

@NotEmpty

显示  may not be empty

 

 @NotEmpty(message = "{username.not.empty}")

提示:{username.not.empty} 。即,直接显示message里面的内容,而我要的是显示:用户名不能为空1

 

2.1.3:在资源文件配置如下:

username.not.empty=用户名不能为空1

NotNull.user.username=用户名不能为空

NotEmpty.user.username=用户名不能为空

 

 

    @NotEmpty 提示:用户名不能为空

 

    @NotEmpty(message = "{username.not.empty}”)   提示用户名不能为空,即:message里面的没有起作用,

 

去掉NotEmpty.user.username=用户名不能为空,则提示为{username.not.empty}

 

 

 

2.1.4:加上以上配置之后

<!-- 以下 validator  ConversionService 在使用 mvc:annotation-driven 会 自动注册-->

    <bean id="validator"

          class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">

        <property name="providerClass"  value="org.hibernate.validator.HibernateValidator"/>

        <!-- 如果不加默认到 使用classpath下的 ValidationMessages.properties -->

        <property name="validationMessageSource" ref="messageSource"/>

    </bean>

 

资源文件还是如下:

username.not.empty=用户名不能为空1

NotNull.user.username=用户名不能为空

NotEmpty.user.username=用户名不能为空

 

注意,上面的并没有在mvc:annoation-driver中注册

 

 

 @NotEmpty 提示用户名不能为空

 

@NotEmpty(message = "{username.not.empty}")提示用户名不能为空,即messages仍然没有起作用

 

去掉NotEmpty.user.username=用户名不能为空,则提示为{username.not.empty}

 

 

 

将<mvc:annotation-driven validator="validator" />

 

    @NotEmpty 提示用户名不能为空

 

@NotEmpty(message = "{username.not.empty}")提示用户名不能为空,即messages仍然没有起作用

 

去掉NotEmpty.user.username=用户名不能为空,则提示为:用户名不能为空1

 

总结:因为不知道为什么基于javax.validator的注解不起作用,所以字总结基于hibernate的注解。这里一NotEmpty来举例。

首先,如果一旦在实体上标识了该注解,则就已经起作用了。如果不配置错误信息,将使用默认的:在hibernate-validator.jar中的Resource中,有一个ValidatorMessages.properties。里面有错误主力的默认显示:org.hibernate.validator.constraints.NotEmpty.message=may not be empty,这也就是为什么只配置了@NotEmpty之后显示 may not be empty的原因。当配置了自己的资源文件后,因为没有配置validator,所以还是显示之前的错误。那为什么后面没有配置validator。为什么NotEmpty.user.usrname能显示而{username.not.empty}不能正常显示呢?这是因为FileError实现了MessageResourceResolvable接口,里面有自己的一套规则:

Annotation.entity.attribute。所以,我使用NotEmpty.user.username能显示中文,而使用{usrname.not.empty}却显示{username.not.empty}。在配置了validator之后,因为<mvc:annotaitonDriver 中没有指定validator,所以相当于没有配置。即:2.1.3的测试结果和2.1.4的测试结果一样。只有配置了 mvc:annotaiton-driver validator=“validator”之后,才能自定义错误格式,这种情况下才@NotEmpty{message={username.not.empty}}才能显示出来,不过NotEmpty.user.username这样格式的优先级比较高,两者放在一起,还是会显示后者。这就是,为什么在所有的一切都配置好之后,还是会显示“用户名不能为空”,只有在messages里面去掉之后才能显示username.not.empty的信息。

 

 

 

 

 

 

问题:

1:基于spring自带的validator前台不使用Spring form标签如何展示错误? 

2:使用ajax方式,怎样进行验证,和上面是同样的问题,也就是不使用表单

 

其中,基于jsr303的可以实现,可以参考springside里面的代码,但是自带的却不好实现,因为循环FiledError的时候,只能得到errorcode和defultMessage,得不到国际化信息

 

 

资料:

JSR 303 - Bean Validation 介绍及最佳实践

http://www.ibm.com/developerworks/cn/java/j-lo-jsr303/

 

开涛的博客:跟我学Spring mvc 3

http://jinnianshilongnian.iteye.com/blog/1617451

 

spring 3.x 企业开发实战

 

 

 

 

 

1
2
分享到:
评论
4 楼 Inmethetiger 2014-07-28  
yixiandave 写道

能不能在Validation注解的message里面写国际化信息的标识然后在处理过程中转化呢?就是在err.getDefaultMessage()后做一步处理

你的意思是使用errorCode,试了,不行,如果是复杂的处理的话不知道如何做!除非查看源码来看hibernate是如何实现的
3 楼 yixiandave 2014-07-28  
Inmethetiger 写道
yixiandave 写道
如果用JSR303的话,可以引入hibernate-validation包,然后直接在spring mvc的requestMapping方法参数中加入@Valid注解即可实现自动校验,如果要获取验证错误信息可以再注入一个Errors参数。错误信息都在这里面,然后用JSON形式返回前端即可(没用过spring的前端模板,刚接触spring mvc就是前后端分离的开发模式,所以第一个问题帮不了你)。

附上代码案例,因为我这里用了spring4.0的@RestController所以省略了一个@ResponseBody注解。如果是普通的@Controller需要在方法头部或者返回值类型前面加上,另外因为自己的项目图方便用了很多groovy所以可能代码会有点怪异,不过大体上应该能看懂。



我在最后面写了:其中,基于jsr303的可以解决以上两种问题,可以参考springside里面的代码,但是Spring自带的却不好实现,因为循环FiledError的时候,只能得到errorcode和defultMessage,得不到国际化信息

嗯,这种方法我知道,我现在主要的是考虑国际化,你的这个代码,errInfo.put("msg",err.getDefaultMessage())。只是拿到了默认的。

基于Jsr303确实能够得到国际化的信息,之前已经实现了,但是基于spring validator却无法实现,如果没有defaultMessage,则无法显示,如果有,则也只是显示defaultMessage,而不是显示messages属性文件中定义的错误信息,不过还是谢了


能不能在Validation注解的message里面写国际化信息的标识然后在处理过程中转化呢?就是在err.getDefaultMessage()后做一步处理
2 楼 Inmethetiger 2014-07-28  
yixiandave 写道
如果用JSR303的话,可以引入hibernate-validation包,然后直接在spring mvc的requestMapping方法参数中加入@Valid注解即可实现自动校验,如果要获取验证错误信息可以再注入一个Errors参数。错误信息都在这里面,然后用JSON形式返回前端即可(没用过spring的前端模板,刚接触spring mvc就是前后端分离的开发模式,所以第一个问题帮不了你)。

附上代码案例,因为我这里用了spring4.0的@RestController所以省略了一个@ResponseBody注解。如果是普通的@Controller需要在方法头部或者返回值类型前面加上,另外因为自己的项目图方便用了很多groovy所以可能代码会有点怪异,不过大体上应该能看懂。



我在最后面写了:其中,基于jsr303的可以解决以上两种问题,可以参考springside里面的代码,但是Spring自带的却不好实现,因为循环FiledError的时候,只能得到errorcode和defultMessage,得不到国际化信息

嗯,这种方法我知道,我现在主要的是考虑国际化,你的这个代码,errInfo.put("msg",err.getDefaultMessage())。只是拿到了默认的。

基于Jsr303确实能够得到国际化的信息,之前已经实现了,但是基于spring validator却无法实现,如果没有defaultMessage,则无法显示,如果有,则也只是显示defaultMessage,而不是显示messages属性文件中定义的错误信息,不过还是谢了
1 楼 yixiandave 2014-07-27  
如果用JSR303的话,可以引入hibernate-validation包,然后直接在spring mvc的requestMapping方法参数中加入@Valid注解即可实现自动校验,如果要获取验证错误信息可以再注入一个Errors参数。错误信息都在这里面,然后用JSON形式返回前端即可(没用过spring的前端模板,刚接触spring mvc就是前后端分离的开发模式,所以第一个问题帮不了你)。

附上代码案例,因为我这里用了spring4.0的@RestController所以省略了一个@ResponseBody注解。如果是普通的@Controller需要在方法头部或者返回值类型前面加上,另外因为自己的项目图方便用了很多groovy所以可能代码会有点怪异,不过大体上应该能看懂。
    @RequestMapping(value = "login", method = RequestMethod.POST)
    SessAccount doLogin(@Valid @RequestBody LoginInfo loginInfo, Errors errors, HttpServletRequest request) {
        ValidErrorHandleUtils.handleErrors(errors);
        String ip = IpAddressUtils.readRequestIp(request);
        loginInfo.setLoginIp(ip);
        SessAccount account = loginService.checkLogin(loginInfo);
        String key = accountCache.saveUser(account)
        account.setLoginKey(key)
        return account;
    }


errors对象的处理(我这里用了一个通用方法),JSONObject来自阿里的fastjson包
    public static void handleErrors(Errors errors) {
        List<JSONObject> errInfoList = new ArrayList<>();
        if (errors.hasErrors()) {
            for (ObjectError err : errors.getAllErrors()) {
                errInfoList.add(readErrorInfo(err));
            }
            throw new ParamInvalidException(JSON.toJSONString(errInfoList));
        }
    }

    private static JSONObject readErrorInfo(ObjectError err){
        JSONObject errInfo = new JSONObject();
        errInfo.put("field",err.getObjectName());
        errInfo.put("msg",err.getDefaultMessage());
        return errInfo;
    }


需要校验的对象定义
public class LoginInfo {
    @NotNull(message = "用户名不能为空")
    private String loginId;
    @NotNull(message = "密码不能为空")
    private String password;
    private String loginIp;

    public String getLoginId() {
        return loginId;
    }

    public void setLoginId(String loginId) {
        this.loginId = loginId;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPwdHash(IHash hashProvider){
        return hashProvider.hashString(password);
    }

    public String getLoginIp() {
        return loginIp;
    }

    public void setLoginIp(String loginIp) {
        this.loginIp = loginIp;
    }
}

相关推荐

    spring 验证总结

    Spring 基于注释的校验机制: 1) 支持Spring框架定义的Validator接口定义的校验。 2) 支持JSR303 Bean Validation定义的校验规范。 3) 比较复杂的验证和自定义验证方法

    Spring攻略(第二版 中文高清版).part1

    1.15 小结 56 第2章 高级Spring IoC容器 57 2.1 调用静态工厂方法创建Bean 57 2.1.1 问题 57 2.1.2 解决方案 57 2.1.3 工作原理 57 2.2 调用一个实例工厂方法创建Bean 58 2.2.1 问题 58 2.2.2 ...

    spring杂谈 作者zhang KaiTao

    1.5 SpringMVC + spring3.1.1 + hibernate4.1.0 集成及常见问题总结 1.6 »Spring 之AOP AspectJ切入点语法详解(最全了,不需要再去其他地找了) 1.7 Spring开闭原则的表现-BeanPostProcessor扩展点-2 1.8 Spring...

    Spring攻略(第二版 中文高清版).part2

    1.15 小结 56 第2章 高级Spring IoC容器 57 2.1 调用静态工厂方法创建Bean 57 2.1.1 问题 57 2.1.2 解决方案 57 2.1.3 工作原理 57 2.2 调用一个实例工厂方法创建Bean 58 2.2.1 问题 58 2.2.2 ...

    spring.net中文手册在线版

    Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序。它提供了很多方面的功能,比如依赖注入、面向方面编程(AOP)、数据访问抽象及ASP.NET扩展等等。Spring.NET以Java版的Spring框架为...

    spring3.0帮助文档(包含REST资料)

    Spring 3.0重要特性总结如下: ◆Spring表达式(SpEL):用于bean定义的核心表达式分析器 ◆对基于注释的组件的更多支持:允许通过元注释创建注释的“快捷方式” ◆标准化的依赖性注入注释:对Java中依赖性注入的...

    Spring开发指南

    Spring初探 准备工作 构建Spring基础代码 Spring 基础语义 Dependency Injection 依赖注入的几种实现类型 Type1 接口注入 Type2 设值注入 Type3 构造子注入 几种依赖注入模式的对比总结 Spring Bean封装...

    Spring AOP实验

    3、了解Spring中两种动态代理方式的区别; 4、掌握基于XML和注解的AspectJ开发。 二、 实验内容 1、按图所示的类图结构,设计接口及其实现类,并完成另外两附加要求:(1)日志功能:在程序执行期间追踪正在发生的...

    spring security 参考手册中文版

    使用Spring 4.0.x和Gradle 24 2.4.3项目模块 25 核心 - spring-security-core.jar 25 远程处理 - spring-security-remoting.jar 25 Web - spring-security-web.jar 25 配置 - spring-security-config.jar 26 LDAP - ...

    Spring.3.x企业应用开发实战(完整版).part2

    1.8 小结 第2章 快速入门 2.1 实例功能概述 2.1.1 比Hello World更适用的实例 2.1.2 实例功能简介 2.2 环境准备 2.2.1 创建库表 2.2.2 建立工程 2.2.3 类包及Spring配置文件规划 2.3 持久层 2.3.1 建立领域对象 ...

    搞定J2EE:STRUTS+SPRING+HIBERNATE整合详解与典型案例 (1)

    一共四个,其中pdf 三个包,源码一个包 第一章 J2EE快速入门 1.1 J2EE概述 1.1.1 J2EE的来源 1.1.2 J2EE整体框架 1.1.3 从J2EE到JavaEE 1.2 J2EE组件 1.2.1 客户端组件 1.2.2 Web组件 ...15.13 小结

    spring in action英文版

    第一部分 Spring基础  第1章 开始Spring之旅  1.1 为什么使用Spring  1.1.1 J2EE开发者的一天  1.1.2 Spring的承诺  1.2 Spring是什么 ... 1.3 开始Spring之旅 ... 1.6 Spring比较 ... 11.6 小结

    SpringSecurity3.1实际摸索总结

    文档包含各种重要命令参数的解析、具体的原理、具体的代码分析、实现资源也从数据库进行验证。在别人资料基础上做了的优化和总结。绝对物有所值。

    搞定J2EE:STRUTS+SPRING+HIBERNATE整合详解与典型案例 (3)

    一共四个,其中pdf 三个包,源码一个包 第一章 J2EE快速入门 1.1 J2EE概述 1.1.1 J2EE的来源 1.1.2 J2EE整体框架 1.1.3 从J2EE到JavaEE 1.2 J2EE组件 1.2.1 客户端组件 1.2.2 Web组件 ...15.13 小结

    基于spring的智慧型无人酒店系统.doc

    2.5小结 4 3 系统需求分析 5 3.1引言 5 3.2 功能性需求分析 5 3.3性能需求分析 6 3.4结果与讨论 6 3.5小结 6 4 系统详细设计 7 4.1引言 7 4.2系统功能模块 7 4.3数据库设计 8 4.3.1 数据库的逻辑设计 8 4.3.2 数据库...

    Spring in Action(第2版)中文版

    目录 第一部分spring的核心 第1章开始spring之旅 1.1spring是什么 1.2开始spring之旅 1.3理解依赖注入 ...1.5小结 ...2.4.2混合使用自动和手动装配 ...2.5.3初始化和销毁bean ...2.6小结 ...3.1声明父bean和子bean ...b.4小结

    Spring Security-3.0.1中文官方文档(翻译版)

    5.2.4. 小结 5.3. 验证 5.3.1. 什么是Spring Security 的验证呢? 5.3.2. 直接设置SecurityContextHolder 的内容 5.4. 在web 应用中验证 5.4.1. ExceptionTranslationFilter 5.4.2. ...

    Spring in Action(第二版 中文高清版).part2

    第一部分 Spring的核心 第1章 开始Spring之旅 1.1 Spring是什么 1.2 开始Spring之旅 1.3 理解依赖注入 1.3.1 依赖注入 1.3.2 DI应用 1.3.3 企业级应用中的依赖注入 1.4 应用AOP ...B.4 小结

    Spring in Action(第二版 中文高清版).part1

    第一部分 Spring的核心 第1章 开始Spring之旅 1.1 Spring是什么 1.2 开始Spring之旅 1.3 理解依赖注入 1.3.1 依赖注入 1.3.2 DI应用 1.3.3 企业级应用中的依赖注入 1.4 应用AOP ...B.4 小结

Global site tag (gtag.js) - Google Analytics