Play Framework で作る簡易認証システム(その2: ログイン機能の実装)

前回から引き続き、簡易認証システムシステムを開発していく。
今回は登録したユーザ情報に基づいてログインできるような機能を開発していく。

1. ログイン画面表示用URL"/login"のリクエストを処理するController の
まずは、下記の通りroutesから登録しよう。

■routes

GET     /login                      controllers.Application.login()

次にコントローラ(Application.login)を実装していくが、その前にFormクラスを用意しよう。
ログイン画面ではアカウント名(username)とパスワード(password)を持つのでForm クラスは下記となる。
ここで、validate()メソッドが定義されているが、ユーザ/パスワードの組み合わせでDBを参照し、
正しいかどうかを判断してくれるようになる。
このクラスがFormのコンストラクタに渡されると、実際にフォームをPOSTする前に、
このvalidate()メソッドを実行し、ユーザ/パスワードの組み合わせが正しいかどうかを判断してくれるのである。

■forms/LoginForm.java

package forms;

import play.data.validation.*;
import play.db.ebean.*;
import java.security.NoSuchAlgorithmException;
import models.*;

public class LoginForm {

	   @Constraints.Required
	   private String username;
	   @Constraints.Required
	   private String password;

	    public String getUsername() {
	        return username;
	    }

	    public void setUsername(String username) {
	        this.username = username;
	    }

	    public String getPassword() {
	        return password;
	    }

	    public void setPassword(String password) {
	        this.password = password;
	    }

	  public String validate() throws NoSuchAlgorithmException{
		if(authenticate(username,password)==null){
			return "Invalid user or password";
		}
		return null;
	  }

	  public static User authenticate(String username, String password) throws java.security.NoSuchAlgorithmException{
		Model.Finder<Long, User> find = new Model.Finder<Long, User>(Long.class, User.class);
			return find.where().eq("username", username).eq("password", password).findUnique();
	  }

}

続いて、"/login"にアクセスした際に呼び出されるコントローラ(Application.login())は下記の通りになる。

■Application.login()

public static Result login(){
	Form<LoginForm> loginForm = new Form(LoginForm.class);
        return ok(login.render(loginForm));
}

2. ログイン画面用のView の実装
シンプルに下記の通り。

@(loginForm: Form[forms.LoginForm])

@import helper._

@form(routes.Application.authenticate) {
	@inputText(userForm("username"))
	@inputText(userForm("password"))
	<br>
	<button id="submit" type="submit" value="Submit" >Login</button>
}

3. セッション情報の付与
上記2.のフォームをPOSTした際に呼び出すコントローラ authenticate メソッドを下記の通り実装する。

■Application.authenticate()

    public static Result authenticate() {
        Form<LoginForm> loginForm = form(LoginForm.class).bindFromRequest();
        if (loginForm.hasErrors()) {
            return badRequest(login.render(loginForm));
        } else {
        	session().clear();
            session("username", loginForm.get().getUsername());
            String returnUrl = ctx().session().get("returnUrl");
            if(returnUrl == null || returnUrl.equals("") || returnUrl.equals(routes.Application.login().absoluteURL(request()))) {
                returnUrl = "/inputchat";
            }
            return redirect(returnUrl);
        }
    }

ここでのポイントは下記2行だけで、ここで、セッション"username" にフォームに入力した値(username)を格納している。つまり、セッション情報を搭載しているだけである。

session().clear();
session("username", loginForm.get().getUsername());

3. 認証ロジックの実装
次に認証の肝であるクラスを実装する。models以下に下記クラスを準備する。

■models/Secured.java

package models;

import play.mvc.Http.Context;
import play.mvc.Result;
import play.mvc.Security.Authenticator;

public class Secured extends Authenticator {
	@Override
	public String getUsername(Context ctx){
		return ctx.session().get("username");
	}

	@Override
	public Result onUnauthorized(Context ctx){
		String returnUrl = ctx.request().uri();
		if(returnUrl == null){
			returnUrl="/";
		}
		ctx.session().put("returnUrl", returnUrl);
		return redirect(controllers.routes.Application.login());
	}

}

