SpringMVC数据校验并通过国际化显示错误信息

SpringMVC数据校验并通过国际化显示错误信息

SpringMVC数据校验

<mvc:annotation-driven/>会默认装配好一个LocalValidatorFactoryBean,通过在处理方法的入参上标注@Valid 注解即可让Spring MVC在完成数据绑定后执行数据校验的工作。

首先,我们在实体类上标注JSR303校验注解

public class User extends BaseDomain{
    private int userId;

    //将属性值长度限定为2~10之间,但中文占两个字符
    @Length(min = 2, max = 10)
    private String userName;
    
    //匹配4~30个数字和字母以及下划线字符
    @Pattern(regexp="w{4, 30}")
    private String name;
    
    @Length(min = 6)
    private String password;  

注解规则有多个可以参考下表

JSR 303注解

image-20200224094944501

Hibernate Validator拓展注解

image-20200224095129519

接着我们要让springMVC使用这些规则对数据进行校验,下面将使用LoginController控制器进行演示。

@Controller
public class LoginController {

    @RequestMapping(value = "/login", produces="text/html;charset=UTF-8")
    public String login(
        @Valid @ModelAttribute("user") User user, 
        BindingResult result, HttpSession session){
        if(result.hasErrors()){
            return "forward:/loginPage";
        }else{
            session.setAttribute("user", user);
            return "home";
        }
    }

​ 在已经标注了JSR303注解的表单/命令对象前标注一个@Valid,Spring MVC框架在将请求数据绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验。

​ 在本例中我们对User对象标注@Valid注解。还有要记得标注@ModelAttribute,并显示的指定其value值为user,在显示错误信息时我们会用到它。

​ 还有一个问题就是校验所产生的校验结果保存在什么地方呢,如何传递给请求处理方法?Spring MVC是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存在其后的入参中,这个保存校验结果的入参必须是BindingResult 或Errors类型,这两个类都位于org.springframework.validation包中。

​ 而且需校验的表单/命令对象和其绑定结果对象或错误对象是成对出现的,它们之间不允许声明其他的入参。

在页面中显示错误信息

​ 由于表单/命令对象所对应的请求一般是从客户端的网页中传送过来的,如果发生了错误,我们必须通过网页显示出错误,提示用户更正错误。

<div class="login-container">

        <form:form modelAttribute="user" action="login" method="post">
            <div class="login-inner">
                <div class="login-inner-item login-operaType">
                    <div class="sign-in active">登录</div>
                    <div class="sign-up">注册</div>
                </div>
                <div class="login-inner-item">
                    <input type="text" class="layui-input" placeholder="账号" name="userName" value="${user.userName}">
                    <div class="message-error">
                        <form:errors path="userName" cssClass="message-error"/>
                    </div>
                </div>
                <div class="login-inner-item">
                    <input type="password" class="layui-input" placeholder="密码" name="password" value="${user.password}">
                    <div class="message-error">
                        <form:errors path="password" cssClass="message-error"/>
                    </div>
                </div>
                <div class="login-inner-item message-error">
                    ${errorMsg}
                </div>
                <div class="login-inner-link">
                    <a href="">忘记密码</a>
                </div>
                <div class="login-inner-item .login-inner-submit">
                    <input type="submit" class="layui-input" value="登录">
                </div>
            </div>
        </form:form>
    </div>

首先我们修改一下form标签,并指定模型数据为user,与前面标注的@ModelAttribute一致,如下

<form:form modelAttribute="user" action="login" method="post">
    ...
</form:form>

然后使用<form:errors />标签来显示错误信息,path用来指定实体类中的属性,cssClass指定css样式类

<form:errors path="userName" cssClass="message-error"/>

也可以使用如下标签显示所有的错误信息,但是不精确没必要

<form:errors path="*"/>

接下来我们测试一下效果,如下就能显示错误信息了

image-20200224102329275

​ 虽然能显示错误信息,但是这些错误信息是springMVC默认的,在实际运用中我们希望使用更加人性化的设置来显示错误信息。这时我们可以给校验注解添加message属性指定错误信息。

public class User extends BaseDomain{
    private int userId;

    @Length(min = 2, max = 10, message = "用户名不正确,长度必须在2~10之间")
    private String userName;

    @Length(min = 6, message = "密码长度不合法,长度必须大于6")
    private String password;

这样我们就能按照我们自己的想法去显示不同的错误信息,如下

image-20200224102844378

​ 虽然这样做成功了,能个性化的显示错误信息,但是这使用硬编码的方式依旧不推荐使用,我们希望在不修改源代码的情况下来修改这些错误信息,这时就可以使用国际化的方式来显示错误信息了。

通过国际化显示错误信息

当一个属性校验失败后,校验框架默认会为该属性生成4个消息代码,这些代码以校验注解类名前缀。例如User类中的password属性标准了一个@Length注解,当该属性值不满足@pattern所定义的规则时,就会产生一下4个错误代码:

Length.user.password
Length.password
Length.java.lang.String
Length

这里我们选择最精确的一个Length.user.password

​ 我们在类路径下添加一个文件夹i18n,用来存放国际化资源。然后再创建国际化资源,一个是message.properties,一个是message_zh_CN.properties,内容如下

image-20200224110320122

接下来的工作是在springmvc-servlet中配置好这个国际化资源:

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

千万要注意,需要给这个bean添加一个id,且其值只能是messageSource,不然这个bean不会起作用。起初我配置好bean后,没给他加上id,然后配置的国际化根本不起作用,这个问题困扰我好久,最后通过查阅资料得知:

​ 对Spring容器启动时的步骤进行剖析,①处的initMessageSource()方法所执行的工作就是初始化容器中的国际化信息资源:它根据反射机制从BeanDefinitionRegistry中找出名称为“messageSource”且类型为org.springframework.context.MessageSource的Bean,将这个Bean定义的信息资源加载为容器级的国际化信息资源。

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                //①初始化消息源
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

然后看看MessageSource类图结构

image-20200224111605031

所以容器中存在ResourceBundleMessageSource这个bean,并且id为messageSource时,spring才会把这个Bean定义的信息资源加载为容器级的国际化信息资源

然后就可以把实体类中硬编码的message属性给去掉了,如下就是效果,和之前的一样

image-20200224102844378

猜你喜欢

转载自www.cnblogs.com/hemou/p/12356175.html