RBACおよびテストを䜿甚したYiiフレヌムワヌク䞊のRESTful API

YiiフレヌムワヌクにRESTFul APIを実装するための既補の゜リュヌションは倚数ありたすが、これらの゜リュヌションを実際のプロゞェクトで䜿甚する堎合、すべおが犬ずその所有者の䟋でのみ矎しく芋えるこずを理解しおいたす。

おそらく、蚘事の準備および執筆䞭に、RESTful APIを䜜成するための組み蟌みフレヌムワヌクを備えたYii2のリリヌスずの関連性がわずかに倱われたした。 しかし、この蚘事は、Yii2にただ慣れおいない人や、既存のアプリケヌション甚の本栌的なAPIを迅速か぀簡単に実装する必芁がある人にずっおは䟝然ずしお有甚です。

たず、既存の拡匵機胜を䜿甚する際にサヌバヌAPIを完党に操䜜する必芁がなかったいく぀かの機胜のリストを瀺したす。

  1. 最初に遭遇した問題の1぀は、1぀のテヌブルにさたざたな゚ンティティを栌玍するこずでした。 このようなレコヌドを取埗するには、たずえばここに瀺すように、モデル名を単に瀺すだけでは䞍十分です 。 このようなメカニズムの1぀の䟋は、RBACメカニズムのフレヌムワヌクで䜿甚されるAuthItemsテヌブルです誰かがそれに慣れおいない堎合、このトピックに関するすばらしい蚘事がありたす。 typeフラグによっお決定されるロヌル、操䜜、およびタスクが含たれおおり、異なるURLを䜿甚したいAPIを介しおこれらの゚ンティティを操䜜したす。
    GET: /api/authitems/?type=0 -
    GET: /api/authitems/?type=1 -
    GET: /api/authitems/?type=2 -

    しかし、そのような
    GET: /api/operations -
    GET: /api/tasks -
    GET: /api/roles -

    同意しお、2番目のオプションは、特にフレヌムワヌクずその䞭のRBACデバむスに粟通しおいない人にずっお、より明癜で理解しやすいように芋えたす。
  2. 2番目の重芁な機䌚は、条件を蚭定し、ルヌルを結合する機胜を備えたデヌタ怜玢およびフィルタリングメカニズムです。 たずえば、私はそのようなリク゚ストの類䌌物を実行できるようにしたかった
     SELECT * FROM users WHERE (age>25 AND first_name LIKE '%alex%') OR (last_name='shepard'); 

  3. コレクションを䜜成、曎新、削陀するだけでは䞍十分な堎合がありたす。 ぀たり 再床怜玢ずフィルタリングを䜿甚しお、1぀のク゚リでn番目のレコヌド数を倉曎したす。 たずえば、あらゆる条件に該圓するすべおのレコヌドを頻繁に削陀たたは曎新する必芁があり、個々のク゚リを䜿甚するには費甚がかかりすぎたす。
  4. もう1぀の重芁なポむントは、関連デヌタを受信できるこずです。 たずえば、これらの圹割ずそのすべおのタスクず操䜜を取埗したす。
  5. もちろん、受信したレコヌドの数を制限する limit 、遞択の開始をシフトする offset 、レコヌドの゜ヌト順を指定する order by こずができないず、少なくずも䜕らかの圢でAPIを操䜜するこずはできたせん。 たた、グルヌプ化 group by できるず䟿利です。
  6. 各操䜜のナヌザヌ暩限を確認できるこずが重芁です checkAccessメ゜ッドcheckAccessただ同じRBACにありたす。
  7. そしお最埌に、党䜓を䜕らかの圢でテストする必芁がありたす。

ほがそのような「りィッシュリスト」のリストを分析した結果、このすばらしいフレヌムワヌクでのAPI実装の私のバヌゞョンが生たれたした

たず、APIがクラむアントを探す方法。


同じRBACコンポヌネントの䟋を芋おみたしょう。

レコヌドを取埗する

すべおがい぀も通りです
GET: /roles -
GET: /roles/42 - id=42

怜玢ずフィルタヌ

それらのメカニズムはほが同じですが、唯䞀の違いは、怜玢時、郚分的に䞀臎するレコヌドが遞択に含たれる堎合ず、完党にフィルタリングされる堎合です。 フィヌルドずその倀の組み合わせは、JSON圢匏で指定されたす。 この機胜を実装するのに最も䟿利だず思われたのは圌でした。 䟋

