4.7. メッセージ管理¶
4.7.1. Overview¶
Warning
以下の場合において、運用中、あるいは運用前の試験の際、エラーの原因を究明できなくなるリスクが生じる。(開発中は、特に困らないかもしれない。)
- エラーメッセージを、1つのみ定義している 
- エラーメッセージを、「重要」と「警告」の2つしか定義していない 
その結果、開発メンバが少ない中でメッセージの定義変更を行い、開発が進むにつれて修正コストが増えることになる。
そのため、あらかじめメッセージは細かい粒度で定義しておくことを推奨する。
本節では、プロパティファイルに設定する際の命名規則やプロパティに設定したメッセージの表示方法、ユーザの画面操作に応じたメッセージの出力方法について説明する。
4.7.1.1. メッセージタイプ¶
| メッセージタイプ | カテゴリ | 概要 | 
|---|---|---|
| info | 情報メッセージ | ユーザの操作による処理が正常に実行された後、画面に表示するメッセージ。 | 
| warn | 警告メッセージ | 処理は継続できるが、注意喚起が必要な状態である場合に表示するメッセージ。(例:パスワード有効期限切れが近い場合の通知メッセージ) | 
| error | 入力エラーメッセージ | ユーザの入力値が不正な場合に、入力画面に表示するメッセージ。 | 
| 業務エラーメッセージ | 業務ロジックでエラーと判定された場合に表示するメッセージ | |
| システムエラーメッセージ | システム起因のエラー(データベースとの接続失敗等)が発生し、ユーザの操作でリカバリできない場合に表示するメッセージ | 
4.7.1.2. パターン別メッセージタイプの分類¶
メッセージの出力パターンを、以下に示す。
 
メッセージパターンとメッセージの表示内容、及びメッセージタイプを、以下に示す。
| 記号 | パターン | 表示内容 | メッセージタイプ | 例 | 
|---|---|---|---|---|
| (A) | タイトル | 画面のタイトル | - | 
 | 
| ラベル | 画面の項目名 帳票の項目名 コメント ガイダンス | - | 
 | |
| (B) | ダイアログ | 確認メッセージ | info | 
 | 
| (C) | 結果メッセージ | 正常終了 | info | 
 | 
| (D) | 警告 | warn | 
 | |
| (E) | 単項目チェックエラー | error | 
 | |
| (F) | 相関チェックエラー | error | 
 | |
| (G) | 業務エラー | error | 
 | |
| (H) | システムエラー | error | 
 | 
4.7.1.3. メッセージID体系¶
- メッセージ変更時に、ソースコードを修正することなくメッセージを変更するため 
- メッセージの出力箇所を特定しやすくするため 
- 国際化に対応できるため 
メッセージIDの決め方は、メンテナンス性向上のため、規約を作って統一することを強く推奨する。
4.7.1.3.1. タイトル¶
- フォーマット - 接頭句 - 区切り - 業務名 - 区切り - 画面名 title.nnn*.nnn*
- 記述内容 - 項目 - 位置 - 内容 - 備考 接頭句1-5桁目 (5桁)“title” (固定)業務名可変長:任意spring-mvc.xmlで定義したviewResolverのprefixの下のディレクトリ(JSP/HTMLの上位ディレクトリ)画面名可変長:任意JSP名/HTML名ファイル名が”aaa.jsp”や”aaa.html”の場合”aaa”の部分
- 定義例 - # "/WEB-INF/views/admin/top.jsp"の場合 title.admin.top=Admin Top # "/WEB-INF/views/staff/createForm.html"の場合 title.staff.createForm=Staff Register Input 
Tip
本例は、テンプレートレイアウトを利用する場合に有効である。詳細はThymeleafにおける画面レイアウトを参照されたい。
テンプレートレイアウトを利用しない場合は、次に説明するラベルの規約を利用しても良い。
4.7.1.3.2. ラベル¶
画面のラベル、帳票の固定文言に使用する、メッセージIDの決め方について説明する。
- フォーマット - 接頭句 - 区切り - プロジェクト区分 - 区切り - 業務名 - 区切り - 項目名 label.xx.nnn*.nnn*
- 記述内容 - 項目 - 位置 - 内容 - 備考 接頭句1-5桁目 (5桁)“label” (固定)プロジェクト区分7-8桁名 (2桁)プロジェクト名のアルファベット2桁表記業務名可変長:任意項目名可変長:任意ラベル名、説明文名- Note - 入力チェックエラーのメッセージに項目名(論理名)を含める場合は、 - フォームのモデル名 + “.” + フィールド名 - staffForm.staffName = Staff name 
- フィールド名 - staffName = Staff name 
 - にする必要がある。 
