Spring Boot框架开发Web项目之八 表单校验

本系列文章主要索引详情 点击查看


通常情况下,我们都不希望用户输入非法的信息,这样的话,我们就需要对表单添加一些校验逻辑。

工具

IntelliJ IDEA 16
JDK 1.8
Maven 3.5
Tomcat 1.8

表单校验

我们可以通过添加一些注解,来辅助完成校验限制。
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规范,它详细规定了bean的校验功能,这个规范最流行的实现是hibernate-validator,它已经包含在了Spring Boot之中。

我们使用了来自javax.validation.constraints包中的注解(在API中定义的)和org.hibernate.validator.constraints包的注解(额外限制)。我们可通过查阅validation-api和hibernate-validator的jar包文件来查看都有哪些可用注解。我们还可以查阅hibernate-validator的文档来了解它提供的可用限制。

2、完成了对DTO属性的限制,我们还学要在控制器中还需要声明在表单提交时,希望得到一个合法的模型。在提交方法的表单的参数上添加@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 , 声明表单提交时,进行校验,从而得到是否是一个合法的模型。
BindingResult : Spring验证的错误返回,来将验证错误的信息返回到页面。

注意:如果表单中包含错误信息,我们不进行重定向,而是直接返回到同一个web页面中,并在页面中显示错误。

3、最后我们需要在Web页面中添加一个位置来展现这些错误,profilePage.html文件中,表单标签开始的地方添加如下代码:

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

注意:这段代码必须添加在<form>...</form>标签中,如果添加在<form>...</form>之外,将会出现如下异常:

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、如果我们不输入任何数据,直接点提交,则会显示如下错误提示:

5、如果我们输入的信息不合法,则会显示如下:

自定义校验信息

从上面我们可以看出,这些显示的错误信息对我们并没有什么用处,我们并不能完全区分出这些错误信息是针对哪个输入域,所以,接下来我们需要完成的一项工作就是将错误提示信息和对应的输入域关联起来,帮助我们更清晰的了解我们的问题出在哪里。
按照如下方式修改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>

在表单的每个输入域中添加 th:errorclass属性,并在输入域的下面新增一个th:errors标签,如果输入域包含错误的话,将会有如下显示:

现在我们已经将错误提示信息和输入域进行了绑定,但是我们可以发现,有些提示信息并不适合展示给用户,所以我们需要对错误提示信息进行自定义。
方法一:
Spring Boot会负责为我们创建信息源bean,信息源的默认位置是 src/main/resource/messages.properties,如果我们没有看到这个文件,则创建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

以上文本定义了错误类型和对应的输入域,以及错误提示信息,并按照键值对的形式存在,解析错误时,根据错误类型,返回对应的错误提示信息到页面。
在Spring中负责解析错误信息的类是DefaultMessageCodesResolver。在进行输入域校验的时候,这个类将会按照如下顺序来尝试解析信息:
1、编码 + “.” + 对象 + “.” + 输入域 (例: Size.profileForm.twitterHandle)
2、编码 + “.” + 输入域
3、编码 + “.” + 输入域类型
4、编码
现在我们将会看到我们自定义的错误信息了:

在开发期,我们可以将信息源配置为每次都重新加载,我们只需要在application.properties文件中,添加

spring.messages.cache-seconds=0

其中 0 表示每次都重新加载,而 -1 则表示不进行重新加载。

方法二:
有了上面的信息,我们接下来可以让它更为具体,定义默认信息的最佳实践如下所示:

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

注意这里的占位符,每个校验错误都有与之相关联的一组参数,通过运行结果我们可以有清晰的了解:

方法三:
声明错误的最后一种方法是直接在检验注解中定义错误信息:

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

但是这种方式的缺点在于它无法与国际化功能兼容。

客户端校验

通过使用HTML5的表单校验规范,现在实现客户端校验已经非常容易了,如果浏览器是Internet Explorer 10 及以上的话,通过添加客户端校验只需要指定正确的输入域类型,不再讲type属性设置为text。

通过添加客户端校验,我们可以预先校验表单,避免已知的不正确的请求对服务器造成过大的负载。

我们可以修改输入域来启动简单的客户端校验。

<!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>

在每个表单输入域中添加属性 required="required",将强制用户输入非空的值,而修改type=“email”,会为对应的输入域进行基本的E-mail格式校验。

上一篇:Spring Boot框架开发Web项目之七 日期的使用和输出日志

下一篇:Spring Boot 框架开发Web项目之九 Spring Boot项目的打包和部署

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容