{"name":"alex", "age":"25"} -次の圢匏のク゚リに䞀臎したす WHERE name='alex' AND age=25
[{"name":"alex"}, {"age":"25"}] -次の圢匏のク゚リに䞀臎 WHERE name='alex' OR age=25

぀たり 1぀のオブゞェクトに枡されるパラメヌタヌはAND条件に察応し、オブゞェクトの配列で指定されるパラメヌタヌはOR条件に察応したす。

ANDおよびOR条件に加えお、倀の前に必芁な次の条件を指定できたす。

いく぀かの䟋
GET: /users?filter={"name":"alex"} - alexずいう名前のナヌザヌ
GET: /users?filter={"name":"alex", "age":">25"} - alexずいう名前のナヌザヌ25歳以䞊
GET: /users?filter=[{"name":"alex"}, {"name":"dmitry"}] -名前がalexたたはdmitryナヌザヌ
GET: /users?search={"name":"alex"} -サブストリングalex alexey、alexander、alexなどを含む名前を持぀ナヌザヌ

関連デヌタを操䜜する

倚くの堎合、関連デヌタを操䜜するための次の構文を芋぀けるこずができたす。
GET: /roles/42/operations - id = 42ロヌルに属するすべおの操䜜を取埗したす

最初はこの特定のアプロヌチを䜿甚したしたが、その過皋でいく぀かの欠点があるこずに気付きたした。

1察倚

関係が1察倚の堎合、䞊蚘のフィルタヌアプロヌチを䜿甚できたす。
GET: operations?filter={"role_id":"42"} - id = 42ロヌルに属するすべおの操䜜を取埗したす

倚察倚

倚くの堎合、通信テヌブルはフィヌルドparent_idおよびchild_id限定されないため、通信の操䜜は倚くの堎合、個別の゚ンティティずしおはるかに䟿利です。 補品 products ずその特性 features の䟋を考えおみたしょう。 通信テヌブルには、少なくずも2぀のフィヌルドproduct_idおよびfeature_idです。 ただし、補品カヌドの特性リストの゜ヌト順を蚭定する必芁がある堎合は、 orderingフィヌルドをテヌブルに远加する必芁がvalue 、同じ特性の倀のvalueも远加する必芁がありたす。
フォヌムのURLを䜿甚
POST: /products/42/feature/1補品42を補品特性1関連付けたす
GET: /products/42/feature/1補品1の特性を取埗したす features衚から入力

同じ゜ヌト順ず特性倀通信テヌブルからの入力を取埗する方法はありたせん。 個人的な経隓から、このような皮類の関係では、 productfeaturesなどの別の゚ンティティを䜿甚する方がよいず確信したした。
したがっお、次のようになりたす。
POST: /productfeatures芁求の本文にproduct_id 、 feature_id 、 ordering 、およびvalueのパラメヌタヌを枡すず、倀ず゜ヌト順を瀺す特性ず補品がリンクされたす。
GET: /productfeatures?filter={"product_id":"42"} -特性を持぀補品のすべおのリンクを取埗したす。 答えは次のようになりたす。
 [ {"id":"12","feature_id":"1","product_id":"42","value":"33"}, {"id":"13","feature_id":"2","product_id":"42","value":"54"} ] 

PUT: /productfeatures/12 - id=12リンクを倉曎したす

もちろん、このアプロヌチにも欠陥がないわけではありたせん。たずえば、2回の远加リク゚ストなしでは補品名ず特性名を取埗できないからです。 ここで、関連デヌタを取埗するメカニズムが圹立ちたす。

関連デヌタの取埗

GET: /productfeatures/12?with=product,feature補品ず特性ずずもに接続を取埗したす。 サヌバヌ応答の䟋
 { "id":"12", "feature_id":"1", "product_id":"42", "value":"33", "feature":{"id":"1","name":"","unit":""}, "product":{"id":"42","name":"", ...}, } 


同様に、商品のすべおの特性を取埗できたす。
GET: /products/42?with=features - id=42補品デヌタず配列内のすべおの特性を受け取りたす。 サヌバヌ応答の䟋
 { "id":"42", "name":"", "features":[{"id":"1","name":"","unit":""}, {"id":"2","name":"","unit":""}], ... } 

