1. 概要
Groovy/GroovyTemplateを利用して実装されたサンプルアプリケーションです。
Groovy/GroovyTemplateのカスタマイズする方法はチュートリアル(カスタマイズ)Groovy/GroovyTemplateをご参照ください。
2. セットアップ
サンプルアプリケーションのセットアップ説明です。
もし、既にiPLASSのInstall and Runまたは開発環境の構築を実行できた方で、サンプルアプリケーションを動かしたい場合、以下の手順に従って実施してみてください。
それ以外の方はInstall and Runから実際に動かしてみることをお勧めします。
-
サンプルアプリケーションのzipファイルをダウンロードサイトからダウンロードします。
-
Install and Runの章で作成したTenant、または開発環境の構築の章でEclipse上で作成したTenant、どちらかを利用してWebアプリケーションを立ち上げて管理者IDとパスワードでログインします。
-
Admin Console
のPackaging
機能を利用してzipファイルをインポートします。ダウンロードした「iplass-sample-app-groovy-package.zip」ファイルを、AdminConsoleの「Packaging」ツールで取り込んでください。
デフォルトの設定で「import」ボタンを押下してください。
「iplass-sample-app-groovy-package.zip」ファイルにテナント情報が含まれているため、「import」ボタン押下後に以下の画面が表示されます。 チェックがついている項目は上書きされてしまうため、独自に設定した項目についてはチェックを外してimportしてください。
Importを実行すると「Log」パネルに処理状況が表示されます。処理が終了したタイミングでエラーが発生していないことを確認してください。
新たに作成したロールなどを反映させるために、メタデータのキャッシュを削除してください。
-
サンプルが全文検索機能(lucene)を利用しますので、service-configに書いてあるINDEXデータファイルの仮の保存場所をローカル環境の適当な場所に変えます。
<service> <interface>org.iplass.mtp.impl.fulltextsearch.FulltextSearchService</interface> <property name="useFulltextSearch" value="true" /> (1) <property name="maxRows" value="1000" /> <property name="throwExceptionWhenOverLimit" value="true"/> <!-- lucene利用 --> <class>org.iplass.mtp.impl.fulltextsearch.lucene.LuceneFulltextSearchService</class> <property name="directory" value="D:\tmp\lucene" /> (2) <property name="defaultOperator" value="AND" /> <property name="analyzer" value="org.apache.lucene.analysis.ja.JapaneseAnalyzer" /> <property name="indexWriterRAMBufferSizeMB" value="64.0"/> <property name="redundantTimeMinutes" value="10"/> <!-- BinaryReferenceのParse可能な最大文字数 --> <property name="binaryParseLimitLength" value="100000"/> </service>
1 useFulltextSearchをtrueに設定します。 2 INDEXファイル保存場所をローカルパスに設定します。 iPLAssのインストーラでWebアプリケーションを起動した場合、ホームディレクトリに作成された設定ファイルを更新してください。
%HOMEPATH%\.iplass\iplass-service-config.xml
EclipseからWebアプリケーションを起動した場合、プロジェクトフォルダにある設定ファイルを更新してください。
src/main/resources/mtp-service-config.xml
-
Admin Console
のEntityExplorer
でEntity Crawl
機能を利用してインデックスファイルを作り直します。ToolsのEntityExplorerを選択します。
全文検索を利用する設定になっている場合、「Entity Crawl」タブが表示されます。
A) 任意のEntityのみを対象としてクローリングしたい場合はリストのEntityにチェックをいれ、「Start Crawl」ボタンをクリックして下さい。
B) クローリング対象Entityを全てクローリングしたい場合は「Re Crawl All Entity」ボタンをクリックして下さい。この場合、チェックをいれていないEntityも全てが対象となります。
C) クローリングが完了しましたら、最新のINDEXデータを反映させるために、「Refresh」ボタンをクリックして下さい。
-
上記起動手順の実施が完了しましたら、サンプルアプリのグローバル設定を確認してください。
3. 機能
3.1. Top画面の作成
-
Layoutの利用
Top画面の構成は下図のようになっています。画面共通で利用するレイアウト用テンプレートdefaultLayout
とshippingLayout
を用意し、画面右部分はそれぞれの機能に併せて呼びだす形にしています。ここでは"top"テンプレートを呼び出して利用しています。レイアウト用テンプレート
-
テンプレートを読み込む側(この例だとdefaultLayoutテンプレート)では、読みこんだテンプレートを表示したい箇所に
<%renderContent(); %>
を記述します。
-
-
レイアウトアクション
-
"privilege execute"にチェックをいれて特権モードにすると、登録していないユーザーにも公開できます。
-
レイアウトテンプレートに関してはパーツとしての利用になる為"parts.not direct access"にチェックをいれます。
-
-
Top画面アクション
-
読み込まれる側(この例だとTop画面アクション)のテンプレートでレイアウトアクションを
defaultLayout
のアクションに指定しています。
-
3.2. Utility Class
ここではAdminConsoleで作成できるUtilityClassの利用方法を解説します。
-
CartBean
クラスを例として説明していきます。CartBean
はオレンジ色の部分で利用されています。※ワークフロー起動はEnterprise Editionの機能です。
-
UtilityClass作成の際には以下を実装するようにしてください。
UtilityClass名
samples.ec01.bean.CartBean
package samples.ec01.bean; (1) import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.validation.Valid; import javax.validation.constraints.Min; import org.hibernate.validator.constraints.NotBlank; import samples.ec01.utils.Consts; class CartBean implements Serializable { private static final long serialVersionUID = 8770008117231095046L; (2) // カートの商品リスト @Valid List<CartItem> cartItems = new ArrayList<CartItem>(); // カートの総額値段 long totalPrice = 0; // コンストラクタ public CartBean() { } // カートに商品を追加 synchronized public void addCartItem(String productId, long price) { for (CartItem item : this.cartItems) { // 商品が既に追加されていたらValueを1増加 if (item.productId == productId) { item.value = item.value + 1; totalPrice += price; return; } } // 商品が無かった場合には初期値1が入ったCartItemを追加 this.cartItems.add(new CartItem(productId)); totalPrice += price; } // カートの商品を削除 synchronized public void removeCartItem(String productId, long price) { for (int index = 0; index < this.cartItems.size(); index++) { CartItem item = this.cartItems.get(index); if (item.productId == productId) { this.cartItems.remove(index); totalPrice = totalPrice - price * item.value; return; } } } ----------------------------------------以下略----------------------------------------
1 package階層はUtilityClassを作成した階層と同一を指定します。 2 Serializableの実装(作成したクラスをSessionに格納する場合)serialVersionUIDを忘れずに定義してください。不整合を起こす原因となる可能性があります。 -
UtilityClassの利用 新たに商品をカートに追加するコマンドを例に、UtilityClassの利用法を説明します。
カートに入れるコマンド
Commandクラス
samples/ec01/cart/InputCartInfoCommand
import org.iplass.mtp.entity.LoadOption; import samples.ec01.bean.CartBean; (1) def productId = request.getParam("productId"); def cartBean = request.getSession().cartBean; (2) if (cartBean == null) { println "セッションにカートなし"; println "セッションに新しいカート作成"; cartBean = new CartBean(); request.getSession().cartBean = cartBean; } else { println "セッションにカートあり"; println ":総計:${cartBean.totalPrice}"; } def product = em.load(productId, "samples.ec01.products.Product", new LoadOption().localized()); def price = product.price; cartBean.addCartItem(productId, price); println ":総計2: ${cartBean.totalPrice}"; request.totalAmount = cartBean.getTotalAmount(); "SUCCESS";
1 UtilityClassで作成したクラスも他のクラスと同じように利用できます。 2 このようにSessionに格納して利用する場合には、Serializableを実装しないと不整合を起こす原因となる可能性があります。 -
補足 WebApiでの設定
Cartオブジェクトは同期をとって利用したいため"Synchronize on Session"を使用しています。
-
3.3. Bean/Bean Validation
iPLAssのBean/Bean Validation機能を利用する場合、チェックしたいBeanクラスのプロパティにアノテーションをつけることで、バリデーションが自動に実行されます。それに、バリデーション結果を画面に表示できます。
サンプルアプリの問い合わせ登録画面で入力された値に対してバリデーションが実行される機能を例として説明していきます。
-
必要なUtilityClassを作成しています。
AdminConsoleのUtilityClassの設定を開くます。-
カスタムのバリデーショングループ。
-
JavaBeanクラス。※ このサンプルではJavaライブラリに既存のバリデーションとカスタムのバリデーショングループを利用しています。
-
org.iplass.mtp.command.beanmapper.BeanParamMapper
のラッパークラスです。UtilityClass名
samples.ec01.utils.MapperSelector
package samples.ec01.utils; import org.iplass.mtp.command.beanmapper.BeanParamMapper; public abstract class MapperSelector { // CartBean Mapper private static final BeanParamMapper cartMapper = new BeanParamMapper().withValidation().enableAutoGrow() .whitelistPropertyNameRegex("cartItems\\[\\d*\\]\\.productId|cartItems\\[\\d*\\].value"); (1) // InquiryBean Mapper private static final BeanParamMapper inquiryMapper = new BeanParamMapper().withValidation() .whitelistPropertyNameRegex("^(mail|content|familyName(Kana)?|firstName(Kana)?)\$"); // UserBean Mapper private static final BeanParamMapper userMapper = new BeanParamMapper().withValidation() .whitelistPropertyNameRegex("^(userId|mail|familyName(Kana)?|firstName(Kana)?)\$"); // ShippingInfo Mapper private static final BeanParamMapper shippingInfoMapper = new BeanParamMapper().withValidation() .whitelistPropertyNameRegex("^(userId|mail|address|tel|familyName(Kana)?|firstName(Kana)?)\$"); public static select(String name) { if (name == "cartBean") { return cartMapper; } else if (name == "inquiryBean") { return inquiryMapper; } else if (name == "userBean"){ return userMapper; } else if (name == "shippingBean"){ return shippingInfoMapper; } else { return null; } } }
1 BeanParamMapperの初期化処理。
※ whitelistPropertyNameRegexメソッドにセット可能な項目の正規表現式を指定します。
-
-
バリデーションエラーメッセージを定義する
Bean Validationエラーメッセージはメッセージマネージャに定義し、多言語利用が可能です。 -
コマンドクラスでBean Validationを利用する
Command名
samples/ec01/inquiry/RegistInquiryCommand
import javax.validation.groups.Default; import org.iplass.mtp.command.beanmapper.BeanParamMapper; import org.iplass.mtp.entity.SelectValue; import org.iplass.mtp.web.template.TemplateUtil; import samples.ec01.bean.InquiryBean; import samples.ec01.bean.validator.group.JapaneseChecks; import samples.ec01.utils.MapperSelector; // 入力チェック def inquiryBean = new InquiryBean(); request.inquiryBean = inquiryBean; // 日本語専用"name_kana"取得フォーム if (TemplateUtil.getLanguage() == null || TemplateUtil.getLanguage() == "ja") { MapperSelector.select("inquiryBean").populate(inquiryBean, request.getParamMap(), Default.class, JapaneseChecks.class); (1) } else { MapperSelector.select("inquiryBean").populate(inquiryBean, request.getParamMap(), Default.class); (2) } def inquiry = inquiryBean.toEntity(); // 問い合わせステータス // 1 : 未対応 // 2 : 対応中 // 3 : 対応完了 // 4 : 終了 SelectValue inquiryStatus = new SelectValue("1"); inquiry.inquiryStatus = inquiryStatus; // 請求の登録 em.insert(inquiry); "SUCCESS";
1 多言語利用で「日本語」が選択されているまたは多言語利用の設定が取得できなかった場合、 samples.ec01.bean.validator.group.JapaneseChecks
グループとデフォルトグループに属する項目に対してバリデーションが実行されます。
※ populate(Object, Map, Class)メソッドはスレッドセーフですが、それ以外の delimiters(char, char, char)等の設定用メソッドがスレッドセーフではありません。 詳しい説明はorg.iplass.mtp.command.beanmapper.BeanParamMapper
のJavaDocを参照してください。2 多言語利用で「日本語」以外が選択されている場合、デフォルトグループに属する項目のみに対してバリデーションが実行されます。 -
GroovyTemplateファイル
Template名
samples/ec01/inquiry/registInquiry
<% bind("bean" : inquiryBean) { %> (1) <div class="form-group row"> ...... <div class="col-12 col-md-6 mt-3"> <div> <% bind("prop" : "familyNameKana") { %> (2) <label for="${name}" class="col-form-label label-hidden">${msg("samples/ec01/general", "samples.ec01.inquiry.regist.familyNameKana")}</label> <input type="text" class="form-control border rounded input-hint-visible" name="${name}" value="${value}" placeholder="${msg('samples/ec01/general', 'samples.ec01.inquiry.regist.familyNameKana')}"> (3) <small class="form-text text-danger"><% errors() %></small> (4) <% } %> </div> </div> ----------------------------------------以下略----------------------------------------
1 org.iplass.mtp.impl.web.template.groovy.BindContext
にBeanインスタンスをバインドします。2 org.iplass.mtp.impl.web.template.groovy.BindContext
にBeanインスタンスに格納されているプロパティ名と値をバインドします。autoDetectErrors=true(デフォルトがtrue)の場合、WebRequestConstants.EXCEPTIONをキーにMappingExceptionを取得し、MappingResultのインスタンスが自動解決されます。当該Bean、プロパティに紐付くエラーがバインドされます。3 バインドされたプロパティの名前と値をテキストボックスにバインドします。 4 バインドされたエラーメッセージを画面に出力します。 ※ 詳しい使い方について、
org.iplass.mtp.impl.web.template.groovy.WebGTmplBase
を参照してください。 -
動作確認
-
「姓」と「名」を空文字として登録しようとしたら、バリデーションエラーが発生することを画面から確認できます。
-
「セイ」と「メイ」に全角カタカナ以外の値を入れて登録しようとしたら、バリデーションエラーが発生することを画面から確認できます。
-
多言語利用で「英語」が選択された場合、英語のバリデーションエラーメッセージが表示されることを確認できます。
※ 英語用の画面にカタカナの「セイ」と「メイ」の入力項目がないので、日本語版のものと比べてレイアウトに少し違いがあります。
-
3.4. Entity
-
Groovy/GroovyTemplateを利用する場合、Java/JSP版サンプルアプリのようなMappingClassの作成は不要です。
-
Entity多言語対応をしています。
Admin Console部分の設定でEntity多言語対応をご参照してください。
3.5. ErrorUrlSelector
エラー画面Template制御スクリプトクラスです。
-
Tokenエラーを例として説明していきます。
Tenantの設定 → エラー画面Template制御Scriptを開きます。import org.iplass.mtp.auth.UserExistsException; import org.iplass.mtp.entity.EntityValidationException; import org.iplass.mtp.web.WebRequestConstants; import org.iplass.mtp.web.actionmapping.TokenValidationException; import samples.ec01.exception.SessionValueNotFoundException; def error = request."mtp.web.exception"; //ECサイト用 if (path != null && path.startsWith("samples/ec01")) { // TokenValidationException用 if (error instanceof TokenValidationException) { (1) return "samples/ec01/error/genericError"; // EntityValidationException用 } else if (error instanceof EntityValidationException) { return "samples/ec01/error/genericError"; // UserExistsException } else if (error instanceof UserExistsException) { return "samples/ec01/error/genericError"; // SessionValueNotFoundException } else if (error instanceof SessionValueNotFoundException) { return "samples/ec01/error/genericError"; // その他のエラー用 } else { return "samples/ec01/error/genericError"; } }
1 TokenValidationExceptionが発生した場合、"samples/ec01/error/genericError"テンプレートを呼び出します。 -
共通エラー画面
Template名
samples/ec01/error/genericError
----------------------------------------以上略---------------------------------------- <div class="row"> <div class="col-12"> <div class="border-top"></div> <nav class="breadcrumb all-breadcrumb"> <a class="breadcrumb-item text-primary" href="${tcPath()}/samples/ec01/top">${msg("samples/ec01/general", "samples.ec01.all.breadcrumb.home")}</a> <span class="breadcrumb-item active" >${msg("samples/ec01/general", "samples.ec01.error.title")}</span> </nav> </div> <div class="col-12"> <span class="h4">${msg("samples/ec01/general", "samples.ec01.error.title")}</span> <div class="mt-3"> <div class="alert alert-danger" role="alert"> <p class="mb-0"> <strong> <% Exception e = (Exception) request."mtp.auth.error"; if (e == null) { e = (Exception) request."mtp.web.exception"; } if (e instanceof LoginFailedException){ %> ${msg("samples/ec01/general", "samples.ec01.error.loginFailed.msg")} <% } else if (e instanceof UserExistsException) { %> ${msg("samples/ec01/general", "samples.ec01.error.userExists.msg")} <% } else if (e instanceof TokenValidationException) { %> ${msg("samples/ec01/general", "samples.ec01.error.token.msg")} (1) <% } else if (e instanceof EntityValidationException) { %> ${msg("samples/ec01/general", "samples.ec01.error.entityValidation.msg")} <% } else if (e instanceof SessionValueNotFoundException) { %> ${msg("samples/ec01/general", "samples.ec01.error.sessionValueNotFound.msg")} <% } else { %> ${msg("samples/ec01/general", "samples.ec01.error.system.msg")} <% } %> </strong> </p> </div> </div> <div class="col-12 mt-5 text-center"> <a class="btn btn-dark" href="${tcPath()}/samples/ec01/top">${msg("samples/ec01/general", "samples.ec01.error.return")}</a> </div> </div> </div>
1 TokenValidationExceptionが発生した場合、該当するエラーメッセージを表示します。
※${msg()}
は開発者ガイド開発者ガイド GroovyTemplateの関数の章を参照してください。メタデータとして定義されているメッセージ定義を出力します。 -
動作確認
Consoleに出力されたエラーログです。
14:48:55.550 [http-nio-8080-exec-14] DEBUG 26 907 samples/ec01/shipping/ConfirmShippingInfoCommand o.i.m.i.transaction.LocalTransaction - create new Transaction:org.iplass.mtp.impl.transaction.LocalTransaction@36460089 with readOnly=false, stacked:null 14:48:55.555 [http-nio-8080-exec-14] DEBUG 26 907 samples/ec01/shipping/ConfirmShippingInfoCommand o.iplass.mtp.transaction.Transaction - rollback transaction cause org.iplass.mtp.web.actionmapping.TokenValidationException: 不正な画面遷移が発生しました(一連の登録処理中にブラウザの戻るボタン等を押下してしまいますと正常に処理を継続できない場合があります)。:org.iplass.mtp.impl.transaction.LocalTransaction@36460089 org.iplass.mtp.web.actionmapping.TokenValidationException: 不正な画面遷移が発生しました(一連の登録処理中にブラウザの戻るボタン等を押下してしまいますと正常に処理を継続できない場合があります)。 at org.iplass.mtp.impl.web.interceptors.TokenInterceptor.doError(TokenInterceptor.java:84) at org.iplass.mtp.impl.web.interceptors.TokenInterceptor.intercept(TokenInterceptor.java:114) at org.iplass.mtp.impl.command.InvocationImpl.proceedCommand(InvocationImpl.java:115) at org.iplass.mtp.impl.web.actionmapping.WebInvocationImpl.proceedCommand(WebInvocationImpl.java:171) at org.iplass.mtp.impl.command.interceptors.TransactionInterceptor.lambda$intercept$0(TransactionInterceptor.java:34) at org.iplass.mtp.transaction.TransactionManager.doTransaction(TransactionManager.java:114) at org.iplass.mtp.transaction.Transaction.with(Transaction.java:303) at org.iplass.mtp.impl.command.interceptors.TransactionInterceptor.intercept(TransactionInterceptor.java:33) at org.iplass.mtp.impl.command.InvocationImpl.proceedCommand(InvocationImpl.java:115) at org.iplass.mtp.impl.web.actionmapping.WebInvocationImpl.proceedCommand(WebInvocationImpl.java:171) ----------------------------------------以下略----------------------------------------
3.6. ReportOutput(注文明細ダウンロード)
-
POIを用いてExcel形式の注文明細帳票の出力機能を実装しています。
-
Groovy版では、GroovyScriptを用いてTemplateのOutput Logicを実装しています。
-
帳票テンプレート
作成方法は開発者ガイド 帳票出力(Jasper/JXLS/POI)をご参照ください。 -
POI帳票出力機能の動作確認
-
管理用画面で注文検索一覧画面を開き、詳細リンクをクリックします。
-
「注文明細ダウンロード」ボタンをクリックします。
-
ダウンロードしたExcel帳票を確認します。
このサンプルでは、英語用の帳票テンプレートも別途用意しています。
-
3.7. Buildin GroovyTemplate関数
-
ここではBuildin GroovyTemplate関数を利用しています。開発者ガイド GroovyTemplateの関数を参照してください。
3.8. WebApiとの連携
Ajaxを利用することで、GroovyTemplateでWebApiと連携することが出来ます。一般消費者向け画面で全文検索処理を例として説明していきます。
-
開発者ガイドWebApiを参照してください。
以下はここで利用しているWebApiです。WebApi名
samples/ec01/search/fulltextSearch
-
WebApiを利用しているテンプレート
Template名
samples/ec01/search/search
----------------------------------------GroovyTemplateの部分略---------------------------------------- <script type="text/javascript"> function fullTextSearch() { var productName = \$("#productName").val(); var categoryOid = \$("#categoryList").attr("category-item-selected"); if(productName == "") { \$("#helpId").html("${msg('samples/ec01/general', 'samples.ec01.search.nokeyword')}"); return false; } var param = "{\"productName\":\"" + productName + "\",\"categoryOid\":\"" + categoryOid +"\"}"; \$.ajax({ type: "POST", contentType: "application/json", url:"${tcPath()}/api/samples/ec01/search/fulltextSearch", (1) dataType: "json", data: param, success: function(commandResult){ if (commandResult.exceptionType != null) { (2) alert("${msg('samples/ec01/general', 'samples.ec01.search.jsError')}"+ commandResult.exceptionType +"\\n"+commandResult.exceptionMessage); return; } if(commandResult.status == "SUCCESS"){ if(commandResult.defaultResult != null && commandResult.defaultResult.length > 0){ var resultHtml = ListSearchResult(commandResult.defaultResult, productName); (3) \$("#searchResultDiv").html(resultHtml); } else{ \$("#helpId").html("${msg('samples/ec01/general', 'samples.ec01.search.keyword')}: " + productName + ", " + "${msg('samples/ec01/general', 'samples.ec01.search.noResult')}"); } } } }); } function dropdownSelect(item){ var t = \$(item); var v = t.attr("category-item-value"); \$("#categoryList").text(t.html()).attr("category-item-selected", v); } function ListSearchResult(entities, productName){ (4) var yen = "${msg('samples/ec01/general', 'samples.ec01.all.yen')}"; var html = "<div class=\"col-12 mb-2\">"; html += " <h4>${msg('samples/ec01/general', 'samples.ec01.search.result')}" + productName + "</h4>"; html += "</div>"; for(var i =0; i < entities.length; i++){ var name = entities[i].name; var price = isNaN(entities[i].price)? "" : entities[i].price; var imageUrl = "${tcPath()}/samples/ec01/resource/bin?id=" + entities[i].productImg.lobId + "&type=productImg"; var detailUrl = "${tcPath()}/samples/ec01/product/detail?productId=" + entities[i].oid; html += "<div class=\"col-12 col-md-4\">"; html += " <div class=\"card border-light border-0\">"; html += " <a href=\"" + detailUrl + "\" class=\"h-100\">"; html += " <img class=\"card-img-top img-thumbnail img-fluid all-product-img\" src=" + imageUrl + " alt=\"" + name + "\">"; html += " </a>"; html += " <div class=\"card-body pt-md-1 text-center\">"; html += " <div>"; html += " <a href=\""+ detailUrl +"\" class=\"card-link text-dark\">" + name + "</a>"; html += " </div>"; html += " <div class=\"all-price\">"; html += " <span>" + price + "</span>" + yen; html += " </div>"; html += " </div>"; html += " </div>"; html += "</div>"; } return html; } </script> ----------------------------------------以下略----------------------------------------
1 WebApiによる検索処理を呼び出します。 2 検索処理でエラーが発生した場合、クライアント側での処理。 3 返された検索処理の結果を取得し、クライアント側の描画処理を呼び出します。 4 検索結果の描画処理。 -
動作確認
動作結果はJava/JSP版と同じなので、そちらの動作確認画面を参照してくださん。
3.9. セキュリティ対策
画面、処理、ともにオレンジ色の部分を例にセキュリティ対策を解説していきます。
ここでは会員登録を例としてiPLAssで利用できる以下のセキュリティ対策について説明します。
・ エスケープ機能 XSS対策 : ユーザーの入力内容を正常に画面表示させる
・ TokenCheck機能 CSRF対策/トランザクション重複起動対策 : 画面表示時に正常な画面遷移が行われているかをチェックする
会員登録処理の中でそれぞれの機能は下記のように利用されます。
-
$エスケープの導入 Templateの作成 入力された値を出力する会員情報確認画面にて、該当箇所に$エスケープを導入する
表示名 Template名 会員情報確認画面
samples/ec01/member/registConfirm
----------------------------------------以上略---------------------------------------- <div class="card col-12 bg-light"> <div class="card-body"> <div class="row mt-3 border-bottom"> <div class="col-12 col-md-4"> <span class="text-muted font-weight-bold">${msg("samples/ec01/general", "samples.ec01.member.regist.userId")}</span> </div> <div class="col-12 col-md-8">$h{userBean.userId}</div> (1) </div> <div class="row mt-3 border-bottom"> <div class="col-12 col-md-4"> <span class="text-muted font-weight-bold">${msg("samples/ec01/general", "samples.ec01.member.registConfirm.fullName")}</span> </div> <div class="col-12 col-md-3"> <span class="text-muted font-weight-bold">${msg("samples/ec01/general", "samples.ec01.member.regist.familyName")}</span> $h{userBean.familyName} (1) </div> <div class="col-12 col-md-3"> <span class="text-muted font-weight-bold">${msg("samples/ec01/general", "samples.ec01.member.regist.firstName")}</span> $h{userBean.firstName} (1) </div> </div> <div class="row mt-3 border-bottom"> <div class="col-12 col-md-4"> <span class="text-muted font-weight-bold">${msg("samples/ec01/general", "samples.ec01.member.registConfirm.fullNameKana")}</span> </div> <div class="col-12 col-md-3"> <span class="text-muted font-weight-bold">${msg("samples/ec01/general", "samples.ec01.member.regist.familyNameKana")}</span> $h{userBean.familyNameKana} (1) </div> <div class="col-12 col-md-3"> <span class="text-muted font-weight-bold">${msg("samples/ec01/general", "samples.ec01.member.regist.firstNameKana")}</span> $h{userBean.firstNameKana} (1) </div> </div> <div class="row mt-3 border-bottom"> <div class="col-12 col-md-4"> <span class="text-muted font-weight-bold">${msg("samples/ec01/general", "samples.ec01.member.regist.mail")}</span> </div> <div class="col-12 col-md-8">$h{userBean.mail}</div> </div> </div> </div> ----------------------------------------以下略----------------------------------------
1 今回は出力先がHTML形式であるため全て $h{変数名}
の形式でエスケープを行っている。開発者ガイドの記述形式の章を参照してください。 -
TokenCheckの導入
この機能では、iPLAssで実装されている正常に画面遷移してきたことを証明する"Token"というオブジェクトを利用します。不正な画面遷移を禁止するActionでTokenをチェックすることにより、正常な画面遷移が行われたかを判断します。-
Actionの設定
不正な画面遷移を禁止するActionでTokenをチェックする設定を行います。表示名 Action名 会員情報確認アクション
samples/ec01/member/confirmMemberInfo
Token Check
Not Check チェックを行わない Check チェックを行う
use Fixed Token
チェック→セッション単位に固定に払いだされるTokenをチェックする
consume a Token
true→Tokenは再利用されません。
not change the Token(exception occurs)
チェック→現在のTokenを再設定
この設定により直接この画面へ遷移するとエラーページが表示されます。
-
TemplateでTokenを作成
不正な画面遷移を禁止するActionへ遷移する画面でTokenを作成します。表示名 Template名 会員情報確認画面
samples/ec01/member/regist
<div class="row"> <div class="col-12"> <div class="border-top"></div> <nav class="breadcrumb all-breadcrumb"> <a class="breadcrumb-item text-primary" href="${tcPath()}/samples/ec01/top"> ${msg("samples/ec01/general", "samples.ec01.all.breadcrumb.home")} </a> <span class="breadcrumb-item active"> ${msg("samples/ec01/general", "samples.ec01.member.regist.title")} </span> </nav> </div> <div class="col-12"> <span class="h4">${msg("samples/ec01/general", "samples.ec01.member.regist.title")}</span> <form class="custom-form mt-3" action="${tcPath()}/samples/ec01/member/confirmMemberInfo" method="post"> <input type="hidden" name="_t" value="${token()}"> (1) <% bind("bean" : userBean, "mappingResult" : result) { %> <div class="form-group row"> <div class="col-12"> <div> <% bind("prop": "userId") { %> <label for="${name}" class="col-form-label label-hidden">${msg("samples/ec01/general", "samples.ec01.member.regist.userId")}</label> <input type="text" class="form-control border rounded input-hint-visible" name="${name}" value="${value}" placeholder="${msg('samples/ec01/general', 'samples.ec01.member.regist.userId')}"> <small class="form-text text-danger"><% errors() %></small> <% } %> </div> </div> ----------------------------------------以下略----------------------------------------
1 GroovyTemplate関数を利用してトークンを生成しています。開発者ガイド開発者ガイド GroovyTemplateの関数の章を参照してください。
-
3.10. 多言語対応テンプレート
-
言語別に同じURLで異なるレイアウトを表示させることが可能です。
日本語用お問合せ登録画面
英語用お問合せ登録画面
-
多言語用テンプレートの作成
テンプレート画面のMutilingualAttributeを開きAddボタンより日本語用・英語用テンプレートを作成します。
※指定した言語のテンプレートが存在しないときには、あらかじめ作成しているテンプレートが表示されます。
(Templateのsamples/01/inquiry/registInquiryを参照)
-
カタカナ「セイ」の入力項目を日本語用画面に表示させます。
-
カタカナ「メイ」の入力項目を日本語用画面に表示させます。
-
4. リソース定義
4.1. 多言語メッセージ
-
GroovyTemplateで特定のメッセージ定義(メタデータ)またはResourceBundleに定義された文字列を取得することができるので、言語別にメッセージ定義を作成することで多言語利用が可能です。
取得方法について、開発者ガイド GroovyTemplateの関数の${rs()}
と${msg()}
関数を参照してください。
サンプルではメタデータとして定義しています。Message名
samples/ec01/general
4.2. Bean Validationエラーメッセージ
-
言語別にメッセージ定義(メタデータ)を作成することで多言語利用が可能です。
Message名
ValidationMessages
-
メッセージにパラメータを利用することが可能です。
-
-
補足
※ サンプルではValidationエラーメッセージの定義を/message
の直下に入れています。もしカスタムのパスに入れたい場合、以下のようにservice-configにorg.iplass.mtp.impl.validation.ValidationService
の定義を上書きしなければならないです。<service> <interfaceName>org.iplass.mtp.impl.validation.ValidationService</interfaceName> <className>org.iplass.mtp.impl.validation.ValidationService</className> <property name="beanValidation"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator" /> <property name="messageInterpolator" className="org.iplass.mtp.impl.validation.bean.TenantContextMessageInterpolator"> <property name="messageInterpolatorFactory" className="org.iplass.mtp.impl.validation.bean.hibernate.HibernateMessageInterpolatorFactory"> <property name="resourceBundleLocator" className="org.iplass.mtp.impl.validation.bean.hibernate.MessageResourceBundleLocator"> <property name="bundleName" value="[カスタムのパス]/ValidationMessages" /> (1) </property> <property name="cachingEnabled" null="true" /> </property> </property> </property> </service>
1 Validationエラーメッセージ定義のパスを[カスタムのパス]/ValidationMessagesに変更します。
4.3. StaticResource
-
サンプルアプリではJavascript、css、fontファイルをZip形式の圧縮ファイルにしてメタデータとして登録しています。
StaticResource名
samples/ec01/resources
resources.zipファイルを解凍したディレクトリ構造が以下のようです。
resources ┣ scripts (1) ┗ styles (2) ┗ fonts (3)
1 javascriptリソースのフォルダ 2 cssリソースのフォルダ 3 fontリソースのフォルダ -
Actionの作成
上記の圧縮ファイルにエントリされたリソースファイルを指定するAction。
Action名
samples/ec01/resource/ref
-
GroovyTemplateでアクセスする例
<%@ page import="org.iplass.mtp.web.template.TemplateUtil"%> <!DOCTYPE html> <html lang="<%=TemplateUtil.getLanguage() %>"> <% def totalAmount = request.totalAmount; %> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Optional meta tags --> <meta name="keywords" content="キーワードその1,キーワードその2,キーワードその3,キーワードその4,キーワードその5" /> <meta name="description" content="ご自身のWebサイトの説明を記載します" /> <!-- Bootstrap CSS --> <link rel="stylesheet" href="${tcPath()}/samples/ec01/resource/ref/styles/bootstrap.min.css?cv=<%=TemplateUtil.getAPIVersion()%>"> (1) <link rel="stylesheet" href="${tcPath()}/samples/ec01/resource/ref/styles/open-iconic-bootstrap.min.css?cv=<%=TemplateUtil.getAPIVersion()%>"> (1) <link rel="stylesheet" href="${tcPath()}/samples/ec01/resource/ref/styles/bookstore.css?cv=<%=TemplateUtil.getAPIVersion()%>"> (1) </head> ----------------------------------------以下略----------------------------------------
1 "{コンテキストルート}/{テナント名}/resource/ref/"のURLで登録されたstatic Resourceをアクセスすることが出来ます。 ※ 詳しくは開発者ガイドStaticResourceの章を参照してください。