- 使用例 - # スタッフ登録画面のフォームの項目名 # プロジェクト区分=em (Event Management System) label.em.staff.staffName=Staff name # ツアー検索画面に表示する説明文の場合 # プロジェクト区分=tr (Tour Reservation System) label.tr.tourSearch.tourSearchMessage=You can search tours with the specified conditions. - Note - プロジェクトが複数存在する場合に、メッセージIDが重複しないようにプロジェクト区分を定義する。 - 単一プロジェクトの場合でも、将来を見据えてプロジェクト区分を定義することを推奨する。 
4.7.1.3.3. 結果メッセージ¶
4.7.1.3.3.1. 業務間で共通して使用するメッセージ¶
同一メッセージを定義しないように、複数の業務間で共有するメッセージについて説明する。
- フォーマット - メッセージタイプ - 区切り - プロジェクト区分 - 区切り - 共通メッセージ区分 - 区切り - エラーレベル - 連番 x.xx.fw.9999
- 記述内容 - 項目 - 位置 - 内容 - 備考 メッセージタイプ1桁目 (1桁)info : iwarn : werror : eプロジェクト区分3-4桁目 (2桁)プロジェクト名のアルファベット2桁表記共通メッセージ区分6-7桁目 (2桁)“fw” (固定)エラーレベル9桁 (1桁)0-1 : 正常メッセージ2-4 : 業務エラー(準正常)5-7 : 入力チェックエラー8 : 業務エラー(エラー)9 : システムエラー連番10-12桁目 (3桁)連番で利用する(000-999)メッセージ削除となっても連番は空き番として、削除しない
- 使用例 - # 登録が成功した場合(正常メッセージ) i.xx.fw.0001=Registered successfully. # サーバリソース不足 w.xx.fw.9002=Server busy. Please, try again. # システムエラー発生の場合(システムエラー) e.xx.fw.9001=A system error has occurred. 
4.7.1.3.3.2. 各業務で個別に使用するメッセージ¶
業務で個別に使用するメッセージについて説明する。
- フォーマット - メッセージタイプ - 区切り - プロジェクト区分 - 区切り - 業務メッセージ区分 - 区切り - エラーレベル - 連番 x.xx.xx.9999
- 記述内容 - 項目 - 位置 - 内容 - 備考 メッセージタイプ1桁目 (1桁)info : iwarn : werror : eプロジェクト区分3-4桁目 (2桁)プロジェクト名のアルファベット2桁表記業務メッセージ区分6-7桁目 (2桁)業務IDなど業務毎に決める2桁の文字エラーレベル9桁 (1桁)0-1 : 正常メッセージ2-4 : 業務エラー(準正常)5-7 : 入力チェックエラー8 : 業務エラー(エラー)9 : システムエラー連番10-12桁目 (3桁)連番で利用する(000-999)メッセージ削除となっても連番は空き番として、削除しない
- 使用例 - # ファイルのアップロードが成功した場合 i.xx.yy.0001={0} upload completed. # パスワードの推奨変更期間が過ぎている場合 w.xx.yy.2001=The recommended change interval of password has passed. Please change your password. # ファイルサイズが制限を超えている場合 e.xx.yy.8001=Cannot upload, Because the file size must be less than {0}MB. # データに不整合がある場合 e.xx.yy.9001=There are inconsistencies in the data. 
4.7.1.3.4. 入力チェックエラーメッセージ¶
入力チェックでエラーがある場合に出力するメッセージについては、エラーメッセージの定義を参照されたい。
Note
入力チェックエラーの出力場所に関する基本方針を、以下に示す。
単項目入力チェックエラーのメッセージは、対象の項目がわかるように項目の横に表示させる。
相関入力チェックエラーのメッセージは、ページ上部などにまとめて表示させる。
単項目チェックでもメッセージを項目の横に表示させにくい場合は、ページ上部に表示させる。その場合は、メッセージに項目名を含める。
4.7.2. How to use¶
4.7.2.1. プロパティファイルに設定したメッセージの表示¶
4.7.2.1.1. プロパティを使用する際の設定¶
メッセージ管理を行うorg.springframework.context.MessageSourceの実装クラスの定義を行う。
- applicationContext.xml - @Bean("messageSource") public MessageSource messageSource() { ResourceBundleMessageSource bean = new ResourceBundleMessageSource(); // (1) bean.setBasenames("i18n/application-messages"); // (2) return bean; } 
- applicationContext.xml - <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <!-- (1) --> <property name="basenames"> <!-- (2) --> <list> <value>i18n/application-messages</value> </list> </property> </bean> 
| 項番 | 説明 | 
|---|---|
| (1) | MessageSourceの定義。ここではResourceBundleMessageSourceを使用する。また、Bean名には必ず messageSourceを設定すること。messageSourceが見つからない場合、空のDelegatingMessageSourceがMessageSourceとして使用される。 | 
| (2) | 使用するメッセージプロパティの基底名を定義する。クラスパス相対で指定する。 この例では”src/main/resources/i18n/application-messages.properties”を読み込む。 | 
4.7.2.1.2. プロパティに設定したメッセージの表示¶
4.7.2.1.2.1. デフォルト(ISO-8859-1)¶
- application-messages.properties - ここでは、 - application-messages.propertiesにメッセージを定義する例を示す。- label.aa.bb.year=Year label.aa.bb.month=Month label.aa.bb.day=Day 
<spring:message>タグを用いて表示できる。<spring:message code="label.aa.bb.year" />
<spring:message code="label.aa.bb.month" />
<spring:message code="label.aa.bb.day" />
フォームのラベルと使用する場合は、以下のように使用すれば良い。
<form:form modelAttribute="sampleForm">
    <form:label path="year">
        <spring:message code="label.aa.bb.year" />
    </form:label>: <form:input path="year" />
    <br>
    <form:label path="month">
        <spring:message code="label.aa.bb.month" />
    </form:label>: <form:input path="month" />
    <br>
    <form:label path="day">
        <spring:message code="label.aa.bb.day" />
    </form:label>: <form:input path="day" />
</form:form>
上記で設定したメッセージは、Thymeleafのメッセージ式 #{} を用いて表示できる。
<span th:text="#{label.aa.bb.year}"></span>
<span th:text="#{label.aa.bb.month}"></span>
<span th:text="#{label.aa.bb.day}"></span>
フォームのラベルと使用する場合は、以下のように使用すれば良い。
<form th:object="${sampleForm}" method="post">
    <label for="year" th:text="#{label.aa.bb.year}">
    </label>: <input th:field="*{year}">
    <br>
    <label for="month" th:text="#{label.aa.bb.month}">
    </label>: <input th:field="*{month}">
    <br>
    <label for="day" th:text="#{label.aa.bb.day}">
    </label>: <input th:field="*{day}">
</form>
ブラウザで表示すると以下のように出力される。
 
Note
日本語などISO-8859-1で表現できない文字は、native2asciiコマンドでISO-8859-1に変換してからpropertiesファイルに設定する。または、次に記載する文字コードの指定を行うことで設定ができる。
4.7.2.1.2.2. 文字コードの指定¶
文字コードにUTF-8を指定することでpropertiesファイルに直接日本語等を設定することが出来る。
- application-messages.properties - label.aa.bb.year=年 label.aa.bb.month=月 label.aa.bb.day=日 
この場合、以下のように、ResourceBundleMessageSourceに読み込む文字コードを指定することでUTF-8として解釈される。
- applicationContext.xml - @Bean("messageSource") public MessageSource messageSource() { ResourceBundleMessageSource bean = new ResourceBundleMessageSource(); bean.setBasenames("i18n/application-messages"); bean.setDefaultEncoding("UTF-8"); return bean; } 
- applicationContext.xml - <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <!-- (1) --> <property name="basenames"> <!-- (2) --> <list> <value>i18n/application-messages</value> </list> </property> <property name="defaultEncoding" value="UTF-8" /> </bean> 
4.7.2.1.2.3. 国際化¶
ResourceBundleMessageSourceに複数のpropertiesファイルを指定することで対応する。src/main/resources/i18n
                    ├ application-messages.properties (デフォルトで使用する言語)
                    ├ application-messages_fr.properties (フランス語)
                    ├ ...
                    └ application-messages_ja.properties (日本語)
詳細は、国際化を参照されたい。
4.7.2.2. 結果メッセージの表示¶
org.terasoluna.gfw.common.message.ResultMessagesおよびorg.terasoluna.gfw.common.message.ResultMessageを提供している。| クラス名 | 説明 | 
|---|---|
| ResultMessages | 結果メッセージの一覧とメッセージタイプを持つクラス。 結果メッセージの一覧は List<ResultMessage>、メッセージタイプはorg.terasoluna.gfw.common.message.ResultMessageTypeインタフェースで表現される。 | 
| ResultMessage | 結果メッセージのメッセージID、または、メッセージ本文を持つクラス。 | 
<t:messagesPanel>タグも提供される。ResultMessageにメッセージIDを登録した場合、メッセージの解決にはorg.springframework.context.MessageSourceが使用される。org.springframework.context.MessageSourceの定義方法についてはプロパティを使用する際の設定を参照されたい。4.7.2.2.1. 基本的な結果メッセージの設定方法¶
ResultMessagesを生成して画面に渡し、結果メッセージを表示する方法を説明する。<t:messagesPanel>タグを使用して結果メッセージを表示する方法を説明する。<t:messagesPanel>をデフォルト設定で出力した際のHTMLを基に、ThymeleafのテンプレートHTMLを記述している。Tip
Thymeleafの各テンプレートHTML画面共通の機能として作成する方法については、汎用的なHTML部品の作成方法を参照されたい。
- Controller - ResultMessagesオブジェクトの生成方法、および画面へメッセージを渡す方法を示す。- application-messages.proertiesには、各業務で個別に使用するメッセージの例が定義されていることとする。 - package com.example.sample.app.message; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.terasoluna.gfw.common.message.ResultMessages; @Controller @RequestMapping("message") public class MessageController { @GetMapping public String hello(Model model) { ResultMessages messages = ResultMessages.error().add("e.xx.yy.9001"); // (1) model.addAttribute(messages); // (2) return "message/index"; } } - 項番 - 説明 (1)- ResultMessagesにメッセージタイプとメッセージIDを設定する。上記例では、メッセージタイプを”error”、メッセージIDを”e.xx.yy.9001”として- ResultMessageを設定している。- Note - メッセージIDを指定する場合は - ResultMessageオブジェクトの生成を省略できるが、省略しない場合の設定は以下となる。- ResultMessages.error().add(ResultMessage.fromCode("e.xx.yy.9001"));(2)- ResultMessagesをModelに追加する。属性名はデフォルトで- resultMessagesが設定される。
- View 
WEB-INF/views/message/index.jspを、以下のように記述する。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Result Message Example</title>
</head>
<body>
    <h1>Result Message</h1>
    <t:messagesPanel /><!-- (1) -->
</body>
</html>
| 項番 | 説明 | 
|---|---|
| (1) | <t:messagesPanel>タグをデフォルト設定で使用する。デフォルトでは、属性名が resultMessagesのオブジェクトを表示する。そのため、ContorollerでModelに ResultMessagesを設定する際に属性名を設定していない場合は属性名を指定する必要はない。 | 
WEB-INF/views/message/index.htmlを、以下のように記述する。
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"> <!-- (1) -->
<head>
<title>Result Message Example</title>
</head>
<body>
    <h1>Result Message</h1>
    <div th:if="${resultMessages} != null" class="alert"
        th:classappend="|alert-${resultMessages.type}|"> <!-- (2) -->
        <ul>
            <li th:each="message : ${resultMessages}"
                 th:text="${message.code} != null ? ${#messages.msgWithParams(message.code, message.args)} : ${message.text}"></li> <!-- (3) -->
        </ul>
    </div>
</body>
</html>
| 項番 | 説明 | 
|---|---|
| (1) | スタンダードダイアレクトが提供する属性を使用したとき、EclipseなどのIDEでの警告を抑止するため、ネームスペースを付与する。 | 
| (2) | 属性名が resultMessagesのオブジェクトがnullでないとき、<div>とその配下の要素が実行される。th:classappend属性は設定した値をもとからあるclass属性の値に追加するもので、ここでは可変であるメッセージタイプを設定している。この class属性とth:classappend属性によって、出力されるHTMLにおけるclass属性が構築される。 | 
| (3) | 属性名が resultMessagesのオブジェクトに格納されたmessage変数を、Thymeleafのメッセージ式#messagesを使用して繰り返し取得し、出力する。 | 
ブラウザで表示すると、以下のように出力される。
 
出力されるHTMLを以下に示す。
<div class="alert alert-error"><!-- (1) -->
  <ul><!-- (2) -->
    <li>There are inconsistencies in the data.</li><!-- (3) -->
  </ul>
</div>
| 項番 | 説明 | 
|---|---|
| (1) | メッセージタイプに対応して”alert-error”クラスが付与されている。デフォルトでは <div>タグのclassに”alert alert-[メッセージタイプ]”が付与される。 | 
| (2) | 結果メッセージのリストが <ul>タグで出力される。 | 
| (3) | メッセージIDに対応するメッセージが MessageSourceから解決される。 | 
出力されるメッセージはclassが付けられているにすぎないため、その見栄えは出力されたclassに合わせてCSSでカスタマイズする必要がある(後述する)。
Note
ResultMessages.error().add(ResultMessage.fromText("There are inconsistencies in the data."));というように、メッセージの本文をハードコードすることもできるが、保守性を高めるため、メッセージIDを使用してResultMessageオブジェクトを作成し、メッセージ本文はプロパティファイルから取得することを推奨する。
4.7.2.2.2. プレースホルダーを使用した結果メッセージの設定方法¶
以下のように、メッセージにプレースホルダーを指定することができる。
e.xx.yy.8001=Cannot upload, Because the file size must be less than {0}MB.
メッセージのプレースホルダに値を埋める場合は、次のようにaddメソッドの第二引数以降に設定する。
ResultMessages messages = ResultMessages.error().add("e.xx.yy.8001", 1024);
model.addAttribute(messages);
この場合、以下のようなHTMLが出力される。
<div class="alert alert-error">
  <ul>
    <li>Cannot upload, Because the file size must be less than 1,024MB.</li>
  </ul>
</div>
Note
ResourceBundleMessageSourceはメッセージを生成する際にjava.text.MessageFormatを使用するため、1024はカンマ区切りで1,024と表示される。カンマが不要な場合は、プロパティファイルには以下のように設定する。
e.xx.yy.8001=Cannot upload, Because the file size must be less than {0,number,#}MB.
詳細は、Javadocを参照されたい。
4.7.2.2.3. 複数の結果メッセージの設定方法¶
以下のように、複数の結果メッセージを設定することもできる。
ResultMessages messages = ResultMessages.error()
    .add("e.xx.yy.9001")
    .add("e.xx.yy.8001", 1024);
model.addAttribute(messages);
この場合は、次のようなHTMLが出力される(JSP/テンプレートHTMLの変更は不要である)。
<div class="alert alert-error">
  <ul>
    <li>There are inconsistencies in the data.</li>
    <li>Cannot upload, Because the file size must be less than 1,024MB.</li>
  </ul>
</div>
4.7.2.2.4. メッセージタイプの変更方法¶
| メッセージタイプ | 
 | デフォルトで出力されるclass名 | 分類 | 使い分け(例) | 
|---|---|---|---|---|
| success | ResultMessages.success() | alert alert-success | info | 成功メッセージやポジティブな通知。 | 
| info | ResultMessages.info() | alert alert-info | info | 一般的な情報提供。 | 
| primary | ResultMessages.primary() | alert alert-primary | info | 主要な情報や重要な通知。 | 
| secondary | ResultMessages.secondary() | alert alert-secondary | info | 補助的な情報やそれほど重要ではない通知。 | 
| warning | ResultMessages.warning() | alert alert-warning | warning | 注意喚起や警告メッセージ。 | 
| light | ResultMessages.light() | alert alert-light | warning | 軽いトーンの通知やメッセージ。 | 
| dark | ResultMessages.dark() | alert alert-dark | warning | 重いトーンの通知やメッセージ。 | 
| danger | ResultMessages.danger() | alert alert-danger | error | 深刻なエラーメッセージや重要な警告。 | 
| error | ResultMessages.error() | alert alert-error | error | 深刻なエラーメッセージや重要な警告。 errorはBootstrapのAlertsコンポーネントには含まれていない。 | 
メッセージタイプに応じてCSSを定義されたい。以下に、CSSを適用した場合の例を示す。
.alert {
  margin-bottom: 15px;
  padding: 10px;
  border: 1px solid;
  border-radius: 4px;
  text-shadow: 0 1px 0 #ffffff;
}
.alert-info {
  background: #ebf7fd;
  color: #2d7091;
  border-color: rgba(45, 112, 145, 0.3);
}
.alert-warning {
  background: #fffceb;
  color: #e28327;
  border-color: rgba(226, 131, 39, 0.3);
}
.alert-error {
  background: #fff1f0;
  color: #d85030;
  border-color: rgba(216, 80, 48, 0.3);
}
- ResultMessages.error().add("e.xx.yy.9001")を出力した例  
- ResultMessages.warning().add("w.xx.yy.2001")を出力した例  
- ResultMessages.info().add("i.xx.yy.0001", "XXXX")を出力した例  
Caution
本例では、メッセージIDをハードコードで設定しているが、保守性を高めるためにも、メッセージIDは定数クラスにまとめることを推奨する。
メッセージID定数クラスの自動生成ツールを参照されたい。
4.7.2.2.5. 結果メッセージの属性名指定¶
ResultMessagesをModelに追加する場合、属性名を省略することが出来る。ResultMessagesは一つのメッセージタイプしか表現できないため、1画面に異なるメッセージタイプのResultMessagesを同時に表示したい場合は、明示的に属性名を指定してModelに設定する必要がある。- Controller - @GetMapping(value = "showMessages") public String showMessages(Model model) { model.addAttribute("messages1", ResultMessages.warning().add("w.xx.yy.2001")); // (1) model.addAttribute("messages2", ResultMessages.error().add("e.xx.yy.9001")); // (2) return "message/showMessages"; } - 項番 - 説明 (1)メッセージタイプが”warning”である- ResultMessagesを、属性名”messages1”でModelに追加する。(2)メッセージタイプが”info”である- ResultMessagesを、属性名”messages2”でModelに追加する。
- WEB-INF/views/message/showMessages.jsp - <h1>Result Message</h1> <h2>Messages1</h2> <t:messagesPanel messagesAttributeName="messages1" /><!-- (1) --> <h2>Messages2</h2> <t:messagesPanel messagesAttributeName="messages2" /><!-- (2) --> - 項番 - 説明 (1)属性名が”messages1”である- ResultMessagesを表示する。(2)属性名が”messages2”である- ResultMessagesを表示する。
- WEB-INF/views/message/showMessages.html - <h1>Result Message</h1> <h2>Messages1</h2> <div th:if="${messages1 != null}" th:text="${messages1}" class="alert" th:classappend="|alert-${messages1.type}|" /><!-- (1) --> <h2>Messages2</h2> <div th:if="${messages2 != null}" th:text="${messages2}" class="alert" th:classappend="|alert-${messages2.type}|" /><!-- (2) --> - 項番 - 説明 (1)属性名が”messages1”である- ResultMessagesを表示する。(2)属性名が”messages2”である- ResultMessagesを表示する。
ブラウザで表示すると、以下のように出力される。
 
4.7.2.2.6. 業務例外メッセージの表示¶
org.terasoluna.gfw.common.exception.BusinessExceptionとorg.terasoluna.gfw.common.exception.ResourceNotFoundExceptionは内部でResultMessagesを保持している。ResultMessagesを設定したBusinessExceptionをスローすること。BusinessExceptionをキャッチし、例外中の結果メッセージをModelに追加する。- Serviceクラス - @Service @Transactional public class UserServiceImpl implements UserService { // omitted public void create(...) { // omitted... if (...) { // illegal state! ResultMessages messages = ResultMessages.error().add("e.ex.an.9001"); // (1) throw new BusinessException(messages); } } } - 項番 - 説明 (1)エラーメッセージを- ResultMessagesで作成し、- BusinessExceptionに設定する。
- Controllerクラス - @PostMapping(value = "create") public String create(@Validated UserForm form, BindingResult result, Model model) { // omitted try { userService.create(user); } catch (BusinessException e) { ResultMessages messages = e.getResultMessages(); // (1) model.addAttribute(messages); return "user/createForm"; } // omitted } - 項番 - 説明 (1)- BusinessExceptionが保持する- ResultMessagesを取得し、Modelに追加する。
通常、エラーメッセージ表示する場合は、ControllerでResultMessagesオブジェクトを作成するのではなく、こちらの方法を使用する。
4.7.3. How to extend¶
4.7.3.1. 独自メッセージタイプを作成する¶
org.terasoluna.gfw.common.message.ResultMessageTypeインタフェースを実装した独自メッセージタイプクラスを作成する。import org.terasoluna.gfw.common.message.ResultMessageType;
public enum ResultMessageTypes implements ResultMessageType { // (1)
    NOTICE("notice");
    private ResultMessageTypes(String type) {
        this.type = type;
    }
    private final String type;
    @Override
    public String getType() { // (2)
        return this.type;
    }
    @Override
    public String toString() {
        return this.type;
    }
}
| 項番 | 説明 | 
|---|---|
| (1) | ResultMessageTypeインタフェースを実装したEnumを定義する。定数オブジェクトで作成してもよいが、Enumで作成することを推奨する。 | 
| (2) | getTypeの返り値が出力されるCSSのclass名に対応する。 | 
ResultMessagesを作成する。ResultMessages messages = new ResultMessages(ResultMessageTypes.NOTICE) // (1)
        .add("w.xx.yy.2001");
model.addAttribute(messages);
| 項番 | 説明 | 
|---|---|
| (1) | ResultMessagesのコンストラクタに対象のResultMessageTypeを指定する。 | 
この場合、以下のようなHTMLが出力される。
<div class="alert alert-notice">
  <ul>
    <li>The recommended change interval has passed password. Please change your password.</li>
  </ul>
</div>
Tip
拡張方法は、org.terasoluna.gfw.common.message.StandardResultMessageTypeが参考になる。
4.7.4. Appendix¶
4.7.4.1. <t:messagesPanel>タグの属性変更¶
<t:messagesPanel>タグには、表示形式を変更する属性がいくつか用意されている。
| オプション | 内容 | defaultの設定値 | 
|---|---|---|
| panelElement | 結果メッセージ表示パネルの要素。 | 
 | 
| panelClassName | 結果メッセージ表示パネルのCSS class名。 | 
 | 
| panelTypeClassPrefix | CSS class名の接頭辞。 | 
 | 
| messagesType | メッセージタイプ。この属性が設定された場合。設定されたメッセージタイプが | デフォルト値なし | 
| outerElement | 結果メッセージ一覧を構成するHTMLの外側のタグ。 | 
 | 
| innerElement | 結果メッセージ一覧を構成するHTMLの内側のタグ。 | 
 | 
| disableHtmlEscape | HTMLエスケープ処理を無効化するためのフラグ。 trueを指定する事で、出力するメッセージに対してHTMLエスケープ処理が行われなくなる。この属性は、出力するメッセージにHTMLを埋め込むことで、メッセージの装飾などができるようにするために用意している。 Caution trueを指定する場合は、XSS対策が必要な文字がメッセージ内に含まれない事が保証されていること。 | 
 | 
以下に、属性変更の例を示す。
4.7.4.1.1. class名の変更¶
例えば、CSSフレームワークBlueTripでは以下のようなCSSが用意されている。
.error,.notice,.success {
    padding: .8em;
    margin-bottom: 1.6em;
    border: 2px solid #ddd;
}
.error {
    background: #FBE3E4;
    color: #8a1f11;
    border-color: #FBC2C4;
}
.notice {
    background: #FFF6BF;
    color: #514721;
    border-color: #FFD324;
}
.success {
    background: #E6EFC2;
    color: #264409;
    border-color: #C6D880;
}
<div class="error">...</div>というようにメッセージを出力する必要がある。<t:messagesPanel>タグを以下のように使用すればよい(Controllerは修正不要である)。<t:messagesPanel panelClassName="" panelTypeClassPrefix="" />
出力されるHTMLは以下のようになる。
<div class="error">
    <ul>
        <li>There are inconsistencies in the data.</li>
    </ul>
</div>
ブラウザで表示すると、以下のように出力される。
 
4.7.4.1.2. HTML Elementの変更¶
例えば、メッセージ一覧を表示するために<ul>タグを使用したくない場合は、outerElement属性とinnerElement属性を使用することでカスタマイズできる。
以下のように属性を設定した場合は、
<t:messagesPanel outerElement="" innerElement="span" />
次のようにHTMLが出力される。
<div class="alert alert-error">
    <span>There are inconsistencies in the data.</span>
    <span>Cannot upload, Because the file size must be less than 1,024MB.</span>
</div>
以下のようにCSSを設定することで、
.alert > span {
    display: block; /* (1) */
}
| 項番 | 説明 | 
|---|---|
| (1) | “alert”クラスの要素の子となる <span>タグをブロックレベル要素にする。 | 
ブラウザで次のように表示される。
 
4.7.4.1.3. HTMLエスケープ処理の無効化¶
trueにした場合、以下のような出力イメージにする事ができる。- jsp - <spring:message var="informationMessage" code="i.xx.yy.0001" /> <t:messagesPanel messagesAttributeName="informationMessage" messagesType="alert alert-info" disableHtmlEscape="true" /> 
- properties - i.xx.yy.0001 = Please confirm order content. <font style="color: red; font-size: 16px;">If this orders submitted, cannot cancel.</font> 
- 出力イメージ   
4.7.4.2. ResultMessagesを使用しない結果メッセージの表示¶
<t:messagesPanel>タグはResultMessagesオブジェクト以外にも
- java.lang.String
- java.lang.Exception
- java.util.List
オブジェクトも出力できる。
<t:messagesPanel>タグはResultMessagesオブジェクトの出力用に使用するが、org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandlerでは、認証エラー時に発生した例外オブジェクトをSPRING_SECURITY_LAST_EXCEPTIONという属性名で、リダイレクト時はセッション、フォワード時はリクエストスコープに格納する。<t:messagesPanel>タグで出力したい場合は、以下のように設定すればよい。<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Login</title>
<style type="text/css">
/* (1) */
.alert {
    margin-bottom: 15px;
    padding: 10px;
    border: 1px solid;
    border-radius: 4px;
    text-shadow: 0 1px 0 #ffffff;
}
.alert-error {
    background: #fff1f0;
    color: #d85030;
    border-color: rgba(216, 80, 48, 0.3);
}
</style>
</head>
<body>
    <c:if test="${param.containsKey('error')}">
        <t:messagesPanel messagesType="error"
            messagesAttributeName="SPRING_SECURITY_LAST_EXCEPTION" /><!-- (2) -->
    </c:if>
    <form:form
        action="${pageContext.request.contextPath}/authentication"
        method="post">
        <fieldset>
            <legend>Login Form</legend>
            <div>
                <label for="username">Username: </label><input
                    type="text" id="username" name="username">
            </div>
            <div>
                <label for="username">Password:</label><input
                    type="password" id="password" name="password">
            </div>
            <div>
                <input type="submit" value="Login" />
            </div>
        </fieldset>
    </form:form>
</body>
</html>
| 項番 | 説明 | 
|---|---|
| (1) | 結果メッセージ表示用のCSSを再掲する。実際はCSSファイルに記述することを強く推奨する。 | 
| (2) | Exceptionオブジェクトが格納されている属性名をmessagesAttributeName属性で指定する。また、 ResultMessagesオブジェクトとは異なり、メッセージタイプの情報をもたないため、messagesType属性で、明示的に、メッセージタイプを指定する必要がある。 | 
ResultMessagesオブジェクト以外にも、フレームワークがリクエストスコープに設定した文字列(エラーメッセージなど)を表示することができる。
ResultMessagesを使用したときとと同様に出力したい場合は、以下のように設定すればよい。<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org"> <!-- (1) -->
<head>
<title>Login</title>
<style type="text/css">
/* (2) */
.alert {
    margin-bottom: 15px;
    padding: 10px;
    border: 1px solid;
    border-radius: 4px;
    text-shadow: 0 1px 0 #ffffff;
}
.alert-error {
    background: #fff1f0;
    color: #d85030;
    border-color: rgba(216, 80, 48, 0.3);
}
</style>
</head>
<body>
    <div th:if="${!param.keySet().contains('error')}" th:remove="tag">
        <div th:if="${SPRING_SECURITY_LAST_EXCEPTION != null}" class="alert alert-error" /> <!-- (3) -->
            <ul>
                <li th:text="${SPRING_SECURITY_LAST_EXCEPTION.message}"></li>
            </ul>
        </div>
    </div>
    <form th:action="@{/authentication}" method="post">
        <fieldset>
            <legend>Login Form</legend>
            <div>
                <label for="username">Username: </label><input
                    id="username" name="username">
            </div>
            <div>
                <label for="password">Password:</label><input
                    type="password" id="password" name="password">
            </div>
            <div>
                <input type="submit" value="Login">
            </div>
        </fieldset>
    </form>
</body>
</html>
| 項番 | 説明 | 
|---|---|
| (1) | スタンダードダイアレクトが提供する属性を使用したとき、EclipseなどのIDEでの警告を抑止するため、ネームスペースを付与する。 | 
| (2) | 結果メッセージ表示用のCSSを再掲する。実際はCSSファイルに記述することを強く推奨する。 | 
| (3) | Exceptionオブジェクトが格納されている属性名をThymeleafの変数式${}に指定する。また、 ResultMessagesオブジェクトとは異なり、メッセージタイプの情報をもたないため、class属性でalert alert-errorを明示的に指定している。 | 
認証エラー時に出力されるHTMLは
<div class="alert alert-error"><ul><li>Bad credentials</li></ul></div>
であり、ブラウザでは以下のように出力される。
 
Tip
ログイン用のJSPの内容については、認証を参照されたい。
4.7.4.3. メッセージID定数クラスの自動生成ツール¶
- メッセージID定数クラスの作成 
まず空のメッセージID定数クラスを作成する。ここでは
com.example.common.message.MessageIdsとする。package com.example.common.message; public class MessageIds { }
- 自動生成クラスの作成 
次に
MessageIdsクラスと同じパッケージにMessageIdsGenクラスを作成し、以下のように記述する。package com.example.common.message; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; import org.springframework.core.io.ClassPathResource; public class MessageIdsGen { public static void main(String[] args) throws IOException { // message properties file Class<?> targetClazz = MessageIds.class; Path output = Path.of("src/main/java", targetClazz.getName().replace(".", "/") + ".java"); try (InputStream inputStream = new ClassPathResource("i18n/application-messages.properties").getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); PrintWriter pw = new PrintWriter(Files.newBufferedWriter(output))) { pw.println("package " + targetClazz.getPackageName() + ";"); pw.println(""); pw.println("/**"); pw.println(" * Message Id"); pw.println(" */"); pw.println("public class " + targetClazz.getSimpleName() + " {"); String line = null; while ((line = bufferedReader.readLine()) != null) { String[] vals = line.split("=", 2); if (vals.length > 1) { String id = vals[0].trim(); String value = vals[1].trim(); pw.println(" /** " + id + "=" + value + " */"); pw.println(" public static final String " + id.toUpperCase().replace(".", "_").replace("-", "_") + " = \"" + id + "\";"); } } pw.println(""); pw.println(" private " + targetClazz.getSimpleName() + "() {}"); pw.println("}"); } } }
- メッセージプロパティファイルの用意 
src/main/resource/i18m/application-messages.propertiesにメッセージを定義する。ここでは例として、以下のように設定する。
i.xx.yy.0001={0} upload completed. w.xx.yy.2001=The recommended change interval has passed password. Please change your password. e.xx.yy.8001=Cannot upload, Because the file size must be less than {0}MB. e.xx.yy.9001=There are inconsistencies in the data.
- 自動生成クラスの実行 

MessageIdsクラスが、以下のように上書きされる。package com.example.common.message; /** * Message Id */ public class MessageIds { /** i.xx.yy.0001={0} upload completed. */ public static final String I_XX_YY_0001 = "i.xx.yy.0001"; /** w.xx.yy.2001=The recommended change interval has passed password. Please change your password. */ public static final String W_XX_YY_2001 = "w.xx.yy.2001"; /** e.xx.yy.8001=Cannot upload, Because the file size must be less than {0}MB. */ public static final String E_XX_YY_8001 = "e.xx.yy.8001"; /** e.xx.yy.9001=There are inconsistencies in the data. */ public static final String E_XX_YY_9001 = "e.xx.yy.9001"; }