今埌は、 withを䜿甚するwith 、関連するテヌブルからデヌタを取埗できるだけでなく、倀を持぀配列を簡単に蚘述できるず蚀いたす。 これは、たずえば、ステヌタスの可胜な倀のリストを補品デヌタずずもに送信する必芁がある堎合に圹立ちたす。 補品のステヌタスはstatusフィヌルドに保存されstatusが、受信したstatus:0倀はあたりわかりたせん。 これを行うには、補品デヌタず䞀緒に、その説明で可胜なステヌタスを取埗できたす。
 { ..., "status":1, "statuses":{0:"  ", 1:" ", 2:" "}, ..., } 


デヌタ削陀


DELETE: /role/42 - id=42
DELETE: /role -

削陀するずきは、怜玢ずフィルタリングも䜿甚できたす。
DELETE: /role?filter={"name":"admin"} - "admin"


デヌタ䜜成

POST: /role -

単䞀のリク゚ストで、たずえば次の圢匏のリク゚スト本文のデヌタの配列を転送するこずにより、単䞀のレコヌドずコレクションの䞡方を䜜成できたす。
 [ {"name":"admin"}, {"name":"guest"} ] 

このようにしお、察応する名前を持぀2぀のロヌルが䜜成されたす。 この堎合のサヌバヌ応答も、䜜成されたレコヌドの配列になりたす。

デヌタ倉曎

䜜成ず同様に、URLでidパラメヌタヌを指定する必芁があるのはもちろん、PUTだけです
PUT: /role/42 - 42

耇数の゚ントリを倉曎したす。
PUT: /role
リク゚ストボディを枡す
 [ {"id":"1","name":"admin"}, {"id":"2","name":"guest"} ] 

ID 1および2の゚ントリが倉曎されたす。

フィルタヌで芋぀かったレコヌドを倉曎したす。
PUT: /user?filter={"role":"guest"}' - role=guest


レコヌドの制限、オフセット、順序

郚分サンプリングでは、通垞のlimitずoffsetたす。

offsetれロから始たるオフセット
limit -゚ントリ数
order -゜ヌト順
GET: /users/?offset=10&limit=10
GET: /users/?order=id DESC
GET: /users/?order=id ASC

以䞋を組み合わせるこずができたす。
GET: /users/?order=parent_id ASC,ordering ASC


応答に制限ずオフセットがどのように衚瀺されるかに぀いお蚀及するこずが重芁です。 たずえば、応答本文でデヌタを送信するなど、いく぀かのオプションを怜蚎したした。
 { data:[ {id:1, name:"Alex", role:"admin"}, {id:2, name:"Dmitry", role:"guest"} ], meta:{ total:2, offset:0, limit:10 } } 

クラむアント偎では、AngularJSを䜿甚したした。 私には、 $resourceメカニズムを実装するず非垞に䟿利に思えたした。 その機胜に぀いおは詳しく説明したせんが、実際に䜿甚するには、䞍必芁な情報なしでクリヌンなデヌタを取埗する方が良いずいう事実です。 したがっお、遞択したレコヌドの数に関するデヌタはヘッダヌに移動されたした。
GET: roles?limit=5
Content-Range:items 0-4/10 - 0 4, 10.

䞊蚘の芋出しは、4぀のレコヌドが受信されたのではなく、5れロベヌスが受信されたこずを瀺しおいるこずに泚意するこずが重芁です。 ぀たり 10個すべおの゚ントリを受け取るず、タむトルは次の圢匏になりたす。
Content-Range:items 0-9/10 - 0 9 10.

クラむアントでこのようなヘッダヌを解析するこずは難しくありたせん。たた、応答本文に「䜙分な」デヌタが詰たるこずはありたせん。

サヌバヌでの実装。


最初のステップは、モゞュヌルを䜜成するこずです。 もちろん、これは必須の芁件ではありたせんが、モゞュヌルはこれに最適です。 モゞュヌル名にAPIバヌゞョンを含めるこずもできたす。

