Spring Boot框架開發(fā)Web項(xiàng)目之八 表單校驗(yàn)

本系列文章主要索引詳情 點(diǎn)擊查看


通常情況下,我們都不希望用戶輸入非法的信息诀姚,這樣的話嘱能,我們就需要對(duì)表單添加一些校驗(yàn)邏輯滥朱。

工具

IntelliJ IDEA 16
JDK 1.8
Maven 3.5
Tomcat 1.8

表單校驗(yàn)

我們可以通過添加一些注解,來輔助完成校驗(yàn)限制毒涧。
1贮预、首先我們需要在DTO的ProfileForm類的屬性字段上添加限制注解:

package com.example.dto;


import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.LocalDate;

public class ProfileForm {
    @Size(min = 2)
    private String twitterHandle;

    @Email
    @NotNull
    private String email;

    @NotNull
    private LocalDate birthDate;

    public String getTwitterHandle() {
        return twitterHandle;
    }

    public void setTwitterHandle(String twitterHandle) {
        this.twitterHandle = twitterHandle;
    }

    public String getEmail() {
        return email;
    }

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

    public LocalDate getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;
    }

    @Override
    public String toString() {
        return "ProfileForm{" +
                "twitterHandle='" + twitterHandle + '\'' +
                ", email='" + email + '\'' +
                ", birthDate='" + birthDate + '\'' +
                '}';
    }
}

這些注解來源于JSR-303規(guī)范,它詳細(xì)規(guī)定了bean的校驗(yàn)功能契讲,這個(gè)規(guī)范最流行的實(shí)現(xiàn)是hibernate-validator仿吞,它已經(jīng)包含在了Spring Boot之中。

我們使用了來自javax.validation.constraints包中的注解(在API中定義的)和org.hibernate.validator.constraints包的注解(額外限制)捡偏。我們可通過查閱validation-api和hibernate-validator的jar包文件來查看都有哪些可用注解唤冈。我們還可以查閱hibernate-validator的文檔來了解它提供的可用限制。

2银伟、完成了對(duì)DTO屬性的限制你虹,我們還學(xué)要在控制器中還需要聲明在表單提交時(shí),希望得到一個(gè)合法的模型彤避。在提交方法的表單的參數(shù)上添加@Valid注解:

package com.example.controller;

import com.example.date.LocalDateFormatter;
import com.example.dto.ProfileForm;
import java.util.Locale;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.validation.Valid;

@Controller
public class ProfileController {

    @RequestMapping("/profile")
    public String displayProfile(ProfileForm profileForm){
        return "profile/profilePage";
    }

    @RequestMapping(value = "/profile" ,method = RequestMethod.POST)
    public String saveProfile(@Valid ProfileForm profileForm, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            return "profile/profilePage";
        }
        System.out.println("Save Ok"+profileForm);
        return "redirect:/profile";
    }

    @ModelAttribute("dataFormat")
    public String localeFormat(Locale locale){
        return LocalDateFormatter.getPattern(locale);
    }
}

@Valid注解 : javax.validation.Valid 傅物, 聲明表單提交時(shí),進(jìn)行校驗(yàn)琉预,從而得到是否是一個(gè)合法的模型董饰。
BindingResult : Spring驗(yàn)證的錯(cuò)誤返回,來將驗(yàn)證錯(cuò)誤的信息返回到頁面圆米。

注意:如果表單中包含錯(cuò)誤信息卒暂,我們不進(jìn)行重定向,而是直接返回到同一個(gè)web頁面中娄帖,并在頁面中顯示錯(cuò)誤也祠。

3、最后我們需要在Web頁面中添加一個(gè)位置來展現(xiàn)這些錯(cuò)誤,profilePage.html文件中块茁,表單標(biāo)簽開始的地方添加如下代碼:

 <ul th:if="${#fields.hasErrors('*')}" class="errorlist">
        <li th:each="err:${#fields.errors('*')}" th:text="${err}">input is incorrect</li>
 </ul>

注意:這段代碼必須添加在<form>...</form>標(biāo)簽中齿坷,如果添加在<form>...</form>之外桂肌,將會(huì)出現(xiàn)如下異常:

Could not bind form errors using expression "*". Please check this expression is being executed inside the adequate context (e.g. a <form> with a th:object attribute)

4、如果我們不輸入任何數(shù)據(jù)永淌,直接點(diǎn)提交崎场,則會(huì)顯示如下錯(cuò)誤提示:

5、如果我們輸入的信息不合法遂蛀,則會(huì)顯示如下:

自定義校驗(yàn)信息