このクラスはAuthenticator を継承したもので、getUsernameとonUnauthorized という
二つのメソッドをOveride する必要がある。
開発者は、getUsernameメソッドで認証OKとする条件を実装する必要がある。
ここでは、セッションから"username" の値が取得できたら認証OKとしている。
また、onUnauthorizedメソッドには認証NGだった場合の処理を実装するが、"/login"へリダイレクトされていることが分かる。

3. アノテーションの追加
最後に閲覧制限をかける画面を指定するためにアノテーションをControllerに設定する。
Application.inputchat()メソッドに"@Security.Authenticated(models.Secured.class)" アノテーションを追加すればよい。これで"/inputchat" にアクセスした時、セッション情報"username"が載っていないとはじかれるようになる。
つまり、Securedクラスでセッション情報"username"を取得できた場合にのみ、指定のリクエスト"/inputchat"を受け入れられるようになる。

■Application.java

@Security.Authenticated(models.Secured.class)
public static Result inputchat() {
     //中略
}

Play Framework で作る簡易認証システム(その1: アカウント登録)

今回はPlay Framework で簡易認証システムを作っていく。
ログインした人だけ、つまりアカウントを持つ人だけにチャットの内容を見せるような仕組みを作りたいわけである。
何はともあれ、アカウント情報を保持するテーブルが必要だ。

1. アカウント情報テーブルの作成
models 以下に下記の通り、User.javaを作成しよう。アカウント情報として、id(自動採番), username(アカウント名), mail(メールアドレス), image(画像), password(パスワード)を持つようにする。

package models;

import javax.persistence.*;

import play.db.ebean.*;
import play.data.validation.Constraints.*;

@Entity
public class User extends Model {
	@Id
	public Long id;

	@Required
	public String username;

	@Email
	public String mail;

	public String image;

	@Required
	public String password;

	public static Finder<Long, User> find =
			new Finder<Long, User>(Long.class, User.class);

	@Override
	public String toString(){
		return ("[id:" + id + ", username:" + username + ", mail:" + mail +
				", tel:" + tel + "]");
	}

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUserame(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    public String getImage() {
        return image;
    }
}

2. ユーザ情報登録機能画面の開発(View の作成)
ユーザ情報を登録する画面を作っていく。本画面にアクセスするパスは"/signup" とするにで、routesを下記の通りとする。

GET     /signup                     controllers.Application.signup()

続いて本URL"/signup"をリクエストされたときに呼び出されるコントローラsignupメソッドを実装する。

■Application.java

	public static Result signup(){
		//Userクラスの各フィールド(id, username, mail...)と同じ属性を持つFormオブジェクトを生成
		Form<User> userForm = new Form(User.class);
		
		return ok(signup.render("Sign up",userForm));
	}

ここで、ちょっとした小技を使っている。

		//Userクラスの各フィールド(id, username, mail...)と同じ属性を持つFormオブジェクトを生成
		Form<User> userForm = new Form(User.class);

userForm というFormクラスをインスタンス化したオブジェクトを作成するが、このときコンストラクタの引数として"User.class"を渡している。
どういう意味かというと、「User.classの持つフィールドを属性として持つForm オブジェクトを作成してね」という意味である。
以前フォームを利用したときに"PostChatForm"というフォームクラスを用意してから、
コントローラでフォームを作成したが、今回はフォームクラスを定義していない。
今回ユーザから求めるインプットは、アカウント名, メールアドレス, 画像, パスワードとなるが、これはUserクラスのフィールドと同じで、フォームの内容とテーブルの内容が1:1(※)で対応している。このようなとき、つまりフォームの内容をそのままテーブルに登録したいときにこの小技が使えるのだ。便利なので覚えておいて欲しいのだが、これはあくまでも小技なので、通常通りForm クラスを定義してから実装してもよい。
(※)厳密には1:1で対応しなくてもよいのだが、詳細は今後順次触れていくことにする。

return ok(signup.render("Sign up",userForm));
||>

続いて、上記メソッドで呼び出されるView の実装をしていく。

3. Viewの実装

■views/signup.scala.html
>|scala|
@(msg: String, userForm: Form[User])


@form(routes.Application.register, 'class -> "form-horizontal", 'enctype -> "multipart/form-data") {
	@inputText(form1("username"))
	@inputText(form1("mail"))
	@inputText(form1("password"))
    <button id="submit" type="submit" value="Submit" class="btn btn-primary">Sing up</button>
}

4. 登録用コントローラ(Application.render())の実装
上記フォームを投稿した際に呼びだされるApplication.render()メソッドを実装する。
まずは、忘れずにroutesに登録しておこう。

■routes

GET     /register                     controllers.Application.register()

続いてController(Application.register())を実装する。

■controllers/Application.java

