プロジェクトにRESTful APIを便利に埋め込む

APIを持っていることがどのプロジェクトにも適していることは周知の事実です。 しかし、多くの場合、システムアーキテクチャにエラーがあるか、完成したプロジェクトに追加すると、サポートとテストのオーバーヘッドに多くの時間がかかります。



コードを複製せず、既存のビジネスロジックへの最小限の変更で、RESTfulアーキテクチャの実装をコミュニティに紹介したいと思います。 または、5分でAPIをプロジェクトに追加する方法は?



このアプローチを実装するために、Yii Frameworkの拡張機能を作成しましたが、アプローチ自体はどのMVCアーキテクチャでも使用できます。



メソッドを持つRestUserController



コントローラーがあると想像してみましょう:



また、 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です



All Articles