從上面我們可以看出谭跨,這些顯示的錯(cuò)誤信息對(duì)我們并沒有什么用處,我們并不能完全區(qū)分出這些錯(cuò)誤信息是針對(duì)哪個(gè)輸入域李滴,所以螃宙,接下來我們需要完成的一項(xiàng)工作就是將錯(cuò)誤提示信息和對(duì)應(yīng)的輸入域關(guān)聯(lián)起來,幫助我們更清晰的了解我們的問題出在哪里所坯。
按照如下方式修改profilePage.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http:www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default">
<head>
    <meta charset="UTF-8"/>
    <title>Your profile</title>
</head>
<body>
    <div class="row" layout:fragment="content">

        <h2 class="indigo-text center">Prosonal info</h2>

        <!--/*@thymesVar id="profileForm" type="com.example.dto.ProfileForm"*/-->
        <form th:action="@{/profile}" th:object="${profileForm}" method="post" class="col m8 s11 offset-m2" >
            <div class="row">
                <div class="input-field col s6">
                    <input th:field="${profileForm.twitterHandle}" id="twitterHandle" type="text" th:errorclass="invalid"/>
                    <label for="twitterHandle">Last name</label>
                    <div th:errors="*{twitterHandle}" class="red-text">Error</div>
                </div>
                <div class="input-field col s6">
                    <input th:field="${profileForm.email}" id="email" type="text" th:errorclass="invalid"/>
                    <label for="email">Email</label>
                    <div th:errors="*{email}" class="red-text">Error</div>
                </div>
            </div>

            <div class="row">
                <div class="input-field col s6">
                    <input id="birthDate" type="text" th:field="${profileForm.birthDate}" th:placeholder="${dataFormat}" th:errorclass="invalid"/>
                    <label for="birthDate">Birth Date</label>
                    <div th:errors="*{birthDate}" class="red-text">Error</div>
                </div>
            </div>
            <div class="row s12">
                <button class="btn waves-effect waves-light" type="submit" name="save">Submit<i class="mdi-content-snd right"></i> </button>
            </div>
        </form>

    </div>
</body>
</html>

在表單的每個(gè)輸入域中添加 th:errorclass屬性谆扎,并在輸入域的下面新增一個(gè)th:errors標(biāo)簽,如果輸入域包含錯(cuò)誤的話芹助,將會(huì)有如下顯示:

現(xiàn)在我們已經(jīng)將錯(cuò)誤提示信息和輸入域進(jìn)行了綁定堂湖,但是我們可以發(fā)現(xiàn),有些提示信息并不適合展示給用戶状土,所以我們需要對(duì)錯(cuò)誤提示信息進(jìn)行自定義无蜂。
方法一:
Spring Boot會(huì)負(fù)責(zé)為我們創(chuàng)建信息源bean,信息源的默認(rèn)位置是 src/main/resource/messages.properties,如果我們沒有看到這個(gè)文件蒙谓,則創(chuàng)建messages.properties斥季,并添加以下文本:

Size.profileForm.twitterHandle=Please type in your twitter user name
Email.profileForm.email=Please specify a valid email address
NotNull.profileForm.email=Please specify your email address
PastLocalDate.profileForm.birthDate=Please specify a real birth date
NotNull.profileForm.birthDate=Please specify your birth date
typeMismatch.birthDate=Invalid birth date format

以上文本定義了錯(cuò)誤類型和對(duì)應(yīng)的輸入域,以及錯(cuò)誤提示信息累驮,并按照鍵值對(duì)的形式存在酣倾,解析錯(cuò)誤時(shí),根據(jù)錯(cuò)誤類型慰照,返回對(duì)應(yīng)的錯(cuò)誤提示信息到頁面灶挟。
在Spring中負(fù)責(zé)解析錯(cuò)誤信息的類是DefaultMessageCodesResolver。在進(jìn)行輸入域校驗(yàn)的時(shí)候毒租,這個(gè)類將會(huì)按照如下順序來嘗試解析信息:
1稚铣、編碼 + “.” + 對(duì)象 + “.” + 輸入域 (例: Size.profileForm.twitterHandle)
2、編碼 + “.” + 輸入域
3墅垮、編碼 + “.” + 輸入域類型
4惕医、編碼
現(xiàn)在我們將會(huì)看到我們自定義的錯(cuò)誤信息了:

在開發(fā)期,我們可以將信息源配置為每次都重新加載算色,我們只需要在application.properties文件中抬伺,添加

spring.messages.cache-seconds=0

其中 0 表示每次都重新加載,而 -1 則表示不進(jìn)行重新加載灾梦。

方法二:
有了上面的信息峡钓,我們接下來可以讓它更為具體妓笙,定義默認(rèn)信息的最佳實(shí)踐如下所示:

Size=the {0} field must be between {2} and {1} characters long

注意這里的占位符,每個(gè)校驗(yàn)錯(cuò)誤都有與之相關(guān)聯(lián)的一組參數(shù)能岩,通過運(yùn)行結(jié)果我們可以有清晰的了解:

