本投稿は、2015/5/30開催のPHPカンファレンス関西2015 - 関西最大のPHPイベントのブーストラックで行われる弊社のセッション、AWSでのRESTfulアプリ開発ワークショップ実演(13:00-13:30)の内容を公開するものです。
メニュー
- 開発用サーバ作成
- 作ったサーバのPHPに関する設定は?
- GitHubからTODOアプリのソース取得
- 取得したソースの中身は?
- ソース詳細
- POSTMANでAPIの動作確認
作成する開発用サーバ
OSや基本的なソフトウェアがインストールされたマシンイメージをAWSではAMI(Amazon Machine Image)と呼びます。
今回は、弊社で作成したAMIを元にEC2インスタンスを作成します。
弊社がそのAMIを作成した手順については
AWS上で構築するRESTfulアプリ勉強会~Web開発ワークショップ~【第1回】の事前準備内容まとめ - Qiita
で公開しています。参考にしてください。
今回用に作成したAMIとはちょっと設定ファイルの記載内容などの違いは有ります。
AMIにインストール済みのプロダクト
予め下記プロダクトをインストール済みです。
今回の手順で開発環境を作成すればすぐに使用可能となります。
プロダクト | バージョン |
---|---|
apache | 2.4.10-1.59 |
php | 5.5.20-2.94 |
mysql | 5.6.14-3 |
phpMyAdmin | 4.0.10.7-1 |
git | 2.1.0 |
xdebug | 2.2.3-1 |
使用するツールなど、必要なもの
- awsアカウント
- 必須です!EC2インスタンスが作成できることを事前に確認しておくとベターです。
以下、別のツールでも代替できますが、説明は下記ツールをベースに行います。
- chromeブラウザ
-
postman
- 作成したAPIのテストツールchromeブラウザの拡張機能です。
- 似たようなツールはたくさんありますので、愛用のものがあればそちらで結構です。
-
ターミナルソフトウェア
- Mac, Linuxの方は標準のターミナルでよいです。
- Windowsの方は、別途ターミナルソフトウェアをご準備ください。
- 当資料では、Macの標準ターミナルと、Windows上のputtyでの実行例を併記しています。
- 事前にPuTTY Download Pageよりインストールしておいてください(インストーラでインストールしたくない場合はputty.zipをダウンロードしてすきなところに解凍すればOKです。
- 当資料では、Macの標準ターミナルと、Windows上のputtyでの実行例を併記しています。
開発用サーバ作成
では、鈴木商店にて事前準備済みのAMIから以下の手順でEC2インスタンスを作成します。
1. AWSマネージメントコンソールにログイン
2. 元にするAMI(マシンイメージ)を検索
下記手順で当該のAMIを表示させます。
下記AMIの部分をクリックします。
次に、下記の通り操作します。
リージョンが東京になっていることを確認。
画面上部右端にリージョンの表示があります(東京になっている場合の図)。
東京になっていない場合、下図のように東京リージョンを選択してください。
AMIを検索します。
- フィルタを[Public Images]に設定
- AMI ID
ami-fed908fe
で検索。php-conf-2015-suzukishoutenという名前のAMIが見つかります。
以降、作成するインスタンスの設定をしていきます。
3.インスタンスタイプを選択
t2.microを選択して[次の手順]。
4.インスタンス詳細設定
基本はデフォルトで[次の手順]クリックでOKです。。
誤った削除から保護します
にチェックを入れていますが、ここにチェックしておくと、インスタンス完成後に間違ってい削除しようとした時に救われます。(このチェックを外してからでないと削除できなくなるので安全です)
ですので、チェックを入れることをおすすめします。
5.ストレージ設定
ストレージを追加します。デフォルトでOKです。[次の手順]クリック。
6.タグ設定
好きな名前をつけて[次の手順]クリック。
ここではphpconf_2015
と命名。
7.セキュリティグループ設定
アクセスを許可するプロトコルやポートを設定します。
下記の通り設定し、[確認と作成]をクリック。
- 「新しいセキュリティグループを作成する」を選択
- 「セキュリティグループ名」 好きな名前(スクリーンショットでは、
web-public
としています。) - 説明 好きな説明文(スクリーンショットでは、
phpconf_2015
としています。) - SSH 任意
- Http 任意
ssh, httpは今回必須です。標準のポートで設定します。
ソースIPアドレスは、スクリーンショットでは任意
(どこからでもアクセス可能)にしています。(セキュリティの警告が出ますが、今回はこれでOK)。
8.確認画面
これまで設定した内容のサマリ。問題なければ[作成]クリック。
(警告が出てますが、上記と同様なのでOK)
9.キーペア作成
SSHアクセスに必要なキーペアを作成します。
新しいキーペアの作成
を選択しキーペア名
に作成するキーペアの名前を入力します。
既に作成済みのキーがあり、それを使用するなら、既存のキーペアの選択
を選択し、さらにキーペアの選択
欄で鍵を選択します。。
ここでは、phpconf_2015
と命名。
[キーペアのダウンロード]をクリックすると秘密鍵の書かれたテキストがダウンロードされるので、拡張子を.pem
にして保存しておきます。
このあたり、MacとWindowsでは少し違いますので説明します。
Macの場合
MacやLinuxでは、sshの鍵は一般的にユーザのホームディレクトリの下の.ssh
ディレクトリに保存します。(~/.ssh
)。
今回はそれに倣います。
コピーした.pemファイルは読み取り専用にしておきます(chmod 600
)。
通常、.
付きのファイルやディレクトリはFinderからは見えませんので、ターミナルで作業します。
以下、Mac上での作業です。
- sshディレクトリ作成(ない場合)
cd ~/
でユーザのホームディレクトリに移動、mkdir
でディレクトリ作成。
cd ~/
mkdir .ssh
- ダウンロードした鍵ファイルをコピー
鍵のファイル名はphpconf_2015.pem
としました。
cp ダウンロードした鍵ファイルのフルパス ~/.ssh/phpconf_2015.pem
chmod 600 ~/.ssh/phpconf_2015.pem
Windowsの場合
今回はputtyを使用した接続を行います。
puttyの場合、ダウンロードした鍵をputty用の形式に変換し、変換後の鍵を使用する必要があるので、いったん好きなところに適当な名前で保存してください。(鍵の変換方法は後述)
以降のスクリーンショットでは、デスクトップにphpconf_2015.pem
で保存したイメージになっています。
以降の説明でも秘密鍵のファイル名は"phpconf_2015.pem"で表記します。
これで鍵の準備はOKです。
[インスタンスの作成]がクリック出来るようになっていますので、クリックします。
10.サーバ完成!
下記画面が出ればOKです![インスタンスの表示]をクリックします。
インスタンス一覧に今作成したインスタンスが表示されました!
右の方にスクロールすると、[パプリックIP]が表示されています。
この後、ブラウザからのアクセス、SSHでのアクセスは全てこのIPアドレスに対して行います。
忘れたらこの画面で確認して下さい。
以降の説明では、このアドレス部分は"[Public IP]"と表記します。
11. ブラウザでアクセスしてみる
http://[Public IP]/
にアクセスすると、apacheのデフォルトルートドキュメントが表示されます。
12. SSHでアクセスしてみる
Mac, Windowsでやりかたが違いますので、それぞれ記載します。
Linuxを使用している方はほぼMacと同じはずですので割愛します。
Macの場合
標準のターミナルを起動します。
ここからはターミナル上で操作します。
sshを実行し、ec2-user
というユーザでログインします。
コマンドラインは下記の通り、ssh -i 秘密鍵のパス ユーザ名@接続先ホスト名(IPアドレス)
です。
ssh -i ~/.ssh/phpconf_2015.pem ec2-user@[Public IP]
うまく行けばこんな感じで表示されます。
※パッケージをアップデートしろと言われますがとりあえず無視。
__| __|_ )
_| ( / Amazon Linux AMI
___|\___|___|
https://aws.amazon.com/amazon-linux-ami/2014.09-release-notes/
39 package(s) needed for security, out of 177 available
Run "sudo yum update" to apply all updates.
Amazon Linux version 2015.03 is available.
Windowsの場合
puttyでの例です。
ダウンロードした鍵をputty用に変換
デフォルトでは、拡張子.pem
は表示されないので、[All Files]を選択し、ダウンロードした鍵ファイルを選択し、開きます。
これでOK。
ファイル名を入力して保存(拡張子は、.ppk
にします)。
これで鍵の変換は終了。
今回はデスクトップに保存していますが、本来はもう少し深いところにおいてください。
sshで接続
Categoryから[Session]を選択し、下記の通り入力します。
- HostName
ec2-user@[Public IP]
- Port
22
- Connection type
SSH
Categoryから[Connection]>[SSH]>[Auth]と選択し、[Browse]ボタンをクリックして作成した.ppk
のファイルを選択します。ここまででパラメータ設定は終わりです。続いてこの設定を保存します。
再び[Session]を選択し、[Saved Sessions]欄に名前を入力し、[Save]ボタンをクリックすると、設定した接続設定に名前をつけて保存することができます。
[Open]をクリックすると接続します。
作ったサーバのPHPに関する設定は?
サーバが起動できたら、もうPHPは使える状態です。
どういう設定になってるのか、AMI作成時にやった設定をまとめておきます。
PHPインストール、設定など
バージョンは5.5にしました。
以下rootで。
yum install -y php55
を実行してインストール。
[root ~]# yum install -y php55
読み込んだプラグイン:priorities, update-motd, upgrade-helper
amzn-main/latest | 2.1 kB 00:00
amzn-updates/latest | 2.3 kB 00:00
依存性の解決をしています
依存性を解決しました
=========================================================================================================================================================================================================
Package アーキテクチャー バージョン リポジトリー 容量
=========================================================================================================================================================================================================
インストール中:
php55 x86_64 5.5.20-2.94.amzn1 amzn-updates 3.0 M
依存性関連でのインストールをします:
json-c x86_64 0.11-6.8.amzn1 amzn-main 31 k
libzip x86_64 0.10.1-1.3.amzn1 amzn-main 47 k
php-pear noarch 1:1.9.5-2.17.amzn1 amzn-main 434 k
〜 中略 〜
インストール:
php55.x86_64 0:5.5.20-2.94.amzn1
依存性関連をインストールしました:
json-c.x86_64 0:0.11-6.8.amzn1 libzip.x86_64 0:0.10.1-1.3.amzn1 php-pear.noarch 1:1.9.5-2.17.amzn1 php55-cli.x86_64 0:5.5.20-2.94.amzn1 php55-common.x86_64 0:5.5.20-2.94.amzn1
php55-pecl-jsonc.x86_64 0:1.3.6-1.12.amzn1 php55-process.x86_64 0:5.5.20-2.94.amzn1 php55-xml.x86_64 0:5.5.20-2.94.amzn1
完了しました!
[root ~]#
バージョン確認
[root ~]# php -v
PHP 5.5.20 (cli) (built: Dec 29 2014 18:02:29)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
[root ~]#
php.ini編集
timezone
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone #←修正前
date.timezone = 'Asia/Tokyo' #←修正後
XDEBUG
XDEBUGは必ずしも必要ないが、IDEでリモートデバッグできて便利なので入れてます。
インストールは下記で。
yum install php55-pecl-xdebug --enablerepo=epel
php.iniを編集する。末尾に下記を追加。
[XDebug]
zend_extension = /usr/lib64/php/5.5/modules/xdebug.so
xdebug.remote_enable = 1
xdebug.remote_mode = req
xdebug.remote_port = 9000
xdebug.idekey = "phpconf2015"
xdebug.remote_host=localhost
xdebug.remote_autostart=1
上記に関連して、OS設定も。
- TimeZone
/etc/localtimeを、/usr/share/zoneinfo/Asia/Tokyoで上書き
cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
- LANG
/etc/sysconfig/i18nを編集
LANG=ja_JP.UTF-8
こんなところです。他にもapacheの設定でドキュメントルートを変えたり、もやってますが割愛します。
GitHubからTODOアプリのソース取得
鈴木商店用GitHubアカウントページを参照
下記URLにアクセスしてください。鈴木商店の主催する勉強会でも使用しているGitHubリポジトリです。ここに今回使用するソースがあります。
suzukishouten-study/phpconf-2015
開発用サーバにリポジトリをクローン
赤枠の部分にクローン元のURLがあります。今回はhttpsでのアクセスとしますので、https〜で始まるURLを使用します。
クローン元URL: https://github.com/suzukishouten-study/phpconf-2015.git
このリポジトリを自分の開発用サーバにCloneします。
ec2-user
ユーザでsshログインし、/var/www/phpconf2015
に移動してから下記のようにgit clone
コマンドを叩きます。
コマンドは、git clone https://github.com/suzukishouten-study/phpconf-2015.git .
です。
最後の "." の打ち忘れに注意!忘れると、サブディレクトリphpconf2015
が作成され、その中にCloneされますので、ディレクトリ階層がひとつ深くなってしまいます。
$ cd /var/www/phpconf2015/
$ git clone https://github.com/suzukishouten-study/phpconf-2015.git .
Cloning into '.'...
remote: Counting objects: 1458, done.
remote: Compressing objects: 100% (808/808), done.
remote: Total 1458 (delta 440), reused 1455 (delta 440), pack-reused 0
Receiving objects: 100% (1458/1458), 1.73 MiB | 615.00 KiB/s, done.
Resolving deltas: 100% (440/440), done.
Checking connectivity... done.
$
これで、/var/www/phpconf2015
にTODOリストアプリケーションのソースがcloneされました。
取得したソースの中身は?
鈴木商店の主催する勉強会でも使用している、TODOリストアプリケーションのソースです。
その作成過程について書いておきます。
CakePHPの準備、設定
ダウンロード、展開
ログイン後、rootになる。
sudo -i
以降の作業はrootで。
公式サイトからcakephpをダウンロード。
2.5系の最新安定版の、2.5.8を使用しています。
/var/www/
にcdしてからwgetでダウンロード。
wget https://github.com/cakephp/cakephp/archive/2.5.8.zip
これをunzipで解凍。
unzip 2.5.8.zip
cakephp-2.5.8
ディレクトリができるので、phpconf2015
に名前変更
mv cakephp-2.5.8 phpconf2015
オーナを変更しておく。
chown -R ec2-user:wheel phpconf2015
セキュリティ設定
これをやっとかないとCakeの警告が出ます。
ここからはrootじゃなくec2-userで。
/app/Config/Core.php.php
を修正。
下記サイトの通り設定しました(ブログ主さま、ありがとうございます!)。
[CakePHP] Security.salt と Security.cipherSeed を生成する | blog.shiten.info
データベース接続設定
作成したAMIには既にデータベースphpconf2015
が作成済みです。これに接続します。
database.php
を作成します。database.php.default
というファイルがあるので、それをファイル名変更して使用します。
次のように修正します。
public $default = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'root',
'password' => 'phpconf2015',
'database' => 'phpconf2015',
'prefix' => '',
'encoding' => 'utf8',
);
ここまでで基本的な設定は完了。
TODOリストアプリケーション作成
TODOアプリを作成していきますが、前提知識を確認しておきます。
URL、Http Method
RESTアプリケーションでは、リソースに対してユニークなURIを割付け、それに対してアクセスします。
そのリソースに対してどのような処理を行うかをHttp Methodで指定します。
(例)
URL
- todoリストの一覧
- /todo_lists.json
- todoリストのidが'1'の1件
- /todo_lists/1.json
Http Method
Http Method | 処理 |
---|---|
GET | 取得する |
POST | 追加する |
PUT | 更新する |
DELETE | 削除する |
CakePHPのController、Actionを含めまとめると
下表の通り、URL、HttpMethod、行う処理、CakePHPのControllerとActionが対応します。
下に、今回作成するソースを載せていますが、この表の通り作成されています。
URL | Http Method | 処理 | Controller | Action |
---|---|---|---|---|
/todo_lists.json | GET | TODOリスト一覧を取得する | TodoListsController | index |
/todo_lists/1.json | GET | TODOリストのidが'1'の1件を取得する | TodoListsController | view |
/todo_lists.json | POST | TODOリストに1件追加する | TodoListsController | add |
/todo_lists/1.json | PUT | TODOリストのidが'1'の1件を更新する | TodoListsController | edit |
/todo_lists/1.json | DELETE | TODOリストのidが'1'の1件を削除する | TodoListsController | delete |
HTTPレスポンスコード
APIの処理結果は、HTTPレスポンスコードで返すようにするのが基本です。
大雑把に言うと、
- 200番台 : 正常終了
- 300番台 : リダイレクト
- 400番台 : クライアントエラー
- 500番台 : サーバエラー
となります。
参考 HTTPステータスコード - Wikipedia
例)
リソースの追加を行い、成功した場合(POST)は、"201 Created"と返す。
では、REST APIによるTODOリストのソース、詳細です。
ソース詳細
- 修正したファイル
- app/Config/routes.php
- app/Controller/AppController.php
- 追加したファイル
- app/Controller/TodoListsController.php
- app/Model/TodoList.php
修正したファイルのdiff
※diff表示中の-
(ピンク色)の部分は、修正前のソースから削除し、+
(緑色)の部分を追加。
app/Config/routes.php
ルーティングの設定です。
/**
* Load all plugin routes. See the CakePlugin documentation on
* how to customize the loading of plugin routes.
*/
CakePlugin::routes();
+/*
+ * API
+ */
+Router::mapResources(array (
+ 'todo_lists',
+));
+Router::parseExtensions('json');
+
/**
* Load the CakePHP default routes. Only remove this if you do not want to use
* the built-in default routes.
*/
require CAKE . 'Config' . DS . 'routes.php';
- mapResources関数
RESTの場合、上で説明したindex
, view
, add
, edit
, delete
のアクションに対するルーティングを自動的に設定してくれる方法があり、それがこのmapResources
関数です。
引数にtodo_lists
と指定しているので、TodoListsController
の各メソッドにルーティングされます。
- parseExtensions関数
URLにつけられた拡張子によって表示するViewを切り替えるための設定です。
ここでは、json
を指定しているので、URLに付けられた拡張子が.json
の場合に、JSON表示用のビュー(/lib/Cake/View/JsonView.php
)が使用されるようになります。
app/Controller/AppController.php
コントローラの親クラスです。コンポーネントを追加します。
class AppController extends Controller {
- public $components = array('DebugKit.Toolbar');
+ public $components = array(
+ 'RequestHandler'
+ );
}
- $components変数
使用するコンポーネント指定します。
'RequestHandler'
というコンポーネントを指定しています。
これにより、クライアントからのリクエストを適切に処理し、さまざまな情報をを適切に取得できるようになります。
追加ファイル
以下は追加したファイルです。
app/Controller/TodoListsController.php
今回のAPIの本体ともいえるControllerです。
前述したindex
, view
, add
, edit
, delete
のアクションはここで実装しています。
<?php
App::uses('AppController', 'Controller');
class TodoListsController extends AppController {
public function index() {
$res = $this->TodoList->find('all');
$this->set(compact('res'));
$this->set('_serialize', 'res');
}
public function view($id = null) {
$res = $this->TodoList->findById($id);
$this->set(compact('res'));
$this->set('_serialize', 'res');
}
public function add() {
$data = $this->request->data;
$res = $this->TodoList->save($data);
$this->set(compact('res'));
$this->set('_serialize', 'res');
}
public function delete($id) {
$res = $this->TodoList->delete($id, false);
$this->set(compact('res'));
$this->set('_serialize', 'res');
}
public function edit($id) {
$this->TodoList->id = $id;
$data = $this->request->data;
$res = $this->TodoList->save($this->request->data);
$res = !empty($res);
$this->set(compact('res'));
$this->set('_serialize', 'res');
}
}
データベースからの検索、保存は次に説明するモデル、TodoList.php
を使用しています。
ポイントは、各アクションに共通している次の2行です。
$this->set(compact('res'));
$this->set('_serialize', 'res');
-
$this->set('変数名', データ);
という構文でコントローラからビューにデータを渡します。これでクライアントに返すデータをビューにセットします。 -
'_serialize'
というキーを指定することにより、ビューのテンプレート(.ctpのファイル)を使用せず、上でセットしたデータをそのままJSONで返すようになります。
app/Model/TodoList.php
モデルです。今回のAPIはシンプルなので、モデルでは何もしていません。
CakePHPのAppModel
をそのまま継承したのみです。
<?php
App::uses('AppModel', 'Model');
class TodoList extends AppModel {
}
以上で、超シンプルですがREST APIが実装できました。
GitHubに登録
これらまとめてGitHubに登録しますが、ついでに.gitignore
も登録します。
下記のGistを参考にしました。
CakePhp - default - .gitignore
では、動作確認。
POSTMANでAPIの動作確認
POSTMANの準備
POSTMAN、または同様の別ツールをインストール済みの方はここはとばしてください。
POSTMANはchromeブラウザの拡張機能です。
下記からインストールします。
インストールしたら、次回起動のためにブックマークしておいてください。
Postman - REST Client - Chrome ウェブストア
では、下記に全件取得、1件取得、追加、更新、削除する際のPOSTMANに設定するパラメータを記載します。
試しみましょう!
全件取得してみる(GETメソッド)
- URL:
http://[Public IP]/todo_lists.json
- メソッド:GET
1件取得してみる(GETメソッド:id付き)
- URL:
http://[Public IP]/todo_lists/1.json
- "1"のところは、更新したいデータのIDを指定する
- メソッド:GET
追加してみる(POSTメソッド)
- URL:
http://[Public IP]/todo_lists.json
- メソッド:POST
- HTTPヘッダ: Content-Type : application/json
- データ: ROW, JSON
{
"todo": "鈴木商店に入社する",
"status": "0"
}
更新してみる(PUTメソッド)
- URL:
http://[Public IP]/todo_lists/1.json
- "1"のところは、更新したいデータのIDを指定する
- メソッド:PUT
- HTTPヘッダ: Content-Type : application/json
- データ: ROW, JSON
{
"todo": "鈴木商店の面接を受ける",
"status": "0"
}
削除してみる
- URL:
http://[Public IP]/todo_lists/2.json
- "2"のところは、削除したいデータのIDを指定する
- メソッド:DELETE
以上です!
Postmanでの、"form-data"と"form-datax-www-form-urlencoded"の違いについて
PUTメソッドで送信する場合の動作について。
(1) form-data
の場合
"Content-type"が"multipart/form-data"に設定され、送信データが複数パートに分割される形式で送られます。
通常はファイルアップロード等を実行する際に指定する形式です。
下記の様になります。(id=1, status=0, todo=あいうえおとした場合)
------WebKitFormBoundaryBKsP47JxDdxYnfHW
Content-Disposition: form-data; name="id"
1
------WebKitFormBoundaryBKsP47JxDdxYnfHW
Content-Disposition: form-data; name="status"
0
------WebKitFormBoundaryBKsP47JxDdxYnfHW
Content-Disposition: form-data; name="todo"
あいうえお
------WebKitFormBoundaryBKsP47JxDdxYnfHW--
この形式だと、今回のTODOリストサーバ側プログラムで正しく送信データが取得できません。
(2) "x-www-form-urlencoded"の場合
"Content-type"が"application/x-www-form-urlencoded"に設定され、送信データは"名前1=値1&名前2=値2..."の形式で送られます。また、送信データはURLエンコードされます。
いわゆるフツーのformのpostで使用する形式です。
id=11&status=0&todo=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A
(3) raw
+ Content-type=application/json
(この記事で紹介している方式)
明示的ににContent-typeを指定してあげると、送信データは"{名前: 値1, 名前2: 値2, ...}と、書いた通り送られ、サーバはJson形式であるものとして認識します。
今回のRestアプリケーションでは、送受信とも全てJSONを想定しているので、この方法がベストです。
{id: 1, todo: "あいうえお", status: "0"}
(2)でもちゃんと動くのは、CakePHPのRequestHandlerがよろしくやってくれて、アプリケーションからは(2)でも(3)でも、単なる配列(連想配列)として取得できるようになっているからですね。
参考URL リクエストハンドリング — CakePHP Cookbook 2.x ドキュメント
phpMyAdminについて
http://[Public IP]/phpMyAdmin/
にアクセスすると起動できます。
user : root
password : phpconf2015
でOKです。パスワードは変えておきましょう!
今回使用しているテーブルは、phpconf2015/todo_lists
です。
以上です!
勉強会もやってますので、よかったら来てくださいね!