	public static Result register(){

		//投稿されたフォームの内容をuserFormオブジェクトとしてインスタンス化
		Form<User> userForm = new Form(User.class).bindFromRequest();
		//もし、フォームの内容にエラーが無いようであれば
		if(!userForm.hasErrors()){
			//エンティティモデルであるUser オブジェクトにフォームの内容を登録する。
			User userRecd = userForm.get();
			//レコードの登録
			userRecd.save();
			return redirect("/inputchat");
		}else{
			return ok(signup.render("ERROR",userForm));
		}
	}


特に特異なことはしていない。
・素直にフォームから値を取得して、Formオブジェクト化

Form<User> userForm = new Form(User.class).bindFromRequest();

・Userオブジェクトをインスタス化してオブジェクトを保存する。

User userRecd = userForm.get();
//レコードの登録
userRecd.save();

これができたら、ここまでの動作を確認してみよう。ユーザ登録したあと、MySQLのWorkbench を起動してレコードが登録されているかどうか確認して欲しい。
http://localhost:9000/signup

Play Framework でつぶやきアプリを開発(その3: 超簡易つぶやきアプリを完成させよう)

今回は、超簡易つぶやきアプリを完成させてみよう。
まずは、前回の記事でテーブルに投入したレコードを/inputchatにアクセスしたときに表示できようにしたいと思う。

1. レコードを取得する。
コントローラのApplication.inputchat()メソッドを下記の通り変更する。

    public static Result inputchat() {

    	//フォームを定義
		Form<PostChatForm> postChatForm = new Form(PostChatForm.class);
		
		//grpchatテーブルの内容をselectし、Listに格納する。
		List<Grpchat> grpchatAllRec = Grpchat.find.all();
		
        return ok(inputchat.render("InputChatForm",postChatForm, grpchatAllRec));
    }

前回の記事で説明を割愛したが、Grpchatのfindフィールドはこのように使う。
このfind フィールドによりレコード(MySQL)の内容をList化(Java)することができるのである。

		//grpchatテーブルの内容をselectし、Listに格納する。
		List<Grpchat> grpchatAllRec = Grpchat.find.all();

そして、このList化したオブジェクトを下記でView側に渡す。

        return ok(inputchat.render("InputChatForm",postChatForm, grpchatAllRec));

2. View側で表示する。
View 側ではController側から渡されたリストを受けて(grpchatList)、それらをfor文で回す(@for(chatrec <- grpchatList){***})
ようにすればOKだ。

@(message: String, postchatform: Form[forms.PostChatForm], grpchatList:List[Grpchat])

@import helper._

This resource is @message.

@form(routes.Application.postchat()) {
	@inputText(postchatform("postMessage"))
}

@for(chatrec <- grpchatList){
	@chatrec.message / @chatrec.postdate<br>
}

最後にController側をちょっと弄って、投稿した後に/inputchat にリダイレクトされるようにしておく。

■Application.postchat()