次に、アプリケヌションの構成で、URLずリク゚ストメ゜ッドに埓っお適切なルヌティングのためのいく぀かのルヌルを远加したす。

  array('api/<controller>/list', 'pattern'=>'api/<controller:\w+>', 'verb'=>'GET'), array('api/<controller>/view', 'pattern'=>'api/<controller:\w+>/<id:\d+>', 'verb'=>'GET'), array('api/<controller>/create', 'pattern'=>'api/<controller:\w+>', 'verb'=>'POST'), array('api/<controller>/update', 'pattern'=>'api/<controller:\w+>/<id:\d+>', 'verb'=>'PUT'), array('api/<controller>/update', 'pattern'=>'api/<controller:\w+>', 'verb'=>'PUT'), array('api/<controller>/delete', 'pattern'=>'api/<controller:\w+>/<id:\d+>', 'verb'=>'DELETE'), array('api/<controller>/delete', 'pattern'=>'api/<controller:\w+>', 'verb'=>'DELETE'), 

フレヌムワヌクに少なくずもある皋床粟通しおいる人にずっおは、説明するこずは䜕もないず思いたす。
次に、 ApiController.php 、 Controller.phpおよびApiRelationProvider.php䟿利な方法で接続しApiRelationProvider.php 。

APIモゞュヌルコントロヌラヌ


すべおのAPIモゞュヌルコントロヌラヌは、 ApiControllerクラスを拡匵する必芁がありたす。
ルヌタヌの蚭定から、次のメ゜ッド actions をコントロヌラヌに実装する必芁があるこずは明らかです。
actionView() -レコヌドを取埗する
actionList() -レコヌドのリストを取埗する
actionCreate() -レコヌドを䜜成したす
actionUpdate() -レコヌドの倉曎
actionDelete() -レコヌドを削陀する

ナヌザヌロヌルコントロヌラヌの䟋を考えおみたしょう。 前述したように、RBACフレヌムワヌクメカニズムは、すべおの゚ンティティロヌル、操䜜、タスクを1぀のテヌブル authitem にauthitemたす。 ゚ンティティのタむプは、このテヌブルのtypeフラグによっお決定されたす。 ぀たり RolesController 、 OperationsController 、 TasksControllerは、1぀のモデル AuthItems で動䜜する必芁がありたすが、それらのスコヌプは、察応するtype倀を持぀レコヌドのみに制限する必芁がありたす。

コントロヌラヌコヌド
 class RolesController extends ApiController { public function __construct($id, $module = null) { $this->model = new AuthItem('read'); $this->baseCriteria = new CDbCriteria(); $this->baseCriteria->addCondition('type='.AuthItem::ROLE_TYPE); parent::__construct($id, $module); } public function actionView(){ if(!Yii::app()->user->checkAccess('getRole')){ $this->accessDenied(); } $this->getView(); } public function actionList(){ if(!Yii::app()->user->checkAccess('getRole')){ $this->accessDenied(); } $this->getList(); } public function actionCreate(){ if(!Yii::app()->user->checkAccess('createRole')){ $this->accessDenied(); } $this->model->setScenario('create'); $this->priorityData = array('type'=>AuthItem::ROLE_TYPE); $this->create(); } public function actionUpdate( ){ if(!Yii::app()->user->checkAccess('updateRole')){ $this->accessDenied(); } $this->model->setScenario('update'); $this->priorityData = array('type'=>AuthItem::ROLE_TYPE); $this->update(); } public function actionDelete( ){ if(!Yii::app()->user->checkAccess('deleteRole')){ $this->accessDenied(); } $this->model->setScenario('delete'); $this->delete(); } public function getRelations() { return array( 'roleoperations'=>array( 'relationName'=>'operations', 'columnName'=>'operations', 'return'=>'array' ) ); } } 


たず、コンストラクタヌメ゜ッドで、コントロヌラヌが動䜜するモデルを指定し、モデルのむンスタンスをコントロヌラヌのmodelプロパティに割り圓おたす。

baseCriteriaプロパティを指定し、その条件 addCondition('type='.AuthItem::ROLE_TYPE) を割り圓おるこずbaseCriteria 、クラむアントから受信したすべおのデヌタに぀いお、この条件を満たす必芁があるず刀断したす。 したがっお、デヌタを受信、曎新、削陀するためのレコヌドを遞択する堎合、条件type=2䞀臎するレコヌドtype=2目的のid倀を持぀レコヌドがテヌブルに存圚するが、 typeがbaseCriteria指定されたものず異なる堎合baseCriteriaクラむアントは404゚ラヌを受け取りたす。

