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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM