読者です 読者をやめる 読者になる 読者になる

かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot でメール送信する Web アプリケーションを作る ( その7 )( メール送信画面の作成2 )

概要

Spring Boot でメール送信する Web アプリケーションを作る ( その6 )( メール送信画面の作成 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • メール送信画面の作成
    • 今回は Validation を実装します。

ソフトウェア一覧

参考にしたサイト

  1. PostgreSQL 9.4.0文書 - CREATE DATABASE
    https://www.postgresql.jp/document/9.4/html/sql-createdatabase.html

  2. DBOnline - データベースの作成
    http://www.dbonline.jp/postgresql/database/index2.html

  3. PostgreSQL 9.4.0文書 - CREATE TABLE
    https://www.postgresql.jp/document/9.4/html/sql-createtable.html

  4. How to set auto increment primary key in PostgreSQL? http://stackoverflow.com/questions/7718585/how-to-set-auto-increment-primary-key-in-postgresql

  5. Let's postgres - 文字列型の使い分け
    http://lets.postgresql.jp/documents/technical/text-processing/1

  6. PostgreSQL assigns foreign key automatically on the referencing side
    http://stackoverflow.com/questions/19776041/postgresql-assigns-foreign-key-automatically-on-the-referencing-side

  7. PostgreSQL 9.4.0文書 - 8.1. 数値データ型
    https://www.postgresql.jp/document/9.4/html/datatype-numeric.html

  8. hibernate-validator
    https://github.com/hibernate/hibernate-validator

    • Hibernate Validator の GitHub です。@Email アノテーションのチェック内容を確認する際に参照しました。
    • @Email アノテーションを付加すると 65 文字までしか入力できなくなるのですが、ソースのどこで最大長が制御されているのか分かりませんでした。。。

手順

1.0.x-make-mailsendform-validation ブランチの作成

  1. IntelliJ IDEA で 1.0.x-make-mailsendform-validation ブランチを作成します。

Web アプリケーション用データベース、メール保存用テーブルの作成

SQL ファイルを格納するための /sql ディレクトリをプロジェクトのルート直下に作成します。

/sql の下に create_database.sql を作成します。作成後、リンク先の内容 に変更します。

コマンドプロンプトから以下のコマンドを実行してユーザとデータベースを作成します。

C:\project-springboot\ksbysample-webapp-email>psql -U postgres postgres
ユーザ postgres のパスワード:
psql (9.4.1)
"help" でヘルプを表示します.

postgres=# CREATE USER ksbyemail_user PASSWORD 'xxxxxxxx';
CREATE ROLE
postgres=# CREATE DATABASE ksbyemail OWNER ksbyemail_user TEMPLATE template0 ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C';
CREATE DATABASE
postgres=# \l
                                                データベース一覧
   名前    |     所有者     | エンコーディング |      照合順序      | Ctype(変換演算子)  |      アクセス権
-----------+----------------+------------------+--------------------+--------------------+-----------------------
 ksbyemail | ksbyemail_user | UTF8             | C                  | C                  |
 postgres  | postgres       | UTF8             | Japanese_Japan.932 | Japanese_Japan.932 |
 template0 | postgres       | UTF8             | Japanese_Japan.932 | Japanese_Japan.932 | =c/postgres          +
           |                |                  |                    |                    | postgres=CTc/postgres
 template1 | postgres       | UTF8             | Japanese_Japan.932 | Japanese_Japan.932 | =c/postgres          +
           |                |                  |                    |                    | postgres=CTc/postgres
(4 行)


postgres=# \q

C:\project-springboot\ksbysample-webapp-email>

/sql の下に create_table.sql を作成します。作成後、リンク先の内容 に変更します。

コマンドプロンプトから以下のコマンドを実行してテーブルを作成します。

C:\project-springboot\ksbysample-webapp-email>psql -U ksbyemail_user ksbyemail
ユーザ ksbyemail_user のパスワード:
psql (9.4.1)
"help" でヘルプを表示します.

ksbyemail=> CREATE TABLE EMAIL
ksbyemail-> (
ksbyemail(>     EMAIL_ID        bigserial primary key
ksbyemail(>     , FROM_ADDR     varchar(65) not null
ksbyemail(>     , TO_ADDR       varchar(65) not null
ksbyemail(>     , SUBJECT       varchar(128) not null
ksbyemail(>     , NAME          varchar(32)
ksbyemail(>     , SEX           smallint
ksbyemail(>     , TYPE          smallint
ksbyemail(>     , NAIYO         text
ksbyemail(> );
CREATE TABLE
ksbyemail=> CREATE TABLE EMAIL_ITEM
ksbyemail-> (
ksbyemail(>     EMAIL_ITEM_ID   bigserial primary key
ksbyemail(>     , EMAIL_ID      bigint not null references EMAIL(EMAIL_ID) on delete cascade
ksbyemail(>     , ITEM          smallint
ksbyemail(> );
CREATE TABLE
ksbyemail=> \d                                                                  
                          リレーションの一覧                                             
 スキーマ |             名前             |     型     |     所有者                        
----------+------------------------------+------------+----------------         
 public   | email                        | テーブル   | ksbyemail_user              
 public   | email_email_id_seq           | シーケンス | ksbyemail_user               
 public   | email_item                   | テーブル   | ksbyemail_user              
 public   | email_item_email_item_id_seq | シーケンス | ksbyemail_user               
(4 行)                                                                           
                                                                                
                                                                                
ksbyemail=> \q

C:\project-springboot\ksbysample-webapp-email>

application.properties の変更

  1. src/main/resources の下の application-develop.properties, application-product.properties, application-unittest.properties をそれぞれ リンク先の内容 に変更します。

  2. Gradle projects View から bootRun タスクを実行して Tomcat が正常に起動することを確認します。確認後 Run View で Ctrl+F2 を押して Tomcat を停止します。

Form クラスの変更

  1. Form クラスに Bean Validation を追加します。src/main/java/ksbysample/webapp/email/web/mailsend の下の MailsendForm.javaリンク先の内容 に変更します。

ValidationMessages_ja_JP.properties, messages_ja_JP.properties の変更

  1. @Email のエラーメッセージを追加します。src/main/resources の下の ValidationMessages_ja_JP.properties を リンク先の内容 に変更します。

  2. src/main/resources の下の messages_ja_JP.properties を リンク先のその1の内容 に変更します。

Controller クラスの変更

  1. Validation が実行されるよう処理を追加します。src/main/java/ksbysample/webapp/email/web/mailsend の下の MailsendController.javaリンク先のその1の内容 に変更します。

mailsend.html の変更

  1. src/main/resources/templates/mailsend の下の mailsend.html を リンク先のその1の内容 に変更します。

MAIL001MailBuilder クラスの変更

  1. MailsendForm クラスのフィールド名を変更したので、このクラスを利用している MAIL001MailBuilder クラスも変更します。
    IntelliJ IDEA のリファクタリングの機能で変更しようとしたら gradle のファイル等の中の "from" の文字まで変更しようとされました。from や to 等予約語と思われるものを使ってはダメですね。。。

    src/main/java/ksbysample/webapp/email/web/mailsend の下の MAIL001MailBuilder.javaリンク先の内容 に変更します。

動作確認

  1. 一旦動作確認します。Gradle projects View から bootRun タスクを実行して Tomcat を起動します。

  2. ブラウザを起動し http://localhost:8080/mailsend へアクセスした後、以下の状態にしてから「送信」ボタンをクリックします。

    • From, To, Subject には何も入力しない。
    • 氏名には 33 文字入力する。
    • 内容には 2001 文字入力する。

    f:id:ksby:20150424024118p:plain

    • エラーメッセージが表示され、ラベルと入力項目が赤くなることを確認します。
    • ラジオボタン、ドロップダウンリスト、チェックボックスにも Validation は入れましたが、画面から入力している場合にはエラーにならないので確認しません。

FormValidator クラスの作成

  1. 以下の複合項目入力チェックを実装します。

    • 「項目」が「資料請求」「商品に関する苦情」の場合には「商品」が何も選択されていない場合にはエラーにする。
    • 「項目」が「その他」の場合には「内容」に何も入力されていない場合にはエラーにする。
    • 上記の入力チェックのエラーメッセージは画面上部の共通エラーメッセージ表示エリアに表示にする。
  2. src/main/java/ksbysample/webapp/email/web/mailsend の下に MailsendFormValidator.java を作成します。作成後、リンク先の内容 に変更します。

messages_ja_JP.properties

  1. src/main/resources の下の messages_ja_JP.properties を リンク先のその2の内容 に変更します。

Controller クラスの変更

  1. Validation が実行されるよう処理を追加します。src/main/java/ksbysample/webapp/email/web/mailsend の下の MailsendController.javaリンク先のその2の内容 に変更します。

mailsend.html の変更

  1. 共通エラーメッセージ表示エリアを追加します。src/main/resources/templates/mailsend の下の mailsend.html を リンク先のその2の内容 に変更します。

動作確認

  1. Run View で Ctrl+F5 を押して Tomcat を再起動します。

  2. ブラウザで http://localhost:8080/mailsend へアクセスした後、何も変更せずに「送信」ボタンをクリックします。

    f:id:ksby:20150426014635p:plain

    画面上部の共通エラーメッセージ表示エリアにエラーメッセージが表示されます。

  3. 再度 http://localhost:8080/mailsend へアクセスした後、以下の状態にしてから「送信」ボタンをクリックします。

    • 「項目」で「その他」を選択する。

    f:id:ksby:20150426015249p:plain

    画面上部の共通エラーメッセージ表示エリアにエラーメッセージが表示されます。

  4. Run View で Ctrl+F2 を押して Tomcat を停止します。

commit、Push、Pull Request、マージ

  1. commit します。commit 時に Code Analysis のダイアログが表示されますので、「Review」ボタンをクリックして以下の対応をします。

    • Unused property の Warning は無視します。
    • Attribute th:... is not allowed here の Warning が出ていましたので、出なくなるようにします。対象は th:if, th:classappend, th:errors です。
  2. commit、GitHub へ Push、1.0.x-make-mailsendform-validation -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-make-mailsendform-validation ブランチを削除、をします。

課題

  • MailsendFormValidator.java のソース内に定数をそのまま記述してしまったのですが、本当は以下のようにしたいです。今回どうすればよいのか思いつかなかったので、よい方法がないか考えてみます。
    • Thymeleaf テンプレートファイルに出力する値は、今回実装したように constant.yml に定義したい。
    • 「資料請求」のコード "1" 等を Java のソースに記述する場合には、public static final で定義された定数を記述したい。
    • 上記2つを2箇所以上に定義するのではなく、1箇所だけで定義したい ( constant.yaml に記述した「資料請求」の 1 と、public static final で定義した「資料請求」の 1 は1箇所だけで定義して、その1箇所を修正すればよいだけにしたい、という意味です )。
    • Thymeleaf テンプレートファイルに出力する値を Enum で定義しているサンプルを見かけたことがあるので、そちらの方がよいのかもしれません。個人的には constant.yml のように1つのファイルにまとまっている方が好みなのですが。。。

ソースコード

create_database.sql

CREATE USER ksbyemail_user PASSWORD 'xxxxxxxx';
CREATE DATABASE ksbyemail OWNER ksbyemail_user TEMPLATE template0 ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C';

create_table.sql

CREATE TABLE EMAIL
(
    EMAIL_ID        bigserial primary key
    , FROM_ADDR     varchar(65) not null
    , TO_ADDR       varchar(65) not null
    , SUBJECT       varchar(128) not null
    , NAME          varchar(32)
    , SEX           smallint
    , TYPE          smallint
    , NAIYO         text
);

CREATE TABLE EMAIL_ITEM
(
    EMAIL_ITEM_ID   bigserial primary key
    , EMAIL_ID      bigint not null references EMAIL(EMAIL_ID) on delete cascade
    , ITEM          smallint
);
  • primary key にするカラムは自動採番にするために bigserial 型にします。
  • EMAIL テーブルの FROM, TO は DB の予約語カラム名に使用できなかったので、FROM_ADDR, TO_ADDR へ変更しました。後で html, クラスの方も変更します。

application-develop.properties, application-product.properties, application-unittest.properties

■application-develop.properties

spring.datasource.url = jdbc:log4jdbc:postgresql://localhost/ksbyemail
spring.datasource.username = ksbyemail_user
spring.datasource.password = xxxxxxxx
spring.datasource.driverClassName = net.sf.log4jdbc.sql.jdbcapi.DriverSpy
  • spring.datasource.url, spring.datasource.username を今回作成したデータベース名、ユーザ名に変更します。

■application-product.properties

spring.datasource.url = jdbc:postgresql://localhost/ksbyemail
spring.datasource.username = ksbyemail_user
spring.datasource.password = xxxxxxxx
spring.datasource.driverClassName = org.postgresql.Driver
  • spring.datasource.url, spring.datasource.username を今回作成したデータベース名、ユーザ名に変更します。

■application-unittest.properties

spring.datasource.url = jdbc:postgresql://localhost/ksbyemail
spring.datasource.username = ksbyemail_user
spring.datasource.password = xxxxxxxx
spring.datasource.driverClassName = org.postgresql.Driver
  • spring.datasource.url, spring.datasource.username を今回作成したデータベース名、ユーザ名に変更します。

MailsendForm.java

package ksbysample.webapp.email.web.mailsend;

import lombok.Data;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotBlank;

import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.util.List;

@Data
public class MailsendForm {

    @NotBlank
    @Email
    private String fromAddr;

    @NotBlank
    @Email
    private String toAddr;

    @NotBlank
    @Size(max = 128, message = "{error.size.max}")
    private String subject;

    @Size(max = 32, message = "{error.size.max}")
    private String name;

    @Pattern(regexp = "^(|1|2)$", message = "{mailsendForm.sex.pattern}")
    private String sex;

    @Pattern(regexp = "^(|1|2|3)$", message = "{mailsendForm.type.pattern}")
    private String type;

    private List<String> item;

    @Size(max = 2000, message = "{error.size.max}")
    private String naiyo;

}
  • フィールド名を fromfromAddrtotoAddr へ変更します。
  • fromAddr, toAddr はメールアドレスなので、@Email アノテーションを使ってみます。

ValidationMessages_ja_JP.properties

javax.validation.constraints.NotNull.message=必須の入力項目です。

org.hibernate.validator.constraints.NotBlank.message=必須の入力項目です。
org.hibernate.validator.constraints.Email.message=メールアドレスを入力して下さい。

error.size.max = {max}文字以内で入力して下さい。
error.digits.integerandfraction = 数値を整数{integer}桁以内、小数{fraction}桁以内で入力して下さい。
error.digits.integeronly = 数値を整数{integer}桁以内で入力して下さい。
  • org.hibernate.validator.constraints.Email.message を追加します。

messages_ja_JP.properties

■その1

AbstractUserDetailsAuthenticationProvider.locked=入力された ID はロックされています
AbstractUserDetailsAuthenticationProvider.disabled=入力された ID は使用できません
AbstractUserDetailsAuthenticationProvider.expired=入力された ID の有効期限が切れています
AbstractUserDetailsAuthenticationProvider.credentialsExpired=入力された ID のパスワードの有効期限が切れています
AbstractUserDetailsAuthenticationProvider.badCredentials=入力された ID あるいはパスワードが正しくありません

typeMismatch.java.math.BigDecimal=数値を入力して下さい。
typeMismatch.java.lang.Long=数値を入力して下さい。

mailsendForm.sex.pattern=男性か女性のいずれかを選択して下さい。
mailsendForm.type.pattern=資料請求、商品に関する苦情、その他のいずれかを選択して下さい。
  • mailsendForm.sex.pattern, mailsendForm.type.pattern を追加します。

■その2

mailsendForm.sex.pattern=男性か女性のいずれかを選択して下さい。
mailsendForm.type.pattern=資料請求、商品に関する苦情、その他のいずれかを選択して下さい。
mailsendForm.item.noSelect=項目に資料請求、商品に関する苦情を選択している場合には、商品を1つ以上選択して下さい。
mailsendForm.naiyo.noText=項目にその他を選択している場合には、内容欄に具体的な内容を入力して下さい。
  • mailsendForm.item.noSelect, mailsendForm.naiyo.noText を追加します。

MailsendController.java

■その1

    @RequestMapping("/send")
    public String send(@Validated MailsendForm mailsendForm
            , BindingResult bindingResult
            , Model model) {
        if (bindingResult.hasErrors()) {
            return "mailsend/mailsend";
        }

        // メールを送信する
        SimpleMailMessage mailMessage = MAIL001MailBuilder.build()
                .setForm(mailsendForm)
                .setVelocityUtils(velocityUtils)
                .setTemplateLocation("mail/MAIL001/MAIL001-body.vm")
                .create();
        emailService.sendSimpleMail(mailMessage);

        return "redirect:/mailsend";
    }
  • send メソッドの以下の点を変更します。
    • MailsendForm mailsendForm@Validated MailsendForm mailsendForm へ変更します。
    • メソッドの引数に BindingResult bindingResult を追加します。
    • Validation エラーチェックの処理である if (bindingResult.hasErrors()) { ... } を追加します。

■その2

@Controller
@RequestMapping("/mailsend")
public class MailsendController {

    @Autowired
    private VelocityUtils velocityUtils;

    @Autowired
    private EmailService emailService;

    @Autowired
    private MailsendFormValidator mailsendFormValidator;

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.addValidators(mailsendFormValidator);
    }
  • private MailsendFormValidator mailsendFormValidator; を追加します。
  • initBinder メソッドを追加します。

mailsend.html

■その1

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>ksbysample-webapp-email</title>
    <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'/>

    <meta th:replace="common/head-cssjs"/>
    <style>
    <!--
    .checkbox label,
    .radio label {
        padding-right: 10px;
    }
    .form-control-static {
        padding-top: 0px;
        padding-bottom: 0px;
    }
    .has-error .form-control {
        background-color: #fff5ee;
    }
    -->
    </style>
</head>
<body class="skin-blue">
<div class="wrapper">

    <!-- Main Header -->
    <div th:replace="common/mainparts :: main-header"></div>

    <!-- Left side column. contains the logo and sidebar -->
    <div th:replace="common/mainparts :: main-sidebar (active='mailsend')"></div>

    <!-- Content Wrapper. Contains page content -->
    <div class="content-wrapper">
        <!-- Content Header (Page header) -->
        <section class="content-header">
            <h1>
                メール送信画面
            </h1>
        </section>

        <!-- Main content -->
        <section class="content">
            <div class="row">
                <div class="col-xs-12">
                    <form id="mailsendForm" method="post" action="/mailsend/send" th:action="@{/mailsend/send}" th:object="${mailsendForm}" class="form-horizontal">
                        <!--<div class="callout callout-danger">-->
                            <!--<p>共通エラーメッセージ表示エリア</p>-->
                        <!--</div>-->
                        <div class="box box-primary">
                            <div class="box-body">
                                <div class="form-group" th:classappend="${#fields.hasErrors('*{fromAddr}')} ? 'has-error' : ''">
                                    <label for="fromAddr" class="control-label col-sm-2">From</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-8"><div class="input-group"><span class="input-group-addon"><i class="fa fa-envelope"></i></span><input type="text" name="fromAddr" id="fromAddr" class="form-control input-sm" value="" placeholder="Fromアドレスを入力して下さい" th:field="*{fromAddr}"/></div></div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{fromAddr}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{fromAddr}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{toAddr}')} ? 'has-error' : ''">
                                    <label for="toAddr" class="control-label col-sm-2">To</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-8"><div class="input-group"><span class="input-group-addon"><i class="fa fa-envelope"></i></span><input type="text" name="toAddr" id="toAddr" class="form-control input-sm" value="" placeholder="Toアドレスを入力して下さい" th:field="*{toAddr}"/></div></div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{toAddr}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{toAddr}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{subject}')} ? 'has-error' : ''">
                                    <label for="subject" class="control-label col-sm-2">Subject</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-12"><input type="text" name="subject" id="subject" class="form-control input-sm" value="" placeholder="件名を入力して下さい" th:field="*{subject}"/></div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{subject}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{subject}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{name}')} ? 'has-error' : ''">
                                    <label for="name" class="control-label col-sm-2">氏名</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-8"><input type="text" name="name" id="name" class="form-control input-sm" value="" placeholder="(例) 田中 太郎" th:field="*{name}"/></div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{name}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{name}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{sex}')} ? 'has-error' : ''">
                                    <label class="control-label col-sm-2">性別</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-12">
                                            <div class="radio">
                                                <label th:each="sex : ${T(ksbysample.webapp.email.config.Constant).getInstance().SEX_MAP.entrySet()}">
                                                    <input type="radio" name="sex" th:value="${sex.getKey()}" th:text="${sex.getValue()}" th:field="*{sex}"/>
                                                </label>
                                            </div>
                                        </div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{sex}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{sex}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{type}')} ? 'has-error' : ''">
                                    <label class="control-label col-sm-2">項目</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-8">
                                            <select name="type" id="type" class="form-control input-sm" th:field="*{type}">
                                                <option th:each="type : ${T(ksbysample.webapp.email.config.Constant).getInstance().TYPE_MAP.entrySet()}"
                                                        th:value="${type.getKey()}"
                                                        th:text="${type.getValue()}">sex</option>
                                            </select>
                                        </div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{type}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{type}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{item}')} ? 'has-error' : ''">
                                    <label class="control-label col-sm-2">商品</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-12">
                                            <div class="checkbox">
                                                <label th:each="item : ${T(ksbysample.webapp.email.config.Constant).getInstance().ITEM_MAP.entrySet()}">
                                                    <input type="checkbox" name="item" th:value="${item.getKey()}" th:text="${item.getValue()}" th:field="*{item}"/>
                                                </label>
                                            </div>
                                        </div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{item}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{item}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>

                                <div class="form-group" th:classappend="${#fields.hasErrors('*{naiyo}')} ? 'has-error' : ''">
                                    <label for="naiyo" class="control-label col-sm-2">内容</label>
                                    <div class="col-sm-10">
                                        <div class="row"><div class="col-sm-12"><textarea rows="5" name="naiyo" id="naiyo" class="form-control input-sm" placeholder="お問い合わせ内容を入力して下さい" th:field="*{naiyo}"></textarea></div></div>
                                        <div class="row" th:if="${#fields.hasErrors('*{naiyo}')}"><div class="col-sm-10"><p class="form-control-static text-danger"><small th:errors="*{naiyo}">ここにエラーメッセージを表示します</small></p></div></div>
                                    </div>
                                </div>
                            </div>
                            <div class="box-footer">
                                <div class="text-center">
                                    <button type="button" id="send" value="send" class="btn btn-primary">送信</button>
                                </div>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </section>
        <!-- /.content -->
    </div>
    <!-- /.content-wrapper -->

</div>
<!-- ./wrapper -->

<!-- REQUIRED JS SCRIPTS -->

<div th:replace="common/bottom-js"></div>
<script type="text/javascript">
<!--
$(document).ready(function() {
    $('#from').focus();

    $('#send').bind('click', function(){
        $('#mailsendForm').submit();
    });
});
-->
</script>
</body>
</html>
  • エラーメッセージが表示される時に入力項目の間が開きすぎるので、あまり開かないようにするために .form-control-static { ... } を追加します。
  • エラー発生時に入力項目の背景色を赤色にするために .has-error .form-control { ... } を追加します。
  • from → fromAddr へ変更します。
  • to → toAddr へ変更します。
  • エラー発生時に入力項目の文字と枠が赤くなるよう、全ての入力項目で <div class="form-group"><div class="form-group" th:classappend="${#fields.hasErrors('*{...}')} ? 'has-error' : ''"> へ変更します。
  • 各入力項目毎にエラーメッセージ表示領域 <div class="row" th:if="${#fields.hasErrors('*{...}')}"> ... </div> を追加します。

■その2

                    <form id="mailsendForm" method="post" action="/mailsend/send" th:action="@{/mailsend/send}" th:object="${mailsendForm}" class="form-horizontal">
                        <div class="callout callout-danger" th:if="${#fields.hasGlobalErrors()}">
                            <p th:each="err : ${#fields.globalErrors()}" th:text="${err}">共通エラーメッセージ表示エリア</p>
                        </div>

                        <div class="box box-primary">
  • 共通エラーメッセージ表示エリアの部分のコメントアウトを外して、<div class="callout callout-danger" th:if="${#fields.hasGlobalErrors()}"> ... </div> に変更します。

MAIL001MailBuilder.java

    public SimpleMailMessage create() {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setFrom(mailsendForm.getFromAddr());
        mailMessage.setTo(mailsendForm.getToAddr());
        mailMessage.setSubject(mailsendForm.getSubject());
  • create メソッド内の以下の点を変更します。
    • mailsendForm.getFrom()mailsendForm.getFromAddr() へ変更します。
    • mailsendForm.getTo()mailsendForm.getToAddr() へ変更します。

MailsendFormValidator.java

package ksbysample.webapp.email.web.mailsend;

import ksbysample.webapp.email.config.Constant;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

@Component
public class MailsendFormValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        return clazz.equals(MailsendForm.class);
    }

    @Override
    public void validate(Object target, Errors errors) {
        MailsendForm mailsendForm = (MailsendForm)target;
        Constant constant = Constant.getInstance();

        // 「項目」が「資料請求」「商品に関する苦情」の場合には「商品」が何も選択されていない場合にはエラー
        if (StringUtils.equals(mailsendForm.getType(), "1")
                || StringUtils.equals(mailsendForm.getType(), "2")) {
            if (mailsendForm.getItem() == null) {
                errors.reject("mailsendForm.item.noSelect");
            }
        }

        // 「項目」が「その他」の場合には「内容」に何も入力されていない場合にはエラー
        if (StringUtils.equals(mailsendForm.getType(), "3")) {
            if (mailsendForm.getNaiyo().length() == 0) {
                errors.reject("mailsendForm.naiyo.noText");
            }
        }
    }

}

履歴

2015/04/26
初版発行。