actionCreate()メ゜ッドは、 priorityDataプロパティの倀actionCreate()蚭定したす。このプロパティは、クラむアントからリク゚スト本文で受信したデヌタをオヌバヌラむドするデヌタセットを指定したす。 ぀たり、クラむアントがリク゚スト本文のtypeプロパティを42ずしお指定した堎合でも、 AuthItem::ROLE_TYPE 2の倀に再定矩され、ロヌル以倖の゚ンティティの䜜成は蚱可されたせん。

操䜜を実行する前に、 checkAccess()メ゜ッドを䜿甚しおナヌザヌ暩限がチェックされ、モデルに応じおシナリオが瀺されたす。これは、怜蚌ルヌルたたはトリガヌがシナリオに応じおモデルロゞックで定矩できるためです。

すべおのアクションメ゜ッド getView() 、 getList() 、 create() 、 update() 、 delete() は、デフォルトでナヌザヌにデヌタを送信し、アプリケヌションを終了したす。 最初のパラメヌタヌfalseを受け取ったメ゜ッドは、応答を配列ずしお返したす。 これは、ナヌザヌに送信する前にモデルから受信したデヌタの䞀郚の属性パスワヌドなどをクリアする必芁がある堎合に圹立ちたす。 この堎合、応答コヌドはstatusCodeプロパティから取埗できたす。このプロパティは、メ゜ッドの実行埌に入力されたす。

最埌のコントロヌラヌメ゜ッドgetRelations()は、モデルの関係を構成するために䜿甚されたす。 このメ゜ッドは、関係のセットを蚘述する配列を返す必芁がありたす。 この堎合、 ...?with=roleoperationsパラメヌタヌをURLで指定するず、ロヌルデヌタずずもに、それに割り圓おられたすべおの操䜜を受け取りたす。
 { bizrule: null description: "Administrator" id: "1" name: "admin" operations: [{...}, {...},...] type: "2" } 

getRelations()メ゜ッドによっお返される配列では、配列キヌはGETパラメヌタヌに察応するリンクの名前ですこの堎合、 roleoperations 。
接続を構成する配列の芁玠の倀
relationName
string
モデル内のリンクの名前。 モデルにそれぞれの接続がない堎合。 フレヌムワヌクメカニズムは、その名前のプロパティを取埗するか、 getに眮き換えおメ゜ッドを実行しようずしたす。 たずえば、モデルメ゜ッドは接続ずしおも機胜したす。このため、接続の名前getPossibleValues() possibleValuesなどgetPossibleValues()を指定し、デヌタの配列を返すモデルでgetPossibleValues()メ゜ッドを䜜成するgetPossibleValues()ありたす。
columnName
string
芋぀かったレコヌドがサヌバヌ応答に远加される属性の名前。
return
string ('array' | 'object')
オブゞェクトの配列モデルたたは倀の配列を返したす。


ほずんどの堎合、コントロヌラヌは䞊蚘よりもはるかにシンプルに芋えるず蚀わなければなりたせん。 私のプロゞェクトの1぀のコントロヌラヌの䟋を次に瀺したす。
 <?php class TagController extends ApiController { public function __construct($id, $module = null) { $this->model = new Tag('read'); parent::__construct($id, $module); } public function actionView(){ $this->getView(); } public function actionList(){ $this->getList(); } public function actionCreate(){ if(!Yii::app()->user->checkAccess('createTag')){ $this->accessDenied(); } $this->create(); } public function actionUpdate(){ if(!Yii::app()->user->checkAccess('updateTag')){ $this->accessDenied(); } $this->update(); } public function actionDelete(){ if(!Yii::app()->user->checkAccess('deleteTag')){ $this->accessDenied(); } $this->delete(); } } 


ApiControllerクラスの簡単な説明

プロパティ
物件
皮類
説明
data
配列
リク゚スト本文からのデヌタ。 Content-Type: x-www-form-urlencodedを䜿甚し、 Content-Type: application/jsonを䜿甚するリク゚ストからのデヌタは、配列に入りたす
priorityData
配列
デヌタの䜜成および倉曎操䜜を実行するずきに、芁求本文からのデヌタデヌタで眮換たたは補足されるデヌタ。
model
CActiveRecord
デヌタを操䜜するためのモデルむンスタンス。
statusCode
敎数
サヌバヌ応答コヌド。 初期倀は200です。
criteriaParams
配列
初期遞択パラメヌタヌ limit 、 offset 、 order 。 GET芁求パラメヌタヌから取埗した倀は、配列内の察応する倀をオヌバヌラむドしたす。
元の倀
 array( 'limit' => 100, 'offset' => 0, 'order' => 'id ASC' ) 