    public static Result postchat(){
        //中略
        //return ok(postchat.render(postMessage));
	return redirect("/inputchat");
    }


ここまでできたら、http://localhost:9000/inputchatにアクセスして、動作を確認してみよう。
超簡易的なチャット機能ができたはずだ。

Play Framework でつぶやきアプリを開発(その2: テーブルにデータを投入しよう)

現状のアプリケーション(http://localhost:9000/inputchat)は、フォームに入力した内容を
単にecho(オウム返し)するだけの機能である。
このオウム返しの内容を先ほど作成したgrpchat テーブルに登録するように改修していこう。
オウム返しアプリがつぶやきアプリに昇華する第一歩だ。

まずは、入力画面(/inputchat)でからフォームを入力してEnter を押下した際に呼び出されるコントローラのメソッドApplication.postchat()を下記の通り改修する。


■Application.java

package controllers;

import play.*;
import play.mvc.*;
import play.mvc.Http.Context;
import models.*;

import views.html.*;
import play.mvc.Http.MultipartFormData;
import play.data.*;
import static play.data.Form.*;
import java.util.*;
import forms.*;

public class Application extends Controller {

    public static Result index() {
        return ok(index.render("Your new application is ready."));
    }

    public static Result inputchat() {

    	//フォームを定義
		Form<PostChatForm> postChatForm = new Form(PostChatForm.class);

        return ok(inputchat.render("InputChatForm",postChatForm));
    }

    public static Result postchat(){
    	//フォームの内容を取得して、PostChatFormクラスのインスタンスに格納する。
    	Form<PostChatForm> postChatForm = new Form(PostChatForm.class).bindFromRequest();

    	//フォームの内容をpostMessageに代入する。
    	String postMessage = postChatForm.get().getPostMessage();
    	
    	//Grpchat オブジェクトをインスタンス化
    	Grpchat grpchatRecd = new Grpchat(); 
    	
    	//Grpchat オブジェクトのmessageフィールドにフォームで取得した内容をセットする
    	grpchatRecd.setMessage(postMessage);
    	
    	//テーブルにデータを登録
    	grpchatRecd.save();
    	
    	
        return ok(postchat.render(postMessage));
    }


}

postchat()メソッドに下記3ステップを追加しただけである。

    	//Grpchat オブジェクトをインスタンス化
    	Grpchat grpchatRecd = new Grpchat(); 
    	
    	//Grpchat オブジェクトのmessageフィールドにフォームで取得した内容をセットする
    	grpchatRecd.setMessage(postMessage);
    	
    	//テーブルにデータを登録
    	grpchatRecd.save();

できたら、http://localhost:9000/inputchatにアクセスして、フォームを投稿してみよう。

投稿できたら、MySQLのWorkbenchを起動し、grpchat テーブルをselect してみよう。
すると、投稿した内容がレコードとして登録されているはずだ。
一点疑問に思ってほしいのだが、grpchatのidフィールドに数値がセットされていると思う。
値はmessage以外セットしていないのに、なぜだろう?

実はこれもEbean の機能の一つとなる。
Grpchat.javaを見てみると、idフィールドに@Idというアノテーションが付与されているのが分かる。
つまり、このアノテーションがあると、レコードが挿入される度に自動的に値を採番してくれるようになる、MySQLのAUTO_INCREMENTオプションが付与された形でCREATE文を発行してくれるのである。

このようにEbean の機能によりMySQLJava オブジェクトがシームレスに同期されていることが分かるだろうか。

Play Framework でつぶやきアプリを開発(その1: Ebean を使う)

前回までの記事(↓)の内容をベースにつぶやきアプリ(超簡易版T○itter的なサムシング)を開発してみよう。
超簡易版と言ってもPlay Framework で開発する上でのノウハウが詰まっているので、非常に教育的なテーマだ。


Play Framework で「Hello World!!」 - hirahiro56のブログ


Play Framework でフォームを扱う(オウム返しアプリを作ろう) - hirahiro56のブログ


Play Framework でMySQL を利用する(環境構築) - hirahiro56のブログ

1. MySQL との連携設定
まずは、前回の記事で構築したMySQL 環境と、Play Framework のプロジェクト(chatapp)との間の連携設定をしていこう。
詳細は割愛するが、まずはMySQL のサイトからJDBCドライバ(Connecotr/J)をダウンロードしてくる。「mysql-connector-java-5.1.32-bin.jar」

2. jar の配置
次にプロジェクトフォルダ直下にlib フォルダを作成(chatapp => lib)する。
そして、そのフォルダに「mysql-connector-java-5.1.32-bin.jar」を配置する。

3. application.conf へ接続文字列を追記
本設定ファイルに下記行を追加する。
※設定内容は前回の記事に従って設定している。
■conf/application.conf

db.default.driver=com.mysql.jdbc.Driver
db.default.url="jdbc:mysql://localhost:3306/chatapp"
db.default.user="chatapp"
db.default.password="password"
ebean.default="models.*"


4. テーブルを作る。
「Create Table だ!!」といいたいところだが、Play Framework の場合Ebean というORマッパーというものが使える。
Ebean について詳細は説明しないが、ここでは、「Java でデータベース連携するアプリを作る時、RDBMSの世界とJavaオブジェクトの世界との間を繋いでくれる凄いヤツ」という理解でOKだ。
Create Table の代わりにappフォルダ以下にmodels パッケージを新規作成し、その中にGrpchat.java というクラスを下記の通り作成してもらいたい。
内容は後ほど説明するので、取りあえずコピペすればよい。

■Grpchat.java

package models;

import java.util.Date;
import java.util.List;
import javax.persistence.*;
import javax.validation.*;

import com.avaje.ebean.annotation.*;

import play.db.ebean.*;
import play.db.ebean.Model.Finder;
import play.data.validation.*;
import play.data.validation.Constraints.Required;

@Entity
public class Grpchat extends Model {
	@Id
	public Long id;

	public String username;

	public String message;

	public String photo;

	public String groupid;

	@CreatedTimestamp
	public Date posdate;

	public static Finder<Long, Grpchat> find =
			new Finder<Long, Grpchat>(Long.class, Grpchat.class);

	@Override
	public String toString(){
		return ("[id:"+id+",username:"+username+",photo:"+photo+",message:"+message+",date:"+posdate);
	}
    public void setPhoto(String photo) {
        this.photo = photo;
    }
    public String getUsername() {
        return this.username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
    public String getPhoto() {
        return this.photo;
    }

    public String getGroupid() {
        return this.groupid;
    }

    public void setGroupid(String groupid) {
        this.groupid = groupid;
    }

    public void setMessage(String message){
    	this.message = message;
    }


}


上記クラスを配置したら、http://localhost:9000/inputchatにブラウザでアクセスしてみよう。すると、エラー画面「Database 'default' needs evolution!」とでるはずだ。
実はこのエラー画面はEabeanの仕業だ。
エラーの内容としては、grpchat というテーブルを作らないと、このクラスは使えませんぞ、
という意味となる。そしてgrpchat テーブルを作成するためのCREATE 文が表示されているわけだ。
よく見ると本画面の上段に「Apply this script now!」というボタンが表示されていると思う。これを押下してみよう。
すると、エラーメッセージが出なくなるはずだ。
次にMySQL のWorkbenchを起ち上げてみて欲しい。

chatapp ユーザで接続して、SHEMAS => chatapp => Tables 以下を見てみると。
何と驚くなかれ、grpchat というテーブルが作成されているのが分かる。

つまり、先ほどの「Apply this script now!」を押下した結果、MySQL 側でCREATE 文が発行されたのである。
その結果、Grpchatというオブジェクトに対応するテーブルが利用できるようになった。
これがEabean の機能なのである。
これにより、MySQL側のテーブルをJava オブジェクトとして自然にJava で開発できるのである。

では、先ほどのGrpchat.javaの内容を見ていこう。
内容は非常にシンプルで、id, username, message, grpid, photo, postdate といったフィールドとsetter とgetter メソッドを持つクラスである。
このフィールドをカラムに持つテーブルがMySQL側にCREATEされるのだが、grpchat テーブルをJava オブジェクト化したものという以外に何物でもない。

finder フィールドについては後ほど使うときになったら説明するが、テーブルからレコードを抽出するときに利用するフィールドと捉えればOKである。

Play Framework でMySQL を利用する(環境構築)

Play Framework でMySQL を利用するための環境を構築していく。

1. MySQL インストーラをダウンロードしてくる。

http://dev.mysql.com/downloads/mysql/
にアクセスし、
インストーラmysql-installer-community-5.6.22.0.msi」をダウンロードする。
(※)32-bit と書いてあるが、64-bit マシンであってもインストーラは同じ。

2. MySQL のインストール
(a) 上記msiファイルを実行する。
(b) License Agreement で同意して Next
(c) Setup Type ⇒ Full を選択
(d) Check Requirements でStatus にManual と記載されているもの以外を選択しExecute を押下
  ⇒ 必要なライブラリ一式をとってきてよしなにしてくれる。
(e) Installation でExecuteを押下
※いくつかセットアップに失敗すると思うが、特に気にせず進めてOK
(f) Configuration でNextを押下
(g) Type and Networking では、Development Machine を選択。後はデフォルトでOK。
(h) Accounts and Roles でroot(管理者) パスワードを設定する。
(i) Accounts and Roles でAdd User を押下してユーザ(一般ユーザ)を作成する。
  ユーザ名: chatapp
Role: DB Admin ※開発環境なので管理者権限を与えることとする。
(j) Windows Service で Next を押下
(k) Apply Server Configuration でExecute を押下
(l) Product Configuration でNext を押下
(m) Connect To Server でCheck ⇒ Next の順で押下
(n) Execute を押下

後は取りあえずNext とFinish を押下していけばOK。


3. スキーマを作成する。
先ほどchatapp というユーザを作成したが、このユーザが保有するスキーマを作成する。
MySQL Workbench を起ち上げて、root ユーザのコネクション(パネル)をダブルクリックする。
左側のNavigator のSCHEMASフィールドを右クリックして「Create Schema」を押下し、
Schema Nameを「chatapp」として、スキーマを作成すr。

4. コネクションの追加(接続設定)
最後に先ほど作成したchatappユーザ(スキーマ)向けのコネクション(接続設定)を追加する。
MySQL Workbench を起ち上げて、MySQL Connections のプラス(+)ボタンを押下する。
立ち上がった接続設定画面で、下記の通り設定する。
Connection Name: chatapp
Username: chatapp
Default Schema: chatapp

以上でMySQLのソフトウェアのセットアップは完了で、Workbench からchatapp ユーザでアクセスできるようになったはずだ。非常にEasy だったと思うがこれでいいのだ。

Play Framework でフォームを扱う(オウム返しアプリを作ろう)

前回の記事(↓)を踏まえて、今度は投稿フォームを利用したアプリ(オウム返しアプリ)をPlay Framework で作ってみる。

Play Framework で「Hello World!!」 - hirahiro56のブログ

作るアプリの機能としては、ユーザがフォームに入力した文字列をそのまま表示するだけのオウム返しアプリで実用性は無いものであるが、今後作るチャットアプリの基礎となるものである。

それでは、このオウム返しアプリを実現するのには、どの様なものを実装すれば良いのかを考えよう。
必要なものは下記の通りである。
つまり、入出力画面2つと、それぞれのリクエスト(URL)を処理するためのコントローラ2つの合計4つのモジュールを作成する必要がある。

・ユーザの入力を受け付けるView
・文字列を出力するView
・入力を受け付ける画面へアクセスする際のリクエスト(URL)を処理するコントローラ
・入力画面で入力内容を投稿した後に出力用のView へ画面遷移する際のリクエストを処理するコントローラ

早速順番に実装して行こう。実装の順番は私自身が実装する手順をそのまま記載することとする。

1. ルーティングの設定(routesファイルの設定)
ユーザの入力を受け付ける画面にアクセスするURLを"/inputchat" とし、routes ファイルに下記の通り記載しよう。

GET     /inputchat                  controllers.Application.inputchat()

この設定により、ユーザがブラウザでhttp://localhost:9000/inputchatのURLでアクセスすると、
"controllers.Application.inputchat()"が呼び出されるようになる。
次に、この"controllers.Application.inputchat()"を実装していく。

2. Controller の実装
controllers => Application.java を開いて下記の通りinputchat メソッドを実装する。

package controllers;

import play.mvc.Controller;
import play.mvc.Result;
import views.html.*; //要注意!!

public class Application extends Controller {
  //中略
    public static Result inputchat() {
        return ok(inputchat.render("InputChatForm"));
    }

}

Eclipse でエラーが出てしまうが、取りあえずは無視で良い。import 文を修正していることに注意。
因みにEclipse の機能でimport 文が勝手に修正されてしまう場合は、Eclipse のウィンドウ => 設定 => Java => 保管アクションの設定で、
インポートの編成のチェックを外しておけば良い。

続いて、"inputchat.render()" で呼び出されるView の実装に入る。

3. ユーザの入力を受け付けるViewの実装
HTML(Scala)ファイルが無ければ始まらないので、viewsパッケージ以下に「inputchat.scala.html」を作成し、
以下の通り編集する。取りあえずガワだけ用意する。

■inputchat.scala.html

@(message: String)

This resource is @message.

ここまでできたら、
http://localhost:9000/inputchat
にアクセスして、"This resource is InputChatForm."とブラウザ上に表示されればOKである。

次に、この画面(inputchat.scala.html)に入力フォームを配置してみよう。
そのためにはちょっとしたお作法があるので最初は慣れないと思うが、Play Framework 以外のフレームワークでも共通する作法となるので、
徐々に慣れていって欲しい。

4. フォームクラスを定義
Play Framework でフォームを利用する際(他のJava フレームワークでも同様だが)、フォームクラスというものを定義する。
なぜ、このようなクラスを定義するかというと、ブラウザ上のフォームの内容をJavaオブジェクトとして扱えるという利点があるのだが、
ピンとこない場合は「お作法なのね。」「便利なのね。」と軽い気持ちで理解しておけば、後々分かってくるであろう。

それでは早速フォームクラスを定義するのだが、app以下にformsというパッケージを作成し、
下記の通りユーザがインプットする内容をフィールド値として持つようなクラスを定義する。

■PostChatForm.java

package form;

import javax.persistence.*;
import java.util.*;
import play.data.validation.Constraints.*;
import play.data.validation.Constraints;

public class PostChatForm {
	public String postMessage;
	
	public void setPostMessage(String postMessage){
		this.postMessage = postMessage;		
	}
	
	public String getPostMessage(){
		return this.postMessage;		
	}

}

次にコントローラの中で下記の通り実装し、View へForm の型を通知する。

■Application.java

package controllers;

import play.*;
import play.mvc.*;
import play.mvc.Http.Context;
import models.*;

import views.html.*;
import play.mvc.Http.MultipartFormData;
import play.data.*;
import static play.data.Form.*;
import java.util.*;
import forms.*;

public class Application extends Controller {

    public static Result index() {
        return ok(index.render("Your new application is ready."));
    }

    public static Result inputchat() {

    	//フォームを定義
		Form<PostChatForm> postChatForm = new Form(PostChatForm.class);

        return ok(inputchat.render("InputChatForm",postChatForm));
    }

}


続いて、View 側で上記Controller から渡されたForm の型を受け取り、Play Framework のフォームヘルパーを用いて下記の通り実装する。

■inputchat.scala.html

@(message: String, postchatform: Form[forms.PostChatForm])

@import helper._

This resource is @message.

@form(routes.Application.index()) {
	@inputText(postchatform("postMessage"))
}

ここまでできたら、
http://localhost:9000/inputchat
へアクセスしてフォームが配置されていることを確認してみよう。
生成されたフォームのコードはブラウザのソースを表示から確認できるが、
下記コードによりフォーム用のHTMLを自動生成してくれていることが分かると思う。

@form(routes.Application.index()) {
	@inputText(postchatform("postMessage"))
}

5. フォーム内容表示画面の開発
まずは、上記作成したView を下記の通り書き換える。

@(message: String, postchatform: Form[forms.PostChatForm])

@import helper._

This resource is @message.

@form(routes.Application.postchat()) {
	@inputText(postchatform("postMessage"))
}

これにより、フォームを入力し投稿するとhttp://localhost:9000/postchatというURLでリクエストが飛ぶようになる。
それでは、このリクエスト"/postchat"に対する処理を実装していこう。
まずは、routesファイルを下記の通り記載する。
■routes

GET     /postchat                   controllers.Application.postchat()

次に、Application.java に、上記ルーティングに呼び出されるpoctchat()メソッドを追加する。
ここでのポイントは、FormクラスのbindFromRequest() でフォームの内容をインスタンス化するところである。フォームのname属性とFormクラスのフィールド名が同一であれば、フォームの値でフィールドを初期化されたFormクラスがインスタンス化される点である。
これは、今後多用するので覚えておきたい。

    public static Result postchat(){
    	//フォームの内容を取得して、PostChatFormクラスのインスタンスに格納する。
    	Form<PostChatForm> postChatForm = new Form(PostChatForm.class).bindFromRequest();
    	
    	//フォームの内容をpostMessageに代入する。
    	String postMessage = postChatForm.get().getPostMessage();

        return ok(postchat.render(postMessage));
    }

最後に、views以下に下記の投稿メッセージ出力用Viewを用意する。


■postchat.scala.html

@(postmessage:String)

@postmessage

ここまでできたら、下記URLにアクセスし、動作を確認してみよう。
フォームに入力した内容がそのままオウム返しされるはずだ。

http://localhost:9000/inputchat