方法三:
聲明錯(cuò)誤的最后一種方法是直接在檢驗(yàn)注解中定義錯(cuò)誤信息:

@Size(min = 2 ,message = "Please specify a valid twitter handle")

但是這種方式的缺點(diǎn)在于它無法與國際化功能兼容寞宫。

客戶端校驗(yàn)

通過使用HTML5的表單校驗(yàn)規(guī)范,現(xiàn)在實(shí)現(xiàn)客戶端校驗(yàn)已經(jīng)非常容易了拉鹃,如果瀏覽器是Internet Explorer 10 及以上的話辈赋,通過添加客戶端校驗(yàn)只需要指定正確的輸入域類型,不再講type屬性設(shè)置為text膏燕。

通過添加客戶端校驗(yàn)钥屈,我們可以預(yù)先校驗(yàn)表單,避免已知的不正確的請(qǐng)求對(duì)服務(wù)器造成過大的負(fù)載坝辫。

我們可以修改輸入域來啟動(dòng)簡單的客戶端校驗(yàn)篷就。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http:www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default">
<head>
    <meta charset="UTF-8"/>
    <title>Your profile</title>
</head>
<body>
    <div class="row" layout:fragment="content">
        <h2 class="indigo-text center">Prosonal info</h2>
        <!--/*@thymesVar id="profileForm" type="com.example.dto.ProfileForm"*/-->
        <form th:action="@{/profile}" th:object="${profileForm}" method="post" class="col m8 s11 offset-m2" >
            <div class="row">
                <div class="input-field col s6">
                    <input th:field="${profileForm.twitterHandle}" id="twitterHandle" type="text" required="required" th:errorclass="invalid"/>
                    <label for="twitterHandle">Last name</label>
                    <div th:errors="*{twitterHandle}" class="red-text">Error</div>
                </div>
                <div class="input-field col s6">
                    <input th:field="${profileForm.email}" id="email" type="email" required="required" th:errorclass="invalid"/>
                    <label for="email">Email</label>
                    <div th:errors="*{email}" class="red-text">Error</div>
                </div>
            </div>
            <div class="row">
                <div class="input-field col s6">
                    <input id="birthDate" type="text" th:field="${profileForm.birthDate}" th:placeholder="${dataFormat}" required="required" th:errorclass="invalid"/>
                    <label for="birthDate">Birth Date</label>
                    <div th:errors="*{birthDate}" class="red-text">Error</div>
                </div>
            </div>
            <div class="row s12">
                <button class="btn waves-effect waves-light" type="submit" name="save">Submit<i class="mdi-content-snd right"></i> </button>
            </div>
        </form>
    </div>
</body>
</html>

在每個(gè)表單輸入域中添加屬性 required="required",將強(qiáng)制用戶輸入非空的值近忙,而修改type=“email”腻脏,會(huì)為對(duì)應(yīng)的輸入域進(jìn)行基本的E-mail格式校驗(yàn)。

上一篇:Spring Boot框架開發(fā)Web項(xiàng)目之七 日期的使用和輸出日志

下一篇:Spring Boot 框架開發(fā)Web項(xiàng)目之九 Spring Boot項(xiàng)目的打包和部署

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末银锻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子做鹰,更是在濱河造成了極大的恐慌击纬,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钾麸,死亡現(xiàn)場離奇詭異更振,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)饭尝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門肯腕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人钥平,你說我怎么就攤上這事实撒。” “怎么了涉瘾?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵知态,是天一觀的道長。 經(jīng)常有香客問我立叛,道長负敏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任秘蛇,我火速辦了婚禮其做,結(jié)果婚禮上顶考,老公的妹妹穿的比我還像新娘。我一直安慰自己妖泄,他們只是感情好驹沿,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著浮庐,像睡著了一般甚负。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上审残,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天梭域,我揣著相機(jī)與錄音,去河邊找鬼搅轿。 笑死病涨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的璧坟。 我是一名探鬼主播既穆,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼雀鹃!你這毒婦竟也來了幻工?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤黎茎,失蹤者是張志新(化名)和其女友劉穎囊颅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體傅瞻,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡踢代,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嗅骄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胳挎。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖溺森,靈堂內(nèi)的尸體忽然破棺而出慕爬,到底是詐尸還是另有隱情,我是刑警寧澤屏积,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布澡罚,位于F島的核電站,受9級(jí)特大地震影響肾请,放射性物質(zhì)發(fā)生泄漏留搔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一铛铁、第九天 我趴在偏房一處隱蔽的房頂上張望隔显。 院中可真熱鬧却妨,春花似錦、人聲如沸括眠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掷豺。三九已至捞烟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間当船,已是汗流浹背题画。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留德频,地道東北人苍息。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像壹置,于是被迫代替她去往敵國和親竞思。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容