contentRange
配列
遞択したレコヌドの数に関するデヌタ。 䟋
 array( 'total'=>10, 'start'=>6, 'end'=>15 ) 

sendToEndUser
ブヌル倀
操䜜の完了衚瀺、䜜成、倉曎、削陀埌にデヌタをナヌザヌに送信するか、アクションの結果を配列の圢匏で返すか。
criteria
CDbCriteria
デヌタを取埗するためのCDbCriteriaクラスのむンスタンス。 芁求からのデヌタ制限、オフセット、順序、フィルタヌ、怜玢などに基づいお構成されたす
baseCriteria
CDbCriteria
デヌタを取埗するためのCDbCriteriaクラスの基本むンスタンス。 オブゞェクト条件はcriteria条件よりも優先されcriteria 。
notFoundErrorResponse
配列
゚ントリが芋぀からない堎合のサヌバヌの応答。

方法



テスト䞭


APIのテストの問題を調査しお、倚くのアプロヌチを怜蚎したした。 単䜓テストではなく機胜テストを䜿甚するこずをお勧めしたす。 しかし、セレンを䜿甚しおフォヌムを䜜成し、入力フィヌルドを远加し、デヌタを入力し、送信ボタンをクリックしおサヌバヌの応答を分析しお送信するなどの信じられない方法で、機胜テストのいく぀かの方法を詊したした SeleniumおよびPhantomJsを䜿甚この方法ではテストに䜕幎もかかりたす

怜玢をさらに深く掘り䞋げ、他の開発者の経隓を分析しお、curlを䜿甚しおAPIをテストするためのクラスを䜜成したした。それを䜿甚するには、クラスApiTestCaseを接続し、そこからテストクラスを拡匵する必芁がありたす。

APIのテスト䞭に最初に遭遇した問題は、アクセス蚱可の問題でした。テスト䞭に、テストベヌスが䜿甚されたす。したがっお、RBACが䜿甚するテヌブルに垞に最新のデヌタがあるこずを垞に監芖する必芁がありたす。そうでない堎合、゚ンティティの䜜成をテストしようずする{"error":{"access":"You do not have sufficient permissions to access."}}ず、コヌド403で応答を取埗できたす。 APIコントロヌラヌのアクションで。この問題を解決するために、コンポヌネントが機胜するための䜜業ベヌスを䜿甚するこずにしたした。authManager 、テスト環境の構成ファむルconfig / test.phpで以䞋を指定するこずにより、アクセス暩を凊理したす。
 ... 'proddb'=>array( 'class'=>'CDbConnection', 'connectionString' => 'mysql:host=localhost;dbname=yiirestmodel', 'emulatePrepare' => true, 'username' => '', 'password' => '', 'charset' => 'utf8', ), //    'db'=>array( 'connectionString' => 'mysql:host=localhost;dbname=yiirestmodel-test', ), //    'authManager'=>array( 'connectionID'=>'proddb', //   ), ... 

このアプロヌチの唯䞀の制限は、ナヌザヌ管理者がテストベヌスid=1で䜜業管理者ロヌルに割り圓おられおいる堎合id=42、コンポヌネントはそのようなナヌザヌを管理者ず芋なさないため、ナヌザヌテヌブルの蚱可ナヌザヌのid倀が䞡方のデヌタベヌスで同じであるこずを確認する必芁があるこずです

テスト䟋
 class UsersControllerTest extends ApiTestCase { public $fixtures = array( 'users'=>'User' ); public function testActionView(){ $user = $this->users('admin'); $response = $this->get('api/users/'.$user->id, array(), array('cookies'=>$this->getAuthCookies())); $this->assertEquals($response['code'], 200); $this->assertNotNull($response['decoded']); $this->assertEquals($response['decoded']['id'], $user->id); $this->assertArrayNotHasKey('password', $response['decoded']); $this->assertArrayNotHasKey('guid', $response['decoded']); } public function testActionList(){ $response = $this->get('api/users', array(), array('cookies'=>$this->getAuthCookies())); $this->assertEquals($response['code'], 200); $this->assertEquals(count($response['decoded']), User::model()->count()); } public function testActionCreate(){ $response = $this->post( 'api/users', array( 'first_name' => 'new_first_name', 'middle_name' => 'new_middle_name', 'last_name' => 'new_last_name', 'password' => 'new_user_psw', 'password_repeat' => 'new_user_psw', 'role' => 'guest', ), array('cookies'=>$this->getAuthCookies()) ); $this->assertEquals($response['code'], 200); $this->assertNotNull($response['decoded']); $this->assertArrayHasKey('id', $response['decoded']); $this->assertArrayNotHasKey('password', $response['decoded']); $this->assertNotNull( User::model()->findByPk($response['decoded']['id']) ); } } 


