コードを複製せず、既存のビジネスロジックへの最小限の変更で、RESTfulアーキテクチャの実装をコミュニティに紹介したいと思います。 または、5分でAPIをプロジェクトに追加する方法は?
このアプローチを実装するために、Yii Frameworkの拡張機能を作成しましたが、アプローチ自体はどのMVCアーキテクチャでも使用できます。
メソッドを持つ
RestUserController
コントローラーがあると想像してみましょう:
-
actionIndex
ユーザーリスト -
actionView
ユーザーを表示 -
actionCreate
ユーザーを作成する -
actionUpdate
ユーザー更新 -
actionDelete
ユーザーを削除する
また、 ActvieRecord rest_usersテーブルである
RestUser
モデルもあります。
新しい
RestUser
ユーザーを作成することをタスクとする
actionCreate
メソッドを検討します。
class RestUserController extends Controller { ... public function actionCreate() { $model = new RestUser(); if (isset($_POST) && ($data = $_POST)) { // POST $model->attributes = $data; // if ($model->save()) { // , - $this->redirect(array('view', 'id' => $model->id)); } } $this->render('create', array('model' => $model)); // html- } ... }
ここではすべてが明確です-
/restUser/create
リクエストするだけで、新しいユーザーを追加するためのhtmlフォームが表示されます。このアドレスにPOSTリクエストを送信すると、ロジックを検証して追加し、ユーザーを表示するか、htmlフォームを表示しますc間違い。
ここで、インターフェイスから新しいユーザーを作成できるモバイルアプリケーションを作成するとします。 正しい方法は、サーバーAPIを作成することです。
なぜなら RESTfulスタイルについて説明しているため、 curlを介したサンプルリクエストを使用したサーバーとモバイルアプリケーションの相互作用は次のようになります。
リクエスト
curl http://test.local/api/users \ -u demo:demo \ -d email="user@test.local" \ -d password="passwd"
答え
< HTTP/1.1 201 Created < Content-Type: application/json < WWW-Authenticate: Basic realm="App" < Location: http://test.local/api/users/TEST_ID { "object":"rest_user", "id":"TEST_ID", "email":"user@test.local", "name":"Test REST User" }
ここでは、デモパスワードを使用したデモログインのHTTP基本認証によって承認が行われ、必要なパラメーターの電子メールとパスワードが送信され、それに応じて、すべてが正しい場合、新しいユーザーのJSONオブジェクトを取得します。
私たちのアプローチの全体的なアイデアは、
redirect
および
render
メソッドを変更し、モデルレンダリングルールを追加
render
ことによってのみ、APIリクエストに正しく応答する機能にアクションを追加することです。
もちろん、APIクライアントへの正しい応答のために、モデル自体の作成時のエラーだけでなく、アプリケーションのエラーとアクションのインターセプトも実装する必要がありますが、コントローラーのアクションのビジネスロジックを変更する必要はありません。
拡張機能では、
onException
onError
と
onError
インターセプトすることで、提案されたアプローチを実装しました。また、
CController
を使用してベース
CActiveRecord
モデルと
CController
追加機能を追加しました。
その結果、APIを介して要求するときに目的の応答を返すコード、および通常の要求を含むhtmlフォームは次のようになります。
class RestUserController extends Controller { ... public function actionCreate() { $model = new RestUser(); if ($this->isPost() && ($data = $_POST)) { // isPost isPut isDelete $model->attributes = $data; if ($model->save()) { $this->redirect(array('view', 'id' => $model), true, 201); // } } $this->render('create', array('model' => $model), false, array('model')); // model } ... }
新しいコードと以前のコードの重要な違いは、
$model->id
ではなく、
$model
オブジェクトの
id
パラメーターとして
redirect
メソッドに転送されることです。これにより、作成されたオブジェクトがクライアントに返されます。 また、3番目のパラメーターに応答コード201が追加されました。これは、標準に準拠するために必要です。 応答とともに、作成されたオブジェクトのアドレスを含むLocationヘッダーが送信されます。 3xx HTTPコードは応答で許可されていません。
もう1つの違いは、
render
メソッドに追加された4番目のパラメーターです。これには、クライアントへの応答で送信される
$data
配列のフィールドの列挙が含まれます。 パラメータが
null
場合、
$data
配列全体が返されます。
現在、リクエストが正しくない場合、通常htmlフォームに表示されるデータは次の形式で返されます。
リクエスト
curl http://test.local/api/users \ -u demo:demo \ -d email="user@test.local"
答え
< HTTP/1.1 400 Bad Request < Content-Type: application/json < WWW-Authenticate: Basic realm="App" { "error":{ "params":[ { "code":"required", "message":"Password cannot be blank.", "name":"password" } ], "type":"invalid_param_error", "message":"Invalid data parameters" } }
さて、今度は何らかの方法で機密モデルデータを保護する必要があります
RestUser
この
password
フィールドがあります。 これを行うには、ルールで返されるフィールドのリストを定義します。
モデルの表示ルールは
rules
メソッドになり
rules
class RestUser extends CModel { public function rules() { return array( ... array('id, email, name', 'safe', 'on' => 'render'), ); } }
このルールは、モデルに追加される
getRenderAttributes
メソッドで考慮されます。このメソッドは、表示に使用できるすべての属性の配列を返し、ルールで指定されている場合、オブジェクトの関係を再帰的に通過します。
結論として、認証と表示機能について少しお話ししたいと思います。
拡張コアは、コンポーネント(サービス)
\rest\Service
中心に構築され、イベントのメイン処理とデータの正しい表示に関与します。 このサービスには、
auth
と
renderer
2つのアダプターグループがあり
renderer
。
auth
は認証アダプターがあります-デフォルトのHTTP基本認証アダプターが使用可能です。
renderer
は、データを表示するアダプターが含まれています-デフォルトでは、2つのJSONおよびXMLアダプターが使用可能です。
延長
簡単な設定
main.php
構成ファイルの例
YiiBase::setPathOfAlias('rest', realpath(__DIR__ . '/../extensions/yii-rest-api/library/rest')); return array( 'basePath' => dirname(__FILE__) . DIRECTORY_SEPARATOR . '..', 'name' => 'My Web Application', 'preload' => array('restService'), 'import' => array( 'application.models.*', 'application.components.*', ), 'components' => array( 'restService' => array( 'class' => '\rest\Service', 'enable' =>strpos($_SERVER['REQUEST_URI'], '/api/') !== false, // ), 'urlManager' => array( 'urlFormat' => 'path', 'showScriptName' => false, 'baseUrl' => '', 'rules' => array( array('restUser/index', 'pattern' => 'api/v1/users', 'verb' => 'GET', 'parsingOnly' => true), array('restUser/create', 'pattern' => 'api/v1/users', 'verb' => 'POST', 'parsingOnly' => true), array('restUser/view', 'pattern' => 'api/v1/users/<id>', 'verb' => 'GET', 'parsingOnly' => true), array('restUser/update', 'pattern' => 'api/v1/users/<id>', 'verb' => 'PUT', 'parsingOnly' => true), array('restUser/delete', 'pattern' => 'api/v1/users/<id>', 'verb' => 'DELETE', 'parsingOnly' => true), array('restUser/index2', 'pattern' => 'api/v2/users', 'verb' => 'GET', 'parsingOnly' => true), // , API ) ), ), );
コントローラーに動作を追加し、メソッドを再定義する
/** * @method bool isPost() * @method bool isPut() * @method bool isDelete() * @method string renderRest(string $view, array $data = null, bool $return = false, array $fields = array()) * @method void redirectRest(string $url, bool $terminate = true, int $statusCode = 302) * @method bool isRestService() * @method \rest\Service getRestService() */ class RestUserController extends Controller { public function behaviors() { return array( 'restAPI' => array('class' => '\rest\controller\Behavior') ); } // $fields , public function render($view, $data = null, $return = false, array $fields = array('count', 'model', 'data')) { if (($behavior = $this->asa('restAPI')) && $behavior->getEnabled()) { if (isset($data['model']) && $this->isRestService() && count(array_intersect(array_keys($data), $fields)) == 1) { $data = $data['model']; // API, , - $fields = null; } return $this->renderRest($view, $data, $return, $fields); } else { return parent::render($view, $data, $return); } } public function redirect($url, $terminate = true, $statusCode = 302) { if (($behavior = $this->asa('restAPI')) && $behavior->getEnabled()) { $this->redirectRest($url, $terminate, $statusCode); } else { parent::redirect($url, $terminate, $statusCode); } } }
これらのメソッドはすべて、各コントローラーに個別に実装されないように、親コントローラーに追加できます。
モデルに動作を追加して、レンダリングルールが機能するようにします
/** * @method array getRenderAttributes(bool $recursive = true) * @method string getObjectId() */ class RestUser extends CActiveRecord { /** * @return array */ public function behaviors() { return array( 'renderModel' => array('class' => '\rest\model\Behavior') ); } }
参照資料
GitHubリポジトリ-github.com/paysio/yii-rest-api
インストールとセットアップの説明-github.com/paysio/yii-rest-api#installation
上記のコードはすべてgithub.com/paysio/yii-rest-api/tree/master/demoです