React.js で作る"Hello React" からのToDoアプリを作成する~その1:取りあえず"Hello React"~

※本稿は開発環境にnpm が入っていることを前提とする。

【Step.1】下ごしらえ
任意のフォルダを作成し、その下にpackage.json を下記の内容で配置する。

{
  "name": "devtodo",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "start": "http-server",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "MIT",
  "dependencies": {
    "http-server": "^0.7.4"
  }
}

本ディレクトリ直下にsrcフォルダとdistフォルダも作成しておく。

【Step.2】必要なツールを色々とインストール(npmでサクッと)
上記フォルダの下において、npm innstall を実行し、ツール類(ReactとかBrowserifyとかHTTPサーバーとか)をインストールする。

npm install
npm install -g browserify
npm install react
npm install reactify

【Step.3】 Hello World!!の作成
PJフォルダ直下にindex.htmlを作成する。

<html>
  <head>
  </head>
  <body>
    <div id="app-container"></div>
    <script src="dist/todoapp.js"></script>
  </body>
</html>

【Step.4】 コンポーネントの作成
srcフォルダ以下にtodoapp.jsx.js というファイル名で、
TodoApp コンポーネントを下記の通り作成する。

var React = require('react');

var TodoApp = React.createClass({
    render: function(){
      return (
        <div>
          <h2>Hello React!!</h2>
        </div>
      );
    }
});

React.render(
  <TodoApp />,document.getElementById('app-container')
);

【Step.5】 ビルド by browserify
上記作成したら、PJフォルダ以下で下記のコマンドでビルドを実行し、
ビルド結果得られたjs をdistフォルダ以下に出力する。

browserify -t reactify src/todoapp.jsx.js -o dist/todoapp.js

【Step.6】 稼働確認
下記のコマンドでhttp-serverを起ち上げ、http://localhost:8080 へアクセスする。

npm start

Play Framework で作る簡易認証システム(その3: ユーザ情報の表示)

前回に引き続き簡易認証システムを作っていく。
現状のつぶやき機能では誰が投稿したのかどうかわからないし、現在入っているユーザが誰なのかも分からない。
そこで、ログインしているユーザ情報と投稿者の情報を画面に表示するようにしよう。
"/inputchat"のリクエストを処理するApplication.inputchat()メソッドを下記の通り変更する。
セッション情報から"username"の内容を取得して、それをView に渡す。

■Application.inputchat()メソッド

  @Security.Authenticated(models.Secured.class)
    public static Result inputchat() {
      	//フォームを定義
		Form<PostChatForm> postChatForm = new Form(PostChatForm.class);

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

		//セッション情報"username"を取得
		String username = session().get("username");

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

続いてViewを下記の通り編集する。
・コントローラからusernameを受け取る。
・フォームにおいて、hiddenでユーザ名を持たす。

■inputchat.scala.html

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

@import helper._

This resource is @message.

こんにちは♪ <br>
@username さん

@form(routes.Application.postchat()) {
	@inputText(postchatform("postMessage"))
	<input type="hidden" name="username" value= "@username" >
}

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

最後に上記フォームが投稿されたときに呼び出されるApplication.postchat()を編集する。
変更内容としては、フォームのusernameフィールドの内容をController 側で取得して
Grpchat オブジェクトに格納している点である。

■Application.postchat()

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

    	//フォームの内容をpostMessageに代入する。
    	String postMessage = postChatForm.get().getPostMessage();
    	String postUsername = postChatForm.get().getUsername();

    	//Grpchat オブジェクトをインスタンス化
    	Grpchat grpchatRecd = new Grpchat();

    	//Grpchat オブジェクトのmessageフィールドにフォームで取得した内容をセットする
    	grpchatRecd.setMessage(postMessage);

    	//Grpchat オブジェクトのusernameフィールドにフォームで取得した内容をセットする
    	grpchatRecd.setUsername(postUsername);

    	//テーブルにデータを登録
    	grpchatRecd.save();

        //return ok(postchat.render(postMessage));
		return redirect("/inputchat");
    }

ここまでできたら、
http://localhost:9000/inputchat
にアクセスして、grpchat テーブルのusernameカラムに投稿者のアカウント名が登録され、
投稿者、およびログインしているユーザ名が表示されることを確認しよう。

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である。