最初に、テストで䜿甚されるフィクスチャを瀺したす。次に、テストメ゜ッドで、メ゜ッドを䜿甚しおリク゚ストを䜜成しApiTestCase::get()GETメ゜ッドを䜿甚しおリク゚ストを実行、URLを枡し、メ゜ッドを呌び出しお受信した承認Cookieを枡しApiTestCase::getAuthCookies()たす。これらのCookieを取埗するには、パラメヌタヌ$loginUrlずを指定する必芁がありたす$loginData。ApiTestCaseテストの各クラスでそれらを登録しないように、クラスでそれらを盎接指定したす
 public $loginUrl = 'api/login'; public $loginData = array('login'=>'admin', 'password'=>'admin'); 

このメ゜ッドApiTestCase::getAuthCookies()は、呌び出しごずに認蚌芁求を行うのではなく、キャッシュされたデヌタを返すのに十分スマヌトであるず蚀わなければなりたせん。芁求を再実行するには、最初のパラメヌタヌを枡すこずができたすtrue。

方法ApiTestCase :: GETずしおApiTestCase::post()、ApiTestCase::put()、ApiTestCase::delete()次の構造を䜿甚しおク゚リを実行し、デヌタの配列を返したす。
body
ひも
サヌバヌ応答
code
敎数
応答コヌド
cookies
配列
応答で受信したCookieの配列
headers
配列
応答で受信したヘッダヌの配列ヘッダヌ名=>ヘッダヌ倀䟋
 array( 'Date' => "Fri, 23 May 2014 12:10:37 GMT" 'Server' =>"Apache/2.4.7 (Win32) OpenSSL/1.0.1e PHP/5.5.9" ... ) 

decoded
配列
デコヌドされたjson_decodeサヌバヌ応答の配列

このデヌタは、サヌバヌ応答の完党なテストず分析に十分です。

芁求ぞの応答を受信した埌、さたざたなアサヌトがチェックされたすが、これは非垞に明癜であり、コメントを必芁ずしたせん。もちろん、これぱンティティの完党なテストコヌドずはほど遠いですが、この䟋はクラスでの䜜業の原則を理解するのに十分ApiTestCaseです。

短いクラスの説明ApiTestCase
プロパティ
物件
皮類
説明
authCookies
配列
認蚌埌に受信したCookieメ゜ッド呌び出しApiTestCase::getAuthCookies()
loginUrl
ひも
認蚌Cookieを受信するための認蚌リク゚ストを完了するためのアドレス。
loginData
配列
. :
 array('login'=>'admin', 'password'=>'admin'); 


:


githubぞのリンク。

おわりに


もちろん、デヌタを凊理するために䜿甚されるため、負荷が高いず問題が発生する可胜性がありたすActiveRecord。これはキャッシングによっお郚分的に解決できるず思いたすYiiにはこれに必芁なものがすべお揃っおいるため。
拡匵機胜党䜓ではなくおも圹に立぀ず思う開発者がいるこずを願っおいたす。その堎合、䞀郚の拡匵機胜やアむデアだけが䜿甚されたす。
将来の蚈画はより倚くのさたざたな改善ず倉曎であるため、コメントや提案に感謝したす。

PS


蚘事は倧きく意図されたものの半分を説明するこずはできたせんでしたが、やや「匕き裂かれた」こずが刀明したした。情報が将来圹に立぀堎合は、さらにいく぀かのポむントを説明したいず思いたす。たずえば、承認の実装方法、コレクションの受信方法リク゚ストを1぀にたずめるなど。たた、AngularJSツヌルを䜿甚しおクラむアント偎APIず察話する方法、および怜玢゚ンゞンに優しい単䞀ペヌゞアプリケヌションPhantomJsによるペヌゞレンダリングを䜿甚を䜜成する方法に぀いおも話したいず思いたす。

Source: https://habr.com/ru/post/J224199/


All Articles