CakePHP

CakePHPでWEB APIを自作してみる。

2013/06/14

サーバAで運営しているDBのデータを、サーバBで必要になりました。

サーバAのデータをエクスポートしてサーバBへインポートし、作成したプログラムで必要に応じて取得する、という流れでもいいのですが、エクスポート、インポートする手間が惜しい。

ならば、サーバBからのリクエストに対し必要なデータを送信するという、いわゆる API をサーバAに設置してはどうかという流れになり、調べてみましたら、意外と簡単にできることが判明。

忘備録代わりにコチラに作り方をポストしてみます。

通信仕様の決定

バージョンは例によって1.3です。
例として『メールアドレスをキーに顧客データを取得する』といったものを作成してみたいと思います。

APIの中身は、以前使ったことがある amazon の API を真似て作ってみます。

アマゾンAPIにリクエスト方式を見ると、オプションを「?」及び「&」で繋げる GETメソッドで HTTP通信を行なっています。
いわゆる、REST(Representational State Transfer の略)という仕様ですね。

リクエスト方式としてはあと、SOAP(Simple Object Access Protocol)という方式がありますが、こちらは、XMLを使って引数やオプションを指定します。

ということで、今回はアマゾンに倣って REST を使ってみることとします。

送信側PHP

では、まずはGETメソッドをHTTP経由でAPIプログラムに投げる処理から。
単純に書くと、以下の様な手順になります。

  1. 送信するパラメータを準備
  2. パラメータを&で連結
  3. URLを作成
  4. APIで生成されたXMLを、file_get_contents関数で受け取る
  5. 手順4で受信した返り値(XML)を、 simplexml_load_string関数 で、オブジェクトに代入
  6. CakePHPのCakePHPのxmlパーサで、配列に変換する

これを、PHPで書くとこんな感じ。

[php]
// APIのURL
$apiURL = 'http://sample-api-url.com/api';

// トークン作成
$token = sha1(uniqid(mt_rand(), true));

// パラメータ準備
$params = array(
'email' => 'someone@mailaddress.com', // 検索するメールアドレス
'accesskey' => 'abc0123-toogie-wataamefrog',
'token' => $token,
'timestamp' => date("Y-m-d\TH:i:s\Z"),
);

// Keyでソート
ksort($params);

// パラメータを連結
$string = '';
foreach ( $params AS $k => $v ) {
$string .= '&' . rawurlencode($k) . '=' . rawurlencode($v);
}

// 先頭の「&」を省く
$string = substr($string, 1);

// URL を作成
$apiOptions = $apiURL .'?' . $string;

$apiResult = file_get_contents($apiOptions);

// XMLオブジェクト作成
$sResult = simplexml_load_string($apiResult);

// CakePHPのxmlパーサを利用
uses('Xml');
$xml = new XML($sResult);
$xml_array = Set::reverse($xml);

// debug
pr($xml_array)

// 欲しい処理
if ( $xml_array['SimpleXMLElement']['Item']['status'] == 'true' ) {
// 受け取った結果を使った処理
//(省略)
}
[/php]

XMLで返ってきたものを、CakePHPのXMLパーサを利用して、配列に変換しています。
これで foreach でもなんでも使えるようになります。

API側のPHPプログラム

呼ばれる先のPHP。コチラはCakePHPではなく、プレーンなPHPです。
GETで送信されているので、$_GET でパラメータを取得し、欲しい内容作成し XML形式で出力しています。

[php]
// アカウント部分を取得
$email = explode('@', $_GET['email']);
$email_account = $email[0];

// ドメイン名取得
$email_domain_name = $email[1];

// アカウント名、ドメイン名からDBを検索
// 存在する場合は、 $ret_email にメールアドレスを、存在しない場合は返り値は false とする処理
//(省略)

// APIの戻り値を作成
if ( $ret_email == false ) {
// 失敗パラメータ作成
$returns = array(
'result' => array(
'status' => 'false',
'timestamp' => $_GET['timestamp'],
'token' => $_GET['token'],
'email' => 'null',
),
);
} else {
// 成功パラメータ作成
$returns = array(
'result' => array(
'status' => 'true',
'timestamp' => $_GET['timestamp'],
'token' => $_GET['token'],
'email' => $ret_email,
),
);
}

// XMLのルートタグとXML宣言
$xmlStr = '';

// オブジェクトの生成
$xmlObj = new SimpleXMLElement($xmlStr);

// パラメータ配列を使ってXMLに要素を追加していく
foreach( $returns as $arr ){
$xmlitem = $xml->addChild("item"); // itemタグ追加
if ( is_array($arr) ) {
foreach ( $arr as $key => $value ){
$xmlitem->addChild($key, $value); // 子要素を追加
}
}
}

// XMLを出力
header('Access-Control-Allow-Origin:*');
header("Content-Type: text/xml");
echo $xmlObj->asXML();

[/php]

パラメータには検索するメールアドレスの他、認証用やセキュリティ対策用のオプションを指定して、できるだけ固いシステムにすることが必要かと思われます。

今回のサンプルでは、timestamp と token はGETで受けたものをそのまま返しているだけです。

帰ってくる結果

pr($xml_array);
した結果。

[php]
Array(
[SimpleXMLElement] => Array (
[Item] => Array(
[status] => true
[timestamp] => 2013-06-05T13:15:57Z
[token] => 6093766c33b27d08dbad4462dfc500e3ba1b5fc3
[email] => someone@mailaddress.com
)

)
);
[/php]

意外と簡単にできることが分かり、色々と画策中。
先にも書きましたが、セキュリティ面を強固にする必要があると思うので、その辺りをまた研究したいと思います。

[tgAmazonItemLookup asin="B005UK3K8O" related="1"]

-CakePHP
-, ,