Compare commits
304 Commits
Author | SHA1 | Date |
---|---|---|
Alexander Makarov | fb45bf2386 | 6 years ago |
Alexander Makarov | 86fbdcbe45 | 6 years ago |
Brandon Kelly | 1d5ae6de43 | 6 years ago |
Sergey Aksenov | 7f36d26f1c | 6 years ago |
Andrii Vasyliev | 08590596e5 | 6 years ago |
Erik Verheij | 6f19d3d4a9 | 6 years ago |
Paul Klimov | e28bf15754 | 6 years ago |
Andrii Vasyliev | d24d795785 | 6 years ago |
Andrii Vasyliev | 22c3126c1c | 6 years ago |
Brandon Kelly | e1a49cfb6d | 6 years ago |
Mahesh S Warrier | f903fb2c0a | 6 years ago |
Alexander Makarov | 705f3e4a80 | 6 years ago |
Alexander Makarov | c184ef864e | 6 years ago |
cjtterabyte | 8cd14e7d54 | 6 years ago |
cjtterabyte | 1c2320fd32 | 6 years ago |
Alexander Makarov | 95e3237054 | 6 years ago |
cjtterabyte | 921447a25a | 6 years ago |
cjtterabyte | 9014f92bc5 | 6 years ago |
Alexander Makarov | 32efb09902 | 6 years ago |
Carsten Brandt | 501ddbeac3 | 6 years ago |
SilverFire - Dmitry Naumenko | be8633702d | 6 years ago |
SilverFire - Dmitry Naumenko | 1cc5f15635 | 6 years ago |
SilverFire - Dmitry Naumenko | fa71f808c1 | 6 years ago |
Kuts Dmitriy | 6b99e4f2e4 | 6 years ago |
SilverFire - Dmitry Naumenko | 2468282154 | 6 years ago |
SilverFire - Dmitry Naumenko | b9ffea141c | 6 years ago |
SilverFire - Dmitry Naumenko | 4f33296491 | 6 years ago |
SilverFire - Dmitry Naumenko | 3e4936982e | 6 years ago |
SilverFire - Dmitry Naumenko | e164c07145 | 6 years ago |
Carsten Brandt | 290621ce4e | 7 years ago |
Leandro Gehlen | 892f1a8711 | 7 years ago |
Leandro Gehlen | b576336beb | 7 years ago |
Pavel Ivanov | c12e665dc8 | 7 years ago |
sashsvamir | 115fa3f746 | 7 years ago |
Carsten Brandt | 418fc416df | 7 years ago |
Carsten Brandt | 7eadd66639 | 7 years ago |
Roman Zhuravlev | 9546bd8187 | 7 years ago |
Dmitry Naumenko | 8ecc361f2b | 7 years ago |
Robert Korulczyk | 8a46b33ef7 | 7 years ago |
Pavel Ivanov | 3e25001a67 | 7 years ago |
Pavel Ivanov | b0b06b1ad3 | 7 years ago |
Pavel Ivanov | 2c6bc8ff1c | 7 years ago |
Pavel Ivanov | 741a569695 | 7 years ago |
Pavel Ivanov | b88bb2b9f7 | 7 years ago |
Pavel Ivanov | a0b3e2bc15 | 7 years ago |
Pavel Ivanov | e4285c59f1 | 7 years ago |
Pavel Ivanov | a7f0fea200 | 7 years ago |
Pavel Ivanov | 20fdf81711 | 7 years ago |
Pavel Ivanov | 93a3260a3a | 7 years ago |
Pavel Ivanov | 0babc4b299 | 7 years ago |
Pavel Ivanov | 893f2a7bbd | 7 years ago |
Pavel Ivanov | f5315185dc | 7 years ago |
Pavel Ivanov | 4e15d33756 | 7 years ago |
Pavel Ivanov | 8005748714 | 7 years ago |
xicond | faac174af8 | 7 years ago |
Klimov Paul | fdd761d6cb | 7 years ago |
Tobias Munk | 25ffc7cefd | 7 years ago |
Klimov Paul | 43c8f457ba | 7 years ago |
Klimov Paul | 854ae7127c | 7 years ago |
Klimov Paul | 8ce498cf65 | 7 years ago |
Klimov Paul | e74fbb1b0f | 7 years ago |
Andrii Vasyliev | d7cf629b10 | 7 years ago |
Klimov Paul | d1ff652b2f | 7 years ago |
Klimov Paul | b04e0e06dc | 7 years ago |
Klimov Paul | 732d0a841f | 7 years ago |
Paul Klimov | 603c084fbc | 7 years ago |
Paul Klimov | 13c37091d5 | 7 years ago |
Paul Klimov | 39e9ba74bc | 7 years ago |
Paul Klimov | 7cf9116fe6 | 7 years ago |
Paul Klimov | 8779f7a187 | 7 years ago |
Paul Klimov | 0a5304419b | 7 years ago |
Paul Klimov | 5e2c16da66 | 7 years ago |
Paul Klimov | fafa4044c0 | 7 years ago |
Paul Klimov | 0cb91006d5 | 7 years ago |
Paul Klimov | 445c838fec | 7 years ago |
Paul Klimov | c68e553879 | 7 years ago |
Paul Klimov | bc77937bfd | 7 years ago |
Paul Klimov | 86abef49ff | 7 years ago |
Paul Klimov | 624d39c43e | 7 years ago |
Paul Klimov | f8893fe6c8 | 7 years ago |
Paul Klimov | 3b11ce91cb | 7 years ago |
Paul Klimov | 0f6dbdd135 | 7 years ago |
SilverFire - Dmitry Naumenko | 41aa5d07ba | 7 years ago |
Paul Klimov | ba73528038 | 7 years ago |
Paul Klimov | 56545c420a | 7 years ago |
Paul Klimov | a8d2127ad4 | 7 years ago |
Klimov Paul | 481970957b | 7 years ago |
Klimov Paul | fdad1c98c5 | 7 years ago |
Klimov Paul | e19d7a7917 | 7 years ago |
Klimov Paul | 8e39886f62 | 7 years ago |
Klimov Paul | e31dda7f22 | 7 years ago |
Klimov Paul | d5117984b3 | 7 years ago |
Paul Klimov | 9ef1c44749 | 7 years ago |
Paul Klimov | 0a7327f87e | 7 years ago |
Paul Klimov | d86b716b08 | 7 years ago |
Paul Klimov | bccd260e9c | 7 years ago |
Paul Klimov | 4c21603cb2 | 7 years ago |
Paul Klimov | c3d8a3ddb4 | 7 years ago |
Paul Klimov | b44a461c2e | 7 years ago |
Paul Klimov | a8b1f69d0c | 7 years ago |
Klimov Paul | c2694e7edc | 7 years ago |
Klimov Paul | 3b4412f195 | 7 years ago |
Alec Pritchard | 523df834b0 | 7 years ago |
Klimov Paul | b01a749afd | 7 years ago |
Klimov Paul | a39d1209ad | 7 years ago |
Klimov Paul | 29ce1e8687 | 7 years ago |
Nikolay Oleynikov | 21da0ed910 | 7 years ago |
Klimov Paul | 7171db4e74 | 7 years ago |
Klimov Paul | fcc12fef65 | 7 years ago |
Klimov Paul | e7da8b52a0 | 7 years ago |
Klimov Paul | f1a738ec07 | 7 years ago |
Klimov Paul | 13d443a68a | 7 years ago |
Alexander Makarov | d5f1627868 | 7 years ago |
Klimov Paul | 359175c04b | 7 years ago |
Klimov Paul | 9b86c46842 | 7 years ago |
Vladimir Khramtsov | 0391a367ed | 7 years ago |
Alexander Makarov | 61dec0f021 | 7 years ago |
Nikolay Oleynikov | fd9384fc00 | 7 years ago |
Dmitry Naumenko | 866c70d62e | 7 years ago |
Vladimir Khramtsov | 646cd73548 | 7 years ago |
Klimov Paul | da364fa419 | 7 years ago |
Klimov Paul | 7e3f096bc0 | 7 years ago |
Klimov Paul | 3666f2c957 | 7 years ago |
Klimov Paul | 3ff4eade2f | 7 years ago |
Paul Klimov | bf116e6103 | 7 years ago |
Klimov Paul | 75349fdeb3 | 7 years ago |
Klimov Paul | 796aed8d16 | 7 years ago |
Paul Klimov | 874fcaaecb | 7 years ago |
Klimov Paul | a21c9fd8eb | 7 years ago |
Alexander Makarov | 87fde26d6d | 7 years ago |
Alexander Makarov | 9a802dbf34 | 7 years ago |
Klimov Paul | dcc74c841a | 7 years ago |
Klimov Paul | ef3e89e36c | 7 years ago |
Klimov Paul | 2fb0b05a0f | 7 years ago |
Alexander Makarov | 45eed055cb | 7 years ago |
Alexander Makarov | 11ecfc54f8 | 7 years ago |
Alexander Makarov | b997fdace6 | 7 years ago |
Alexander Makarov | ac167cbd10 | 7 years ago |
Alexander Makarov | 742e7b81f6 | 7 years ago |
Alexander Makarov | 66b6e48f1a | 7 years ago |
Alexander Makarov | b62077d581 | 7 years ago |
Elvira Sheina | 7ce415b8c7 | 7 years ago |
E.Alamo | 34865a7eb0 | 7 years ago |
243083df | 08cafaa2ee | 7 years ago |
243083df | cac4462e7a | 7 years ago |
Klimov Paul | c421a121d2 | 7 years ago |
Klimov Paul | 2cfbc38bf8 | 7 years ago |
Paul Klimov | 497a073dfd | 7 years ago |
Paul Klimov | a136f414ec | 7 years ago |
Klimov Paul | d039605dd1 | 7 years ago |
Klimov Paul | 82ea6a86ee | 7 years ago |
Klimov Paul | 3ff59d37ee | 7 years ago |
Klimov Paul | fcff67548c | 7 years ago |
Klimov Paul | a5b9af951e | 7 years ago |
Klimov Paul | 4488a6d9af | 7 years ago |
Klimov Paul | 83fa82aceb | 7 years ago |
Klimov Paul | ee81a042cf | 7 years ago |
Paul Klimov | e0a7baf631 | 7 years ago |
Klimov Paul | 8854793834 | 7 years ago |
Klimov Paul | e7943971d2 | 7 years ago |
Paul Klimov | e703645b4d | 7 years ago |
Klimov Paul | 5c7bdb32a6 | 7 years ago |
Klimov Paul | 257604f19e | 7 years ago |
Klimov Paul | 37580db6b1 | 7 years ago |
Klimov Paul | 722022f744 | 7 years ago |
Alexander Makarov | 3c16faa5b0 | 7 years ago |
Klimov Paul | 2e01076abc | 7 years ago |
Klimov Paul | 89c14b7dea | 7 years ago |
Alexander Makarov | 2e267461a1 | 7 years ago |
Alexander Makarov | 4f9c642d74 | 7 years ago |
Paul Klimov | e82b9c72f3 | 7 years ago |
Alexander Makarov | dc42a59b71 | 7 years ago |
Klimov Paul | 08e5e1e71b | 7 years ago |
Klimov Paul | c39a1ba4c3 | 7 years ago |
Paul Klimov | 3e6f8b1c19 | 7 years ago |
Paul Klimov | 4d9204d9f3 | 7 years ago |
Paul Klimov | 28b26fb74b | 7 years ago |
Ruitang Du | 003d83c6e0 | 7 years ago |
Paul Klimov | a015795c7b | 7 years ago |
Paul Klimov | f590670ac9 | 7 years ago |
Boudewijn Vahrmeijer | 209bcf0773 | 7 years ago |
Boudewijn Vahrmeijer | 6605d9830b | 7 years ago |
Boudewijn Vahrmeijer | 31a405bf80 | 7 years ago |
Alexander Makarov | 42a7f2b012 | 7 years ago |
Alexander Makarov | cc869d2587 | 7 years ago |
Alexander Makarov | 2875624600 | 7 years ago |
Alexander Makarov | 0b421596a0 | 7 years ago |
Alexander Makarov | e52e1124c1 | 7 years ago |
Alexander Makarov | 1b45ed9691 | 7 years ago |
Alexander Makarov | a94e804ae4 | 7 years ago |
Alexander Makarov | adca091fbf | 7 years ago |
Alexander Makarov | fc47291b7e | 7 years ago |
Alexander Makarov | 85df384e8f | 7 years ago |
Alexander Makarov | ceaf101151 | 7 years ago |
Alexander Makarov | 6100194bf7 | 7 years ago |
Alexander Makarov | d4e7cf0171 | 7 years ago |
Alexander Makarov | 3e688c1a6b | 7 years ago |
Alexander Makarov | d98f0aff1a | 7 years ago |
Alexander Makarov | 0297c4e2aa | 7 years ago |
Alexander Makarov | 2a9d4efc45 | 7 years ago |
Alexander Makarov | 207f8cc0d5 | 7 years ago |
Alexander Makarov | 66095fb89a | 7 years ago |
Alexander Makarov | f41399a6dc | 7 years ago |
Alexander Makarov | 7ef2cf55b3 | 7 years ago |
Alexander Makarov | 75dda6e08a | 7 years ago |
Angel Guevara | 0117459444 | 7 years ago |
Alexander Makarov | 51a5d78c95 | 7 years ago |
Alexander Makarov | c3bca69b5b | 7 years ago |
Alexander Makarov | 8e7ebfdad4 | 7 years ago |
Alexander Makarov | add189e5ef | 7 years ago |
Alexander Makarov | 68543a63ff | 7 years ago |
Alexander Makarov | acef7e1e7c | 7 years ago |
Alexander Makarov | f475affaeb | 7 years ago |
Alexander Makarov | 0afc410d12 | 7 years ago |
Bob van Leeuwen | 7c85258c36 | 7 years ago |
刘旭 | e310618168 | 8 years ago |
Alexander Makarov | 7aeb008a5d | 8 years ago |
Alexander Makarov | 1f97e03b4f | 8 years ago |
Alexander Makarov | 68dd3d4567 | 8 years ago |
Klimov Paul | 07f65f411b | 8 years ago |
Klimov Paul | aca84c9a7a | 8 years ago |
Klimov Paul | 498ecb3b4f | 8 years ago |
Klimov Paul | bd989566ff | 8 years ago |
Klimov Paul | e3f26cc5dc | 8 years ago |
Alexander Makarov | f7a4838026 | 8 years ago |
Alexander Makarov | 0aca17dcfb | 8 years ago |
Alexander Makarov | 575609f21d | 8 years ago |
Alexander Makarov | a21c4a8dc0 | 8 years ago |
Alexander Makarov | a7b9ef650c | 8 years ago |
Alexander Makarov | 9eabc170ad | 8 years ago |
Alexander Makarov | 736fb099fb | 8 years ago |
Alexander Makarov | d6892d554a | 8 years ago |
Alexey Rogachev | 62932ef321 | 8 years ago |
Alexey Rogachev | 409c56c5c5 | 8 years ago |
SilverFire - Dmitry Naumenko | 8ea211771c | 8 years ago |
SilverFire - Dmitry Naumenko | 7a47f5f8c1 | 8 years ago |
SilverFire - Dmitry Naumenko | 511784631b | 8 years ago |
SilverFire - Dmitry Naumenko | 2ab2fd96cc | 8 years ago |
SilverFire - Dmitry Naumenko | 7f8fdf9d56 | 8 years ago |
SilverFire - Dmitry Naumenko | b063323547 | 8 years ago |
Carsten Brandt | 55a827dad9 | 8 years ago |
boehsermoe | c2cd20ee49 | 8 years ago |
SilverFire - Dmitry Naumenko | a560db1a8f | 8 years ago |
SilverFire - Dmitry Naumenko | c64cfa9373 | 8 years ago |
SilverFire - Dmitry Naumenko | fcba5f3171 | 8 years ago |
SilverFire - Dmitry Naumenko | a94c2e611d | 8 years ago |
SilverFire - Dmitry Naumenko | a8be03f2b1 | 8 years ago |
SilverFire - Dmitry Naumenko | e45f5c3b4f | 8 years ago |
SilverFire - Dmitry Naumenko | 76e6a20cee | 8 years ago |
SilverFire - Dmitry Naumenko | 1e24d9a92e | 8 years ago |
SilverFire - Dmitry Naumenko | d335fd6ea3 | 8 years ago |
Michael Härtl | 224aac83ab | 8 years ago |
Dmitry Naumenko | 164f35afef | 8 years ago |
Robert Korulczyk | c597518378 | 8 years ago |
Robert Korulczyk | 41f5981561 | 8 years ago |
Robert Korulczyk | a77749a6a1 | 8 years ago |
Yordan Ivanov | 0d72e37f3c | 8 years ago |
Alexander Makarov | 2f53cee566 | 8 years ago |
Alexander Makarov | 46e68bf9ec | 8 years ago |
Dmitry Naumenko | 78224615ad | 8 years ago |
Boudewijn Vahrmeijer | 410ceab3ae | 8 years ago |
Dmitry Naumenko | eea58f3462 | 8 years ago |
Brandon Kelly | e94a46d720 | 8 years ago |
Alexander Makarov | b5c2407c1d | 8 years ago |
Alexander Makarov | f3478bb59b | 8 years ago |
Alexander Makarov | b53430f959 | 8 years ago |
Alexander Makarov | 2722b3a7cf | 8 years ago |
Alexander Makarov | 653362e6f8 | 8 years ago |
Alexander Makarov | f8cf1e2b6a | 8 years ago |
Carsten Brandt | 6791fdf1c4 | 8 years ago |
Carsten Brandt | aeadab5012 | 8 years ago |
Carsten Brandt | 8c03e6753b | 8 years ago |
Alexander Makarov | b764b42f51 | 8 years ago |
Carsten Brandt | fcfd54a28c | 8 years ago |
Carsten Brandt | 9b54694bb0 | 8 years ago |
Carsten Brandt | 98ed490c3d | 8 years ago |
Kirill Pomerantsev | 7f61cc81d8 | 8 years ago |
Alexander Makarov | c952e8052b | 8 years ago |
Alexander Makarov | 5ebdf6c29a | 8 years ago |
Lennart van den Dool | 30286387db | 8 years ago |
Lennart van den Dool | 79bdc5eb15 | 8 years ago |
Alexander Makarov | f8358acaeb | 8 years ago |
Alexander Makarov | a10365a3db | 8 years ago |
Carsten Brandt | 04949e1ab4 | 8 years ago |
Angel Guevara | f978199fcc | 9 years ago |
Angel Guevara | 35d7cacfbd | 9 years ago |
Angel Guevara | 0dee3509a8 | 9 years ago |
Carsten Brandt | 82d36df284 | 9 years ago |
Angel Guevara | e880ff2ff6 | 9 years ago |
Carsten Brandt | 25ac762d8c | 9 years ago |
Carsten Brandt | 9e830b1fbf | 9 years ago |
Carsten Brandt | b41e720bf8 | 9 years ago |
Carsten Brandt | 9cf7ffa186 | 9 years ago |
Carsten Brandt | 636591d284 | 9 years ago |
Carsten Brandt | 0888d6967b | 9 years ago |
Carsten Brandt | b503dc9ff2 | 9 years ago |
Carsten Brandt | 88a4f4ae9f | 9 years ago |
Alexander Makarov | c5efe32c28 | 9 years ago |
Alexander Makarov | 52d70732c1 | 9 years ago |
Alexander Makarov | 64e134c29c | 9 years ago |
Alexander Makarov | 6e947c84be | 9 years ago |
Alexander Makarov | 9b1a87b776 | 9 years ago |
Alexander Makarov | b05e01b2d4 | 9 years ago |
Alexander Makarov | 083db9792d | 9 years ago |
778 changed files with 15259 additions and 34426 deletions
@ -1,127 +0,0 @@ |
|||||||
Autenticación |
|
||||||
============= |
|
||||||
|
|
||||||
A diferencia de las aplicaciones Web, las API RESTful son usualmente sin estado (stateless), lo que permite que las sesiones o las cookies |
|
||||||
no sean usadas. Por lo tanto, cada petición debe llevar alguna suerte de credenciales de autenticación, |
|
||||||
porque la autenticación del usuario no puede ser mantenida por las sesiones o las cookies. Una práctica común |
|
||||||
es enviar una pieza (token) secreta de acceso con cada petición para autenticar al usuario. Dado que una pieza de autenticación |
|
||||||
puede ser usada para identificar y autenticar solamente a un usuario, **la API de peticiones tiene que ser siempre enviado |
|
||||||
vía HTTPS para prevenir ataques tipo "man-in-the-middle" (MitM) **. |
|
||||||
|
|
||||||
Hay muchas maneras de enviar una token (pieza) de acceso: |
|
||||||
|
|
||||||
* [Autenticación Básica HTTP](https://es.wikipedia.org/wiki/Autenticaci%C3%B3n_de_acceso_b%C3%A1sica): la pieza de acceso |
|
||||||
es enviada como nombre de usuario. Esto sólo debe de ser usado cuando la pieza de acceso puede ser guardada |
|
||||||
de forma segura en la parte del API del consumidor. Por ejemplo, el API del consumidor es un programa ejecutándose en un servidor. |
|
||||||
* Parámetro de la consulta: la pieza de acceso es enviada como un parámetro de la consulta en la URL de la API, p.e., |
|
||||||
`https://example.com/users?access-token=xxxxxxxx`. Debido que muchos servidores dejan los parámetros de consulta en los logs del servidor, |
|
||||||
esta aproximación suele ser usada principalmente para servir peticiones `JSONP` |
|
||||||
que no usen las cabeceras HTTP para enviar piezas de acceso. |
|
||||||
* [OAuth 2](http://oauth.net/2/): la pieza de acceso es obtenida por el consumidor por medio de una autorización del servidor |
|
||||||
y enviada al API del servidor según el protocolo |
|
||||||
OAuth 2 [tokens HTTP del portador](http://tools.ietf.org/html/rfc6750). |
|
||||||
|
|
||||||
Yii soporta todos los métodos anteriores de autenticación. Puedes crear nuevos métodos de autenticación de una forma fácil. |
|
||||||
|
|
||||||
Para activar la autenticación para tus APIs, sigue los pasos siguientes: |
|
||||||
|
|
||||||
1. Configura el componente `user` de la aplicación: |
|
||||||
- Define la propiedad [[yii\web\User::enableSession|enableSession]] como `false`. |
|
||||||
- Define la propiedad [[yii\web\User::loginUrl|loginUrl]] como `null` para mostrar un error HTTP 403 en vez de redireccionar a la pantalla de login. |
|
||||||
2. Especifica cuál método de autenticación planeas usar configurando el comportamiento (behavior) `authenticator` en tus |
|
||||||
clases de controladores REST. |
|
||||||
3. Implementa [[yii\web\IdentityInterface::findIdentityByAccessToken()]] en tu [[yii\web\User::identityClass|clase de identidad de usuarios]]. |
|
||||||
|
|
||||||
El paso 1 no es necesario pero sí recomendable para las APIs RESTful, pues son sin estado (stateless). |
|
||||||
Cuando [[yii\web\User::enableSession|enableSession]] es `false`, el estado de autenticación del usuario puede NO persistir entre peticiones usando sesiones. |
|
||||||
Si embargo, la autenticación será realizada para cada petición, lo que se consigue en los pasos 2 y 3. |
|
||||||
|
|
||||||
> Tip:Puedes configurar [[yii\web\User::enableSession|enableSession]] del componente de la aplicación `user` en la configuración |
|
||||||
> de las aplicaciones si estás desarrollando APIs RESTful en términos de un aplicación. Si desarrollas un módulo de las APIs RESTful, |
|
||||||
> puedes poner la siguiente línea en el método del módulo `init()`, tal y como sigue: |
|
||||||
> |
|
||||||
> ```php |
|
||||||
> public function init() |
|
||||||
> { |
|
||||||
> parent::init(); |
|
||||||
> \Yii::$app->user->enableSession = false; |
|
||||||
> } |
|
||||||
> ``` |
|
||||||
|
|
||||||
Por ejemplo, para usar HTTP Basic Auth, puedes configurar el comportamiento (behavior) `authenticator` como sigue, |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\filters\auth\HttpBasicAuth; |
|
||||||
|
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['authenticator'] = [ |
|
||||||
'class' => HttpBasicAuth::className(), |
|
||||||
]; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Si quieres implementar los tres métodos de autenticación explicados antes, puedes usar `CompositeAuth` de la siguiente manera, |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\filters\auth\CompositeAuth; |
|
||||||
use yii\filters\auth\HttpBasicAuth; |
|
||||||
use yii\filters\auth\HttpBearerAuth; |
|
||||||
use yii\filters\auth\QueryParamAuth; |
|
||||||
|
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['authenticator'] = [ |
|
||||||
'class' => CompositeAuth::className(), |
|
||||||
'authMethods' => [ |
|
||||||
HttpBasicAuth::className(), |
|
||||||
HttpBearerAuth::className(), |
|
||||||
QueryParamAuth::className(), |
|
||||||
], |
|
||||||
]; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Cada elemento en `authMethods` debe de ser el nombre de una clase de método de autenticación o un array de configuración. |
|
||||||
|
|
||||||
|
|
||||||
La implementación de `findIdentityByAccessToken()` es específico de la aplicación. Por ejemplo, en escenarios simples |
|
||||||
cuando cada usuario sólo puede tener un token de acceso, puedes almacenar este token en la columna `access_token` |
|
||||||
en la tabla de usuario. El método debe de ser inmediatamente implementado en la clase `User` como sigue, |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\db\ActiveRecord; |
|
||||||
use yii\web\IdentityInterface; |
|
||||||
|
|
||||||
class User extends ActiveRecord implements IdentityInterface |
|
||||||
{ |
|
||||||
public static function findIdentityByAccessToken($token, $type = null) |
|
||||||
{ |
|
||||||
return static::findOne(['access_token' => $token]); |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Después que la autenticación es activada, tal y como se describe arriba, para cada petición de la API, el controlador solicitado |
|
||||||
puede intentar autenticar al usuario en su evento `beforeAction()`. |
|
||||||
|
|
||||||
Si la autenticación tiene éxito, el controlador realizará otras comprobaciones (como son límite del ratio, autorización) |
|
||||||
y entonces ejecutar la acción. La identidad del usuario autenticado puede ser recuperada via `Yii::$app->user->identity`. |
|
||||||
|
|
||||||
Si la autenticación falla, una respuesta con estado HTTP 401 será devuelta junto con otras cabeceras apropiadas |
|
||||||
(tal como la cabecera para autenticación básica HTTP `WWW-Authenticate`). |
|
||||||
|
|
||||||
|
|
||||||
## Autorización <span id="authorization"></span> |
|
||||||
|
|
||||||
Después de que un usuario se ha autenticado, probablementer querrás comprobar si él o ella tiene los permisos para realizar |
|
||||||
la acción solicitada. Este proceso es llamado *autorización (authorization)* y está cubierto en detalle |
|
||||||
en la [Sección de Autorización](security-authorization.md). |
|
||||||
|
|
||||||
Si tus controladores extienden de [[yii\rest\ActiveController]], puedes sobreescribir |
|
||||||
el método [[yii\rest\ActiveController::checkAccess()|checkAccess()]] para realizar la comprobación de la autorización. |
|
||||||
El método será llamado por las acciones contenidas en [[yii\rest\ActiveController]]. |
|
@ -1,156 +0,0 @@ |
|||||||
Controladores |
|
||||||
============= |
|
||||||
|
|
||||||
Después de crear las clases de recursos y especificar cómo debe ser el formato de datos de recursos, el siguiente paso |
|
||||||
es crear acciones del controlador para exponer los recursos a los usuarios finales a través de las APIs RESTful. |
|
||||||
|
|
||||||
Yii ofrece dos clases controlador base para simplificar tu trabajo de crear acciones REST: |
|
||||||
[[yii\rest\Controller]] y [[yii\rest\ActiveController]]. La diferencia entre estos dos controladores |
|
||||||
es que este último proporciona un conjunto predeterminado de acciones que están específicamente diseñado para trabajar con |
|
||||||
los recursos representados como [Active Record](db-active-record.md). Así que si estás utilizando [Active Record](db-active-record.md) |
|
||||||
y te sientes cómodo con las acciones integradas provistas, podrías considerar extender tus controladores |
|
||||||
de [[yii\rest\ActiveController]], lo que te permitirá crear potentes APIs RESTful con un mínimo de código. |
|
||||||
|
|
||||||
Ambos [[yii\rest\Controller]] y [[yii\rest\ActiveController]] proporcionan las siguientes características, |
|
||||||
algunas de las cuales se describen en detalle en las siguientes secciones: |
|
||||||
|
|
||||||
* Método de Validación HTTP; |
|
||||||
* [Negociación de contenido y formato de datos](rest-response-formatting.md); |
|
||||||
* [Autenticación](rest-authentication.md); |
|
||||||
* [Límite de Rango](rest-rate-limiting.md). |
|
||||||
|
|
||||||
[[yii\rest\ActiveController]] además provee de las siguientes características: |
|
||||||
|
|
||||||
* Un conjunto de acciones comunes necesarias: `index`, `view`, `create`, `update`, `delete`, `options`; |
|
||||||
* La autorización del usuario de acuerdo a la acción y recurso solicitado. |
|
||||||
|
|
||||||
|
|
||||||
## Creando Clases de Controlador <span id="creating-controller"></span> |
|
||||||
|
|
||||||
Al crear una nueva clase de controlador, una convención para nombrar la clase del controlador es utilizar |
|
||||||
el nombre del tipo de recurso en singular. Por ejemplo, para servir información de usuario, |
|
||||||
el controlador puede ser nombrado como `UserController`. |
|
||||||
|
|
||||||
Crear una nueva acción es similar a crear una acción para una aplicación Web. La única diferencia |
|
||||||
es que en lugar de renderizar el resultado utilizando una vista llamando al método `render()`, para las acciones REST |
|
||||||
regresas directamente los datos. El [[yii\rest\Controller::serializer|serializer]] y el |
|
||||||
[[yii\web\Response|response object]] se encargarán de la conversión de los datos originales |
|
||||||
al formato solicitado. Por ejemplo, |
|
||||||
|
|
||||||
```php |
|
||||||
public function actionView($id) |
|
||||||
{ |
|
||||||
return User::findOne($id); |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Filtros <span id="filters"></span> |
|
||||||
|
|
||||||
La mayoría de las características API REST son proporcionadas por [[yii\rest\Controller]] son implementadas en los términos de [filtros](structure-filters.md). |
|
||||||
En particular, los siguientes filtros se ejecutarán en el orden en que aparecen: |
|
||||||
|
|
||||||
* [[yii\filters\ContentNegotiator|contentNegotiator]]: soporta la negociación de contenido, que se explica en |
|
||||||
la sección [Formateo de respuestas](rest-response-formatting.md); |
|
||||||
* [[yii\filters\VerbFilter|verbFilter]]: soporta métodos de validación HTTP; |
|
||||||
* [[yii\filters\auth\AuthMethod|authenticator]]: soporta la autenticación de usuarios, que se explica en |
|
||||||
la sección [Autenticación](rest-authentication.md); |
|
||||||
* [[yii\filters\RateLimiter|rateLimiter]]: soporta la limitación de rango, que se explica en |
|
||||||
la sección [Límite de Rango](rest-rate-limiting.md). |
|
||||||
|
|
||||||
Estos filtros se declaran nombrándolos en el método [[yii\rest\Controller::behaviors()|behaviors()]]. |
|
||||||
Puede sobrescribir este método para configurar filtros individuales, desactivar algunos de ellos, o añadir los tuyos. |
|
||||||
Por ejemplo, si sólo deseas utilizar la autenticación básica HTTP, puede escribir el siguiente código: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\filters\auth\HttpBasicAuth; |
|
||||||
|
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['authenticator'] = [ |
|
||||||
'class' => HttpBasicAuth::className(), |
|
||||||
]; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Extendiendo `ActiveController` <span id="extending-active-controller"></span> |
|
||||||
|
|
||||||
Si tu clase controlador extiende de [[yii\rest\ActiveController]], debe establecer |
|
||||||
su propiedad [[yii\rest\ActiveController::modelClass|modelClass]] con el nombre de la clase del recurso |
|
||||||
que planeas servir a través de este controlador. La clase debe extender de [[yii\db\ActiveRecord]]. |
|
||||||
|
|
||||||
|
|
||||||
### Personalizando Acciones <span id="customizing-actions"></span> |
|
||||||
|
|
||||||
Por defecto, [[yii\rest\ActiveController]] provee de las siguientes acciones: |
|
||||||
|
|
||||||
* [[yii\rest\IndexAction|index]]: listar recursos página por página; |
|
||||||
* [[yii\rest\ViewAction|view]]: devolver el detalle de un recurso específico; |
|
||||||
* [[yii\rest\CreateAction|create]]: crear un nuevo recurso; |
|
||||||
* [[yii\rest\UpdateAction|update]]: actualizar un recurso existente; |
|
||||||
* [[yii\rest\DeleteAction|delete]]: eliminar un recurso específico; |
|
||||||
* [[yii\rest\OptionsAction|options]]: devolver los métodos HTTP soportados. |
|
||||||
|
|
||||||
Todas esta acciones se declaran a través de método [[yii\rest\ActiveController::actions()|actions()]]. |
|
||||||
Puedes configurar estas acciones o desactivar alguna de ellas sobrescribiendo el método `actions()`, como se muestra a continuación, |
|
||||||
|
|
||||||
```php |
|
||||||
public function actions() |
|
||||||
{ |
|
||||||
$actions = parent::actions(); |
|
||||||
|
|
||||||
// disable the "delete" and "create" actions |
|
||||||
unset($actions['delete'], $actions['create']); |
|
||||||
|
|
||||||
// customize the data provider preparation with the "prepareDataProvider()" method |
|
||||||
$actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider']; |
|
||||||
|
|
||||||
return $actions; |
|
||||||
} |
|
||||||
|
|
||||||
public function prepareDataProvider() |
|
||||||
{ |
|
||||||
// prepare and return a data provider for the "index" action |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Por favor, consulta las referencias de clases de acciones individuales para aprender las opciones de configuración disponibles para cada una. |
|
||||||
|
|
||||||
|
|
||||||
### Realizando Comprobación de Acceso <span id="performing-access-check"></span> |
|
||||||
|
|
||||||
Al exponer los recursos a través de RESTful APIs, a menudo es necesario comprobar si el usuario actual tiene permiso |
|
||||||
para acceder y manipular el/los recurso solicitado/s. Con [[yii\rest\ActiveController]], esto puede lograrse |
|
||||||
sobrescribiendo el método [[yii\rest\ActiveController::checkAccess()|checkAccess()]] como a continuación, |
|
||||||
|
|
||||||
```php |
|
||||||
/** |
|
||||||
* Checks the privilege of the current user. |
|
||||||
* |
|
||||||
* This method should be overridden to check whether the current user has the privilege |
|
||||||
* to run the specified action against the specified data model. |
|
||||||
* If the user does not have access, a [[ForbiddenHttpException]] should be thrown. |
|
||||||
* |
|
||||||
* @param string $action the ID of the action to be executed |
|
||||||
* @param \yii\base\Model $model the model to be accessed. If `null`, it means no specific model is being accessed. |
|
||||||
* @param array $params additional parameters |
|
||||||
* @throws ForbiddenHttpException if the user does not have access |
|
||||||
*/ |
|
||||||
public function checkAccess($action, $model = null, $params = []) |
|
||||||
{ |
|
||||||
// check if the user can access $action and $model |
|
||||||
// throw ForbiddenHttpException if access should be denied |
|
||||||
if ($action === 'update' || $action === 'delete') { |
|
||||||
if ($model->author_id !== \Yii::$app->user->id) |
|
||||||
throw new \yii\web\ForbiddenHttpException(sprintf('You can only %s articles that you\'ve created.', $action)); |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
El método `checkAccess()` será llamado por defecto en las acciones predeterminadas de [[yii\rest\ActiveController]]. Si creas |
|
||||||
nuevas acciones y también deseas llevar a cabo la comprobación de acceso, debe llamar a este método de forma explícita en las nuevas acciones. |
|
||||||
|
|
||||||
> Tip: Puedes implementar `checkAccess()` mediante el uso del [Componente Role-Based Access Control (RBAC)](security-authorization.md). |
|
@ -1,94 +0,0 @@ |
|||||||
Manejo de errores |
|
||||||
================= |
|
||||||
|
|
||||||
Cuando se maneja una petición de API RESTful, si ocurre un error en la petición del usuario o si algo inesperado |
|
||||||
ocurre en el servidor, simplemente puedes lanzar una excepción para notificar al usuario que algo erróneo ocurrió. |
|
||||||
Si puedes identificar la causa del error (p.e., el recurso solicitado no existe), debes considerar lanzar una excepción |
|
||||||
con el código HTTP de estado apropiado (p.e., [[yii\web\NotFoundHttpException]] representa un código de estado 404). |
|
||||||
Yii enviará la respuesta a continuación con el correspondiente código de estado HTTP y el texto. Yii puede incluir también |
|
||||||
la representación serializada de la excepción en el cuerpo de la respuesta. |
|
||||||
Por ejemplo: |
|
||||||
|
|
||||||
``` |
|
||||||
HTTP/1.1 404 Not Found |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{ |
|
||||||
"name": "Not Found Exception", |
|
||||||
"message": "The requested resource was not found.", |
|
||||||
"code": 0, |
|
||||||
"status": 404 |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
La siguiente lista sumariza los códigos de estado HTTP que son usados por el framework REST: |
|
||||||
|
|
||||||
* `200`: OK. Todo ha funcionado como se esperaba. |
|
||||||
* `201`: El recurso ha creado con éxito en respuesta a la petición `POST`. La cabecera de situación `Location` |
|
||||||
contiene la URL apuntando al nuevo recurso creado. |
|
||||||
* `204`: La petición ha sido manejada con éxito y el cuerpo de la respuesta no tiene contenido (como una petición `DELETE`). |
|
||||||
* `304`: El recurso no ha sido modificado. Puede usar la versión en caché. |
|
||||||
* `400`: Petición errónea. Esto puede estar causado por varias acciones de el usuario, como proveer un JSON no válido |
|
||||||
en el cuerpo de la petición, proveyendo parámetros de acción no válidos, etc. |
|
||||||
* `401`: Autenticación fallida. |
|
||||||
* `403`: El usuario autenticado no tiene permitido acceder a la API final. |
|
||||||
* `404`: El recurso pedido no existe. |
|
||||||
* `405`: Método no permitido. Por favor comprueba la cabecera `Allow` por los métodos HTTP permitidos. |
|
||||||
* `415`: Tipo de medio no soportado. El tipo de contenido pedido o el número de versión no es válido. |
|
||||||
* `422`: La validación de datos ha fallado (en respuesta a una petición `POST` , por ejemplo). Por favor, comprueba en el cuerpo de la respuesta el mensaje detallado. |
|
||||||
* `429`: Demasiadas peticiones. La petición ha sido rechazada debido a un limitación de rango. |
|
||||||
* `500`: Error interno del servidor. Esto puede estar causado por errores internos del programa. |
|
||||||
|
|
||||||
|
|
||||||
## Personalizar la Respuesta al Error <span id="customizing-error-response"></span> |
|
||||||
|
|
||||||
A veces puedes querer personalizar el formato de la respuesta del error por defecto . Por ejemplo, en lugar de depender |
|
||||||
del uso de diferentes estados HTTP para indicar los diferentes errores, puedes querer usar siempre el estado HTTP 200 |
|
||||||
y encapsular el código de estado HTTP real como parte de una estructura JSON en la respuesta, como se muestra a continuación, |
|
||||||
|
|
||||||
``` |
|
||||||
HTTP/1.1 200 OK |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{ |
|
||||||
"success": false, |
|
||||||
"data": { |
|
||||||
"name": "Not Found Exception", |
|
||||||
"message": "The requested resource was not found.", |
|
||||||
"code": 0, |
|
||||||
"status": 404 |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Para lograrlo, puedes responder al evento `beforeSend` del componente `response` en la configuración de la aplicación: |
|
||||||
|
|
||||||
```php |
|
||||||
return [ |
|
||||||
// ... |
|
||||||
'components' => [ |
|
||||||
'response' => [ |
|
||||||
'class' => 'yii\web\Response', |
|
||||||
'on beforeSend' => function ($event) { |
|
||||||
$response = $event->sender; |
|
||||||
if ($response->data !== null && Yii::$app->request->get('suppress_response_code')) { |
|
||||||
$response->data = [ |
|
||||||
'success' => $response->isSuccessful, |
|
||||||
'data' => $response->data, |
|
||||||
]; |
|
||||||
$response->statusCode = 200; |
|
||||||
} |
|
||||||
}, |
|
||||||
], |
|
||||||
], |
|
||||||
]; |
|
||||||
``` |
|
||||||
|
|
||||||
El anterior código reformateará la respuesta (sea exitosa o fallida) como se explicó cuando |
|
||||||
`suppress_response_code` es pasado como un parámetro `GET`. |
|
@ -1,203 +0,0 @@ |
|||||||
Guía Breve |
|
||||||
========== |
|
||||||
|
|
||||||
Yii ofrece todo un conjunto de herramientas para simplificar la tarea de implementar un |
|
||||||
servicio web APIs RESTful. |
|
||||||
En particular, Yii soporta las siguientes características sobre APIs RESTful; |
|
||||||
|
|
||||||
* Prototipado rápido con soporte para APIs comunes para [Active Record](db-active-record.md); |
|
||||||
* Formato de respuesta de negocio (soporta JSON y XML por defecto); |
|
||||||
* Personalización de objetos serializados con soporte para campos de salida seleccionables; |
|
||||||
* Formateo apropiado de colecciones de datos y validación de errores; |
|
||||||
* Soporte para [HATEOAS](http://en.wikipedia.org/wiki/HATEOAS); |
|
||||||
* Eficiente enrutamiento con una adecuada comprobación del verbo(verb) HTTP; |
|
||||||
* Incorporado soporte para las `OPTIONS` y `HEAD` verbos; |
|
||||||
* Autenticación y autorización; |
|
||||||
* Cacheo de datos y cacheo HTTP; |
|
||||||
* Limitación de rango; |
|
||||||
|
|
||||||
|
|
||||||
A continuación, utilizamos un ejemplo para ilustrar como se puede construir un conjunto de APIs RESTful con un esfuerzo mínimo de codificación. |
|
||||||
|
|
||||||
Supongamos que deseas exponer los datos de los usuarios vía APIs RESTful. Los datos de usuario son almacenados en la tabla DB `user`, |
|
||||||
y ya tienes creado la clase [[yii\db\ActiveRecord|ActiveRecord]] `app\models\User` para acceder a los datos del usuario. |
|
||||||
|
|
||||||
|
|
||||||
## Creando un controlador <span id="creating-controller"></span> |
|
||||||
|
|
||||||
Primero, crea una clase controladora `app\controllers\UserController` como la siguiente, |
|
||||||
|
|
||||||
```php |
|
||||||
namespace app\controllers; |
|
||||||
|
|
||||||
use yii\rest\ActiveController; |
|
||||||
|
|
||||||
class UserController extends ActiveController |
|
||||||
{ |
|
||||||
public $modelClass = 'app\models\User'; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
La clase controladora extiende de [[yii\rest\ActiveController]]. Especificado por [[yii\rest\ActiveController::modelClass|modelClass]] |
|
||||||
como `app\models\User`, el controlador sabe que modelo puede ser usado para recoger y manipular sus datos. |
|
||||||
|
|
||||||
|
|
||||||
## Configurando las reglas de las URL <span id="configuring-url-rules"></span> |
|
||||||
|
|
||||||
A continuación, modifica la configuración del componente `urlManager` en la configuración de tu aplicación: |
|
||||||
|
|
||||||
```php |
|
||||||
'urlManager' => [ |
|
||||||
'enablePrettyUrl' => true, |
|
||||||
'enableStrictParsing' => true, |
|
||||||
'showScriptName' => false, |
|
||||||
'rules' => [ |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => 'user'], |
|
||||||
], |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
La configuración anterior principalmente añade una regla URL para el controlador `user` de manera |
|
||||||
que los datos de user pueden ser accedidos y manipulados con URLs amigables y verbos HTTP significativos. |
|
||||||
|
|
||||||
|
|
||||||
## Habilitando entradas JSON <span id="enabling-json-input"></span> |
|
||||||
|
|
||||||
Para permitir que la API acepte datos de entrada con formato JSON, configura la propiedad [[yii\web\Request::$parsers|parsers]] |
|
||||||
del componente de aplicación `request` para usar [[yii\web\JsonParser]] para entradas JSON: |
|
||||||
|
|
||||||
```php |
|
||||||
'request' => [ |
|
||||||
'parsers' => [ |
|
||||||
'application/json' => 'yii\web\JsonParser', |
|
||||||
] |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
> Tip: La configuración anterior es opcional. Sin la configuración anterior, la API sólo reconocería |
|
||||||
`application/x-www-form-urlencoded` y `multipart/form-data` como formatos de entrada. |
|
||||||
|
|
||||||
|
|
||||||
## Probándolo <span id="trying-it-out"></span> |
|
||||||
|
|
||||||
Con la mínima cantidad de esfuerzo, tienes ya finalizado tu tarea de crear las APIs RESTful |
|
||||||
para acceder a los datos de user. Las APIs que tienes creado incluyen: |
|
||||||
|
|
||||||
* `GET /users`: una lista de todos los usuarios página por página; |
|
||||||
* `HEAD /users`: muestra la información general de la lista de usuarios; |
|
||||||
* `POST /users`: crea un nuevo usuario; |
|
||||||
* `GET /users/123`: devuelve los detalles del usuario 123; |
|
||||||
* `HEAD /users/123`: muestra la información general del usuario 123; |
|
||||||
* `PATCH /users/123` y `PUT /users/123`: actualiza el usuario 123; |
|
||||||
* `DELETE /users/123`: elimina el usuario 123; |
|
||||||
* `OPTIONS /users`: muestra los verbos compatibles respecto al punto final `/users`; |
|
||||||
* `OPTIONS /users/123`: muestra los verbos compatibles respecto al punto final `/users/123`. |
|
||||||
|
|
||||||
> Info: Yii automáticamente pluraliza los nombres de los controladores para usarlo en los puntos finales. |
|
||||||
> Puedes configurar esto usando la propiedad [[yii\rest\UrlRule::$pluralize]]. |
|
||||||
|
|
||||||
Puedes acceder a tus APIs con el comando `curl` de la siguiente manera, |
|
||||||
|
|
||||||
``` |
|
||||||
$ curl -i -H "Accept:application/json" "http://localhost/users" |
|
||||||
|
|
||||||
HTTP/1.1 200 OK |
|
||||||
... |
|
||||||
X-Pagination-Total-Count: 1000 |
|
||||||
X-Pagination-Page-Count: 50 |
|
||||||
X-Pagination-Current-Page: 1 |
|
||||||
X-Pagination-Per-Page: 20 |
|
||||||
Link: <http://localhost/users?page=1>; rel=self, |
|
||||||
<http://localhost/users?page=2>; rel=next, |
|
||||||
<http://localhost/users?page=50>; rel=last |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
[ |
|
||||||
{ |
|
||||||
"id": 1, |
|
||||||
... |
|
||||||
}, |
|
||||||
{ |
|
||||||
"id": 2, |
|
||||||
... |
|
||||||
}, |
|
||||||
... |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Intenta cambiar el tipo de contenido aceptado para ser `application/xml`, y verá que el resultado |
|
||||||
se devuelve en formato XML: |
|
||||||
|
|
||||||
``` |
|
||||||
$ curl -i -H "Accept:application/xml" "http://localhost/users" |
|
||||||
|
|
||||||
HTTP/1.1 200 OK |
|
||||||
... |
|
||||||
X-Pagination-Total-Count: 1000 |
|
||||||
X-Pagination-Page-Count: 50 |
|
||||||
X-Pagination-Current-Page: 1 |
|
||||||
X-Pagination-Per-Page: 20 |
|
||||||
Link: <http://localhost/users?page=1>; rel=self, |
|
||||||
<http://localhost/users?page=2>; rel=next, |
|
||||||
<http://localhost/users?page=50>; rel=last |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/xml |
|
||||||
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<response> |
|
||||||
<item> |
|
||||||
<id>1</id> |
|
||||||
... |
|
||||||
</item> |
|
||||||
<item> |
|
||||||
<id>2</id> |
|
||||||
... |
|
||||||
</item> |
|
||||||
... |
|
||||||
</response> |
|
||||||
``` |
|
||||||
|
|
||||||
El siguiente comando creará un nuevo usuario mediante el envío de una petición POST con los datos del usuario en formato JSON: |
|
||||||
|
|
||||||
``` |
|
||||||
$ curl -i -H "Accept:application/json" -H "Content-Type:application/json" -XPOST "http://localhost/users" -d '{"username": "example", "email": "user@example.com"}' |
|
||||||
|
|
||||||
HTTP/1.1 201 Created |
|
||||||
... |
|
||||||
Location: http://localhost/users/1 |
|
||||||
Content-Length: 99 |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{"id":1,"username":"example","email":"user@example.com","created_at":1414674789,"updated_at":1414674789} |
|
||||||
``` |
|
||||||
|
|
||||||
> Tip: También puedes acceder a tus APIs a través del navegador web introduciendo la URL `http://localhost/users`. |
|
||||||
Sin embargo, es posible que necesites algunos plugins para el navegador para enviar cabeceras especificas en la petición. |
|
||||||
|
|
||||||
Como se puede ver, en las cabeceras de la respuesta, hay información sobre la cuenta total, número de páginas, etc. |
|
||||||
También hay enlaces que permiten navegar por otras páginas de datos. Por ejemplo, `http://localhost/users?page=2` |
|
||||||
le daría la página siguiente de los datos de usuario. |
|
||||||
|
|
||||||
Utilizando los parámetros `fields` y `expand`, puedes también especificar que campos deberían ser incluidos en el resultado. |
|
||||||
Por ejemplo, la URL `http://localhost/users?fields=id,email` sólo devolverá los campos `id` y `email`. |
|
||||||
|
|
||||||
|
|
||||||
> Info: Puedes haber notado que el resultado de `http://localhost/users` incluye algunos campos sensibles, |
|
||||||
> tal como `password_hash`, `auth_key`. Seguramente no quieras que éstos aparecieran en el resultado de tu API. |
|
||||||
> Puedes y deberías filtrar estos campos como se describe en la sección [Response Formatting](rest-response-formatting.md). |
|
||||||
|
|
||||||
|
|
||||||
## Resumen <span id="summary"></span> |
|
||||||
|
|
||||||
Utilizando el framework Yii API RESTful, implementa un punto final API en términos de una acción de un controlador, y utiliza |
|
||||||
un controlador para organizar las acciones que implementan los puntos finales para un sólo tipo de recurso. |
|
||||||
|
|
||||||
Los recursos son representados como modelos de datos que extienden de la clase [[yii\base\Model]]. |
|
||||||
Si estás trabajando con bases de datos (relacionales o NoSQL), es recomendable utilizar [[yii\db\ActiveRecord|ActiveRecord]] |
|
||||||
para representar los recursos. |
|
||||||
|
|
||||||
Puedes utilizar [[yii\rest\UrlRule]] para simplificar el enrutamiento de los puntos finales de tu API. |
|
||||||
|
|
||||||
Aunque no es obligatorio, es recomendable que desarrolles tus APIs RESTful como una aplicación separada, diferente de |
|
||||||
tu WEB front end y tu back end para facilitar el mantenimiento. |
|
@ -1,44 +0,0 @@ |
|||||||
Limitando el rango (rate) |
|
||||||
========================= |
|
||||||
|
|
||||||
Para prevenir el abuso, puedes considerar añadir un *límitación del rango (rate limiting)* para tus APIs. Por ejemplo, |
|
||||||
puedes querer limitar el uso del API de cada usuario a un máximo de 100 llamadas al API dentro de un periodo de 10 minutos. |
|
||||||
Si se reciben demasiadas peticiones de un usuario dentro del periodo de tiempo declarado, deveríá devolverse una respuesta con código de estado 429 (que significa "Demasiadas peticiones"). |
|
||||||
|
|
||||||
Para activar la limitación de rango, la clase [[yii\web\User::identityClass|user identity class]] debe implementar [[yii\filters\RateLimitInterface]]. |
|
||||||
Este interface requiere la implementación de tres métodos: |
|
||||||
|
|
||||||
* `getRateLimit()`: devuelve el número máximo de peticiones permitidas y el periodo de tiempo (p.e., `[100, 600]` significa que como mucho puede haber 100 llamadas al API dentro de 600 segundos). |
|
||||||
* `loadAllowance()`: devuelve el número de peticiones que quedan permitidas y el tiempo (fecha/hora) UNIX |
|
||||||
con el último límite del rango que ha sido comprobado. |
|
||||||
* `saveAllowance()`: guarda ambos, el número restante de peticiones permitidas y el tiempo actual (fecha/hora) UNIX . |
|
||||||
|
|
||||||
Puedes usar dos columnas en la tabla de usuario para guardar la información de lo permitido y la fecha/hora (timestamp). Con ambas definidas, |
|
||||||
entonces `loadAllowance()` y `saveAllowance()` pueden ser utilizados para leer y guardar los valores de las dos columnas correspondientes al actual usuario autenticado. |
|
||||||
Para mejorar el desempeño, también puedes considerar almacenar esas piezas de información en caché o almacenamiento NoSQL. |
|
||||||
|
|
||||||
Una vez que la clase de identidad implementa la interfaz requerida, Yii utilizará automáticamente [[yii\filters\RateLimiter]] |
|
||||||
configurado como un filtro de acción para que [[yii\rest\Controller]] compruebe el límite de rango. El limitador de rango |
|
||||||
lanzará una excepeción [[yii\web\TooManyRequestsHttpException]] cuando el límite del rango sea excedido. |
|
||||||
|
|
||||||
Puedes configurar el limitador de rango |
|
||||||
en tu clase controlador REST como sigue: |
|
||||||
|
|
||||||
```php |
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['rateLimiter']['enableRateLimitHeaders'] = false; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Cuando se activa el límite de rango, por defecto todas las respuestas serán enviadas con la siguiente cabecera HTTP conteniendo |
|
||||||
información sobre el límite actual de rango: |
|
||||||
|
|
||||||
* `X-Rate-Limit-Limit`, el máximo número de peticiones permitidas en un periodo de tiempo |
|
||||||
* `X-Rate-Limit-Remaining`, el número de peticiones restantes en el periodo de tiempo actual |
|
||||||
* `X-Rate-Limit-Reset`, el número de segundos a esperar para pedir el máximo número de peticiones permitidas |
|
||||||
|
|
||||||
Puedes desactivar estas cabeceras configurando [[yii\filters\RateLimiter::enableRateLimitHeaders]] a `false`, |
|
||||||
tal y como en el anterior ejemplo. |
|
@ -1,190 +0,0 @@ |
|||||||
Recursos |
|
||||||
========= |
|
||||||
|
|
||||||
Las APIs RESTful lo son todos para acceder y manipular *recursos (resources)*. Puedes observar los recursos en el paradigma MVC en [Modelos (models)](structure-models.md) . |
|
||||||
|
|
||||||
Mientras que no hay restricción a cómo representar un recurso, en YII usualmente, puedes representar recursos como objetos de la clase [[yii\base\Model]] o sus clases hijas (p.e. [[yii\db\ActiveRecord]]), por las siguientes razones: |
|
||||||
|
|
||||||
* [[yii\base\Model]] implementa el interface [[yii\base\Arrayable]] , el cual te permite personalizar como exponer los datos de los recursos a travès de las APIs RESTful. |
|
||||||
* [[yii\base\Model]] soporta [Validación de entrada (input validation)](input-validation.md), lo cual es muy usado en las APIs RESTful para soportar la entrada de datos. |
|
||||||
* [[yii\db\ActiveRecord]] provee un poderoso soporte para el acceso a datos en bases de datos y su manipulación, lo que lo le hace servir perfectamente si sus recursos de datos están en bases de datos. |
|
||||||
|
|
||||||
En esta sección, vamos principalmente a describir como la clase con recursos que extiende de [[yii\base\Model]] (o sus clases hijas) puede especificar qué datos puede ser devueltos vía las APIs RESTful. Si la clase de los recursos no extiende de [[yii\base\Model]], entonces todas las variables públicas miembro serán devueltas. |
|
||||||
|
|
||||||
|
|
||||||
## Campos (fields) <span id="fields"></span> |
|
||||||
|
|
||||||
Cuando incluimos un recurso en una respuesta de la API RESTful, el recurso necesita ser serializado en una cadena. |
|
||||||
Yii divide este proceso en dos pasos. Primero, el recurso es convertido en un array por [[yii\rest\Serializer]]. |
|
||||||
Segundo, el array es serializado en una cadena en el formato requerido (p.e. JSON, XML) por [[yii\web\ResponseFormatterInterface|response formatters]]. El primer paso es en el que debes de concentrarte principalmente cuando desarrolles una clase de un recurso. |
|
||||||
|
|
||||||
Sobreescribiendo [[yii\base\Model::fields()|fields()]] y/o [[yii\base\Model::extraFields()|extraFields()]], |
|
||||||
puedes especificar qué datos, llamados *fields*, en el recursos, pueden ser colocados en el array que le representa. |
|
||||||
La diferencia entre estos dos métodos es que el primero especifica el conjunto de campos por defecto que deben ser incluidos en el array que los representa, mientras que el último especifica campos adicionales que deben de ser incluidos en el array si una petición del usuario final para ellos vía el parámetro de consulta `expand`. Por ejemplo, |
|
||||||
|
|
||||||
``` |
|
||||||
// devuelve todos los campos declarados en fields() |
|
||||||
http://localhost/users |
|
||||||
|
|
||||||
// sólo devuelve los campos id y email, provistos por su declaración en fields() |
|
||||||
http://localhost/users?fields=id,email |
|
||||||
|
|
||||||
// devuelve todos los campos en fields() y el campo profile siempre y cuando esté declarado en extraFields() |
|
||||||
http://localhost/users?expand=profile |
|
||||||
|
|
||||||
// sólo devuelve los campos id, email y profile, siempre y cuando ellos estén declarados en fields() y extraFields() |
|
||||||
http://localhost/users?fields=id,email&expand=profile |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
### Sobreescribiendo `fields()` <span id="overriding-fields"></span> |
|
||||||
|
|
||||||
Por defecto, [[yii\base\Model::fields()]] devuelve todos los atributos de los modelos como si fueran campos, mientras [[yii\db\ActiveRecord::fields()]] sólo devuelve los atributos que tengan datos en la base de datos. |
|
||||||
|
|
||||||
Puedes sobreescribir `fields()` para añadir, quitar, renombrar o redefinir campos. El valor de retorno de `fields()` ha de estar en un array. Las claves del array son los nombres de los campos y los valores del array son las correspondientes definiciones de los campos que pueden ser tanto nombres de propiedades/atributos o funciones anónimas que devuelven los correspondientes valores del los campos. En el caso especial de que el nombre de un campo sea el mismo que su definición puedes omitir la clave en el array. Por ejemplo, |
|
||||||
|
|
||||||
```php |
|
||||||
// explícitamente lista cada campo, siendo mejor usarlo cuando quieras asegurarte que los cambios |
|
||||||
// en una tabla de la base de datos o en un atributo del modelo no provoque el cambio de tu campo (para mantener la compatibilidad anterior). |
|
||||||
public function fields() |
|
||||||
{ |
|
||||||
return [ |
|
||||||
// el nombre de campo es el mismo nombre del atributo |
|
||||||
'id', |
|
||||||
// el nombre del campo es "email", su atributo se denomina "email_address" |
|
||||||
'email' => 'email_address', |
|
||||||
// el nombre del campo es "name", su valor es definido está definido por una función anónima de retrollamada (callback) |
|
||||||
'name' => function () { |
|
||||||
return $this->first_name . ' ' . $this->last_name; |
|
||||||
}, |
|
||||||
]; |
|
||||||
} |
|
||||||
|
|
||||||
// el ignorar algunos campos, es mejor usarlo cuando heredas de una implementación padre |
|
||||||
// y pones en la lista negra (blacklist) algunos campos sensibles |
|
||||||
public function fields() |
|
||||||
{ |
|
||||||
$fields = parent::fields(); |
|
||||||
|
|
||||||
// quita los campos con información sensible |
|
||||||
unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']); |
|
||||||
|
|
||||||
return $fields; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
> Warning: Dado que, por defecto, todos los atributos de un modelo pueden ser incluidos en la devolución del API, debes |
|
||||||
> examinar tus datos para estar seguro de que no contiene información sensible. Si se da este tipo de información, |
|
||||||
> debes sobreescribir `fields()` para filtrarlos. En el ejemplo anterior, escogemos |
|
||||||
> quitar `auth_key`, `password_hash` y `password_reset_token`. |
|
||||||
|
|
||||||
|
|
||||||
### Sobreescribiendo `extraFields()` <span id="overriding-extra-fields"></span> |
|
||||||
|
|
||||||
Por defecto, [[yii\base\Model::extraFields()]] no devuelve nada, mientras que [[yii\db\ActiveRecord::extraFields()]] devuelve los nombres de las relaciones que tienen datos (populated) obtenidos de la base de datos. |
|
||||||
|
|
||||||
El formato de devolución de los datos de `extraFields()` es el mismo que el de `fields()`. Usualmente, `extraFields()` es principalmente usado para especificar campos cuyos valores sean objetos. Por ejemplo, dado la siguiente declaración de campo, |
|
||||||
|
|
||||||
```php |
|
||||||
public function fields() |
|
||||||
{ |
|
||||||
return ['id', 'email']; |
|
||||||
} |
|
||||||
|
|
||||||
public function extraFields() |
|
||||||
{ |
|
||||||
return ['profile']; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
la petición `http://localhost/users?fields=id,email&expand=profile` puede devolver los siguientes datos en formato JSON : |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
{ |
|
||||||
"id": 100, |
|
||||||
"email": "100@example.com", |
|
||||||
"profile": { |
|
||||||
"id": 100, |
|
||||||
"age": 30, |
|
||||||
} |
|
||||||
}, |
|
||||||
... |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Enlaces (Links) <span id="links"></span> |
|
||||||
|
|
||||||
[HATEOAS](http://en.wikipedia.org/wiki/HATEOAS), es una abreviación de Hipermedia es el Motor del Estado de la Aplicación (Hypermedia as the Engine of Application State), promueve que las APIs RESTfull devuelvan información que permita a los clientes descubrir las acciones que soportan los recursos devueltos. El sentido de HATEOAS es devolver un conjunto de hiperenlaces con relación a la información, cuando los datos de los recursos son servidos por las APIs. |
|
||||||
|
|
||||||
Las clases con recursos pueden soportar HATEOAS implementando el interfaz [[yii\web\Linkable]] . El interfaz contiene sólo un método [[yii\web\Linkable::getLinks()|getLinks()]] el cual debe de de devolver una lista de [[yii\web\Link|links]]. |
|
||||||
Típicamente, debes devolver al menos un enlace `self` representando la URL al mismo recurso objeto. Por ejemplo, |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\db\ActiveRecord; |
|
||||||
use yii\web\Link; |
|
||||||
use yii\web\Linkable; |
|
||||||
use yii\helpers\Url; |
|
||||||
|
|
||||||
class User extends ActiveRecord implements Linkable |
|
||||||
{ |
|
||||||
public function getLinks() |
|
||||||
{ |
|
||||||
return [ |
|
||||||
Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true), |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Cuando un objeto `User` es devuelto en una respuesta, puede contener un elemento `_links` representando los enlaces relacionados con el usuario, por ejemplo, |
|
||||||
|
|
||||||
``` |
|
||||||
{ |
|
||||||
"id": 100, |
|
||||||
"email": "user@example.com", |
|
||||||
// ... |
|
||||||
"_links" => { |
|
||||||
"self": { |
|
||||||
"href": "https://example.com/users/100" |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Colecciones <span id="collections"></span> |
|
||||||
|
|
||||||
Los objetos de los recursos pueden ser agrupados en *collections*. Cada colección contiene una lista de recursos objeto del mismo tipo. |
|
||||||
|
|
||||||
Las colecciones pueden ser representadas como arrays pero, es usualmente más deseable representarlas como [proveedores de datos (data providers)](output-data-providers.md). Esto es así porque los proveedores de datos soportan paginación y ordenación de los recursos, lo cual es comunmente necesario en las colecciones devueltas con las APIs RESTful. Por ejemplo, la siguiente acción devuelve un proveedor de datos sobre los recursos post: |
|
||||||
|
|
||||||
```php |
|
||||||
namespace app\controllers; |
|
||||||
|
|
||||||
use yii\rest\Controller; |
|
||||||
use yii\data\ActiveDataProvider; |
|
||||||
use app\models\Post; |
|
||||||
|
|
||||||
class PostController extends Controller |
|
||||||
{ |
|
||||||
public function actionIndex() |
|
||||||
{ |
|
||||||
return new ActiveDataProvider([ |
|
||||||
'query' => Post::find(), |
|
||||||
]); |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Cuando un proveedor de datos está enviando una respuesta con el API RESTful, [[yii\rest\Serializer]] llevará la actual página de los recursos y los serializa como un array de recursos objeto. Adicionalmente, [[yii\rest\Serializer]] |
|
||||||
puede incluir también la información de paginación a través de las cabeceras HTTP siguientes: |
|
||||||
|
|
||||||
* `X-Pagination-Total-Count`: Número total de recursos; |
|
||||||
* `X-Pagination-Page-Count`: Número de páginas; |
|
||||||
* `X-Pagination-Current-Page`: Página actual (iniciando en 1); |
|
||||||
* `X-Pagination-Per-Page`: Número de recursos por página; |
|
||||||
* `Link`: Un conjunto de enlaces de navegación permitiendo al cliente recorrer los recursos página a página. |
|
||||||
|
|
||||||
Un ejemplo se puede ver en la sección [Inicio rápido (Quick Start)](rest-quick-start.md#trying-it-out). |
|
@ -1,157 +0,0 @@ |
|||||||
Formato de Respuesta |
|
||||||
==================== |
|
||||||
|
|
||||||
Cuando se maneja una petición al API RESTful, una aplicación realiza usualmente los siguientes pasos que están relacionados |
|
||||||
con el formato de la respuesta: |
|
||||||
|
|
||||||
1. Determinar varios factores que pueden afectar al formato de la respuesta, como son el tipo de medio, lenguaje, versión, etc. |
|
||||||
Este proceso es también conocido como [negociación de contenido (content negotiation)](http://en.wikipedia.org/wiki/Content_negotiation). |
|
||||||
2. La conversión de objetos recurso en arrays, como está descrito en la sección [Recursos (Resources)](rest-resources.md). |
|
||||||
Esto es realizado por la clase [[yii\rest\Serializer]]. |
|
||||||
3. La conversión de arrays en cadenas con el formato determinado por el paso de negociación de contenido. Esto es |
|
||||||
realizado por los [[yii\web\ResponseFormatterInterface|formatos de respuesta]] registrados |
|
||||||
con la propiedad [[yii\web\Response::formatters|formatters]] del |
|
||||||
[componente de la aplicación](structure-application-components.md) `response`. |
|
||||||
|
|
||||||
|
|
||||||
## Negociación de contenido (Content Negotiation) <span id="content-negotiation"></span> |
|
||||||
|
|
||||||
Yii soporta la negociación de contenido a través del filtro [[yii\filters\ContentNegotiator]]. La clase de controlador base |
|
||||||
del API RESTful [[yii\rest\Controller]] está equipada con este filtro bajo el nombre `contentNegotiator`. |
|
||||||
El filtro provee tanto un formato de respuesta de negociación como una negociación de lenguaje. Por ejemplo, si la petición API RESTful |
|
||||||
contiene la siguiente cabecera, |
|
||||||
|
|
||||||
``` |
|
||||||
Accept: application/json; q=1.0, */*; q=0.1 |
|
||||||
``` |
|
||||||
|
|
||||||
puede obtener una respuesta en formato JSON, como a continuación: |
|
||||||
|
|
||||||
``` |
|
||||||
$ curl -i -H "Accept: application/json; q=1.0, */*; q=0.1" "http://localhost/users" |
|
||||||
|
|
||||||
HTTP/1.1 200 OK |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
X-Powered-By: PHP/5.4.20 |
|
||||||
X-Pagination-Total-Count: 1000 |
|
||||||
X-Pagination-Page-Count: 50 |
|
||||||
X-Pagination-Current-Page: 1 |
|
||||||
X-Pagination-Per-Page: 20 |
|
||||||
Link: <http://localhost/users?page=1>; rel=self, |
|
||||||
<http://localhost/users?page=2>; rel=next, |
|
||||||
<http://localhost/users?page=50>; rel=last |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
[ |
|
||||||
{ |
|
||||||
"id": 1, |
|
||||||
... |
|
||||||
}, |
|
||||||
{ |
|
||||||
"id": 2, |
|
||||||
... |
|
||||||
}, |
|
||||||
... |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Detrás de escena, antes de que sea ejecutada una acción del controlador del API RESTful, el filtro [[yii\filters\ContentNegotiator]] |
|
||||||
comprobará la cabecera HTTP `Accept` de la petición y definirá el [[yii\web\Response::format|response format]] |
|
||||||
como `'json'`. Después de que la acción sea ejecutada y devuelva el objeto recurso o la colección resultante, |
|
||||||
[[yii\rest\Serializer]] convertirá el resultado en un array. Y finalmente, [[yii\web\JsonResponseFormatter]] |
|
||||||
serializará el array en una cadena JSON incluyéndola en el cuerpo de la respuesta. |
|
||||||
|
|
||||||
Por defecto, el API RESTful soporta tanto el formato JSON como el XML. Para soportar un nuevo formato, debes configurar |
|
||||||
la propiedad [[yii\filters\ContentNegotiator::formats|formats]] del filtro `contentNegotiator` tal y como sigue, |
|
||||||
en las clases del controlador del API: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\web\Response; |
|
||||||
|
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_HTML; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Las claves de la propiedad `formats` son los tipos MIME soportados, mientras que los valores son los nombres de formato de respuesta correspondientes, |
|
||||||
los cuales deben ser soportados en [[yii\web\Response::formatters]]. |
|
||||||
|
|
||||||
|
|
||||||
## Serialización de Datos <span id="data-serializing"></span> |
|
||||||
|
|
||||||
Como hemos descrito antes, [[yii\rest\Serializer]] es la pieza central responsable de convertir |
|
||||||
objetos recurso o colecciones en arrays. Reconoce objetos tanto implementando [[yii\base\ArrayableInterface]] |
|
||||||
como [[yii\data\DataProviderInterface]]. El primer formateador es implementado principalmente para objetos recursos, |
|
||||||
mientras que el segundo para recursos collección. |
|
||||||
|
|
||||||
Puedes configurar el serializador definiendo la propiedad [[yii\rest\Controller::serializer]] con un array de configuración. |
|
||||||
Por ejemplo, a veces puedes querer ayudar a simplificar el trabajo de desarrollo del cliente incluyendo información de la paginación |
|
||||||
directamente en el cuerpo de la respuesta. Para hacer esto, configura la propiedad [[yii\rest\Serializer::collectionEnvelope]] |
|
||||||
como sigue: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\rest\ActiveController; |
|
||||||
|
|
||||||
class UserController extends ActiveController |
|
||||||
{ |
|
||||||
public $modelClass = 'app\models\User'; |
|
||||||
public $serializer = [ |
|
||||||
'class' => 'yii\rest\Serializer', |
|
||||||
'collectionEnvelope' => 'items', |
|
||||||
]; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Puedes obtener la respuesta que sigue para la petición `http://localhost/users`: |
|
||||||
|
|
||||||
``` |
|
||||||
HTTP/1.1 200 OK |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
X-Powered-By: PHP/5.4.20 |
|
||||||
X-Pagination-Total-Count: 1000 |
|
||||||
X-Pagination-Page-Count: 50 |
|
||||||
X-Pagination-Current-Page: 1 |
|
||||||
X-Pagination-Per-Page: 20 |
|
||||||
Link: <http://localhost/users?page=1>; rel=self, |
|
||||||
<http://localhost/users?page=2>; rel=next, |
|
||||||
<http://localhost/users?page=50>; rel=last |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{ |
|
||||||
"items": [ |
|
||||||
{ |
|
||||||
"id": 1, |
|
||||||
... |
|
||||||
}, |
|
||||||
{ |
|
||||||
"id": 2, |
|
||||||
... |
|
||||||
}, |
|
||||||
... |
|
||||||
], |
|
||||||
"_links": { |
|
||||||
"self": { |
|
||||||
"href": "http://localhost/users?page=1" |
|
||||||
}, |
|
||||||
"next": { |
|
||||||
"href": "http://localhost/users?page=2" |
|
||||||
}, |
|
||||||
"last": { |
|
||||||
"href": "http://localhost/users?page=50" |
|
||||||
} |
|
||||||
}, |
|
||||||
"_meta": { |
|
||||||
"totalCount": 1000, |
|
||||||
"pageCount": 50, |
|
||||||
"currentPage": 1, |
|
||||||
"perPage": 20 |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
@ -1,92 +0,0 @@ |
|||||||
Enrutamiento |
|
||||||
============ |
|
||||||
|
|
||||||
Con las clases de controlador y recurso preparadas, puedes acceder a los recursos usando una URL como |
|
||||||
`http://localhost/index.php?r=user/create`, parecida a la que usas con aplicaciones Web normales. |
|
||||||
|
|
||||||
En la práctica, querrás usualmente usar URLs limpias y obtener ventajas de los verbos HTTP. |
|
||||||
Por ejemplo, una petición `POST /users` significaría acceder a la acción `user/create`. |
|
||||||
Esto puede realizarse fácilmente configurando el componente de la aplicación `urlManager` |
|
||||||
como sigue: |
|
||||||
|
|
||||||
```php |
|
||||||
'urlManager' => [ |
|
||||||
'enablePrettyUrl' => true, |
|
||||||
'enableStrictParsing' => true, |
|
||||||
'showScriptName' => false, |
|
||||||
'rules' => [ |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => 'user'], |
|
||||||
], |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
En comparación con la gestión de URL en las aplicaciones Web, lo principalmente nuevo de lo anterior es el uso de |
|
||||||
[[yii\rest\UrlRule]] para el enrutamiento de las peticiones con el API RESTful. Esta clase especial de regla URL creará |
|
||||||
un conjunto completo de reglas URL hijas para soportar el enrutamiento y creación de URL para el/los controlador/es especificados. |
|
||||||
Por ejemplo, el código anterior es equivalente a las siguientes reglas: |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'PUT,PATCH users/<id>' => 'user/update', |
|
||||||
'DELETE users/<id>' => 'user/delete', |
|
||||||
'GET,HEAD users/<id>' => 'user/view', |
|
||||||
'POST users' => 'user/create', |
|
||||||
'GET,HEAD users' => 'user/index', |
|
||||||
'users/<id>' => 'user/options', |
|
||||||
'users' => 'user/options', |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Y los siguientes puntos finales del API son mantenidos por esta regla: |
|
||||||
|
|
||||||
* `GET /users`: lista de todos los usuarios página a página; |
|
||||||
* `HEAD /users`: muestra ĺa información resumén del usuario listado; |
|
||||||
* `POST /users`: crea un nuevo usuario; |
|
||||||
* `GET /users/123`: devuelve los detalles del usuario 123; |
|
||||||
* `HEAD /users/123`: muestra la información resúmen del usuario 123; |
|
||||||
* `PATCH /users/123` y `PUT /users/123`: actualizan al usuario 123; |
|
||||||
* `DELETE /users/123`: borra el usuario 123; |
|
||||||
* `OPTIONS /users`: muestra los verbos soportados de acuerdo al punto final `/users`; |
|
||||||
* `OPTIONS /users/123`: muestra los verbos soportados de acuerdo al punto final `/users/123`. |
|
||||||
|
|
||||||
Puedes configurar las opciones `only` y `except` para explícitamente listar cuáles acciones a soportar o cuáles |
|
||||||
deshabilitar, respectivamente. Por ejemplo, |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'class' => 'yii\rest\UrlRule', |
|
||||||
'controller' => 'user', |
|
||||||
'except' => ['delete', 'create', 'update'], |
|
||||||
], |
|
||||||
``` |
|
||||||
|
|
||||||
También puedes configurar las propiedades `patterns` o `extraPatterns` para redifinir patrones existentes o añadir nuevos soportados por esta regla. |
|
||||||
Por ejemplo, para soportar una nueva acción `search` por el punto final `GET /users/search`, configura la opción `extraPatterns` como sigue, |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'class' => 'yii\rest\UrlRule', |
|
||||||
'controller' => 'user', |
|
||||||
'extraPatterns' => [ |
|
||||||
'GET search' => 'search', |
|
||||||
], |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Puedes haber notado que el ID del controlador `user` aparece en formato plural `users` en los puntos finales de las URLs. |
|
||||||
Esto se debe a que [[yii\rest\UrlRule]] automáticamente pluraliza los IDs de los controladores al crear reglas URL hijas. |
|
||||||
Puedes desactivar este comportamiento definiendo la propiedad [[yii\rest\UrlRule::pluralize]] como `false`. |
|
||||||
|
|
||||||
> Info: La pluralización de los IDs de los controladores es realizada por [[yii\helpers\Inflector::pluralize()]]. Este método respeta |
|
||||||
reglas especiales de pluralización. Por ejemplo, la palabra `box` (caja) será pluralizada como `boxes` en vez de `boxs`. |
|
||||||
|
|
||||||
En caso de que la pluralización automática no encaje en tus requerimientos, puedes además configurar la propiedad |
|
||||||
[[yii\rest\UrlRule::controller]] para especificar exlpícitamente cómo mapear un nombre utilizado en un punto final URL |
|
||||||
a un ID de controlador. Por ejemplo, el siguiente código mapea el nombre `u` al ID del controlador `user`. |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'class' => 'yii\rest\UrlRule', |
|
||||||
'controller' => ['u' => 'user'], |
|
||||||
] |
|
||||||
``` |
|
@ -1,111 +0,0 @@ |
|||||||
Versionado |
|
||||||
========== |
|
||||||
|
|
||||||
Una buena API ha de ser *versionada*: los cambios y las nuevas características son implementadas en las nuevas versiones del API, en vez de estar continuamente modificando sólo una versión. Al contrario que en las aplicaciones Web, en las cuales tienes total control del código de ambas partes lado del cliente y lado del servidor, |
|
||||||
las APIs están destinadas a ser usadas por los clientes fuera de tu control. Por esta razón, compatibilidad hacia atrás (BC Backward compatibility) |
|
||||||
de las APIs ha de ser mantenida siempre que sea posible. Si es necesario un cambio que puede romper la BC, debes de introducirla en la nueva versión del API, e incrementar el número de versión. Los clientes que la usan pueden continuar usando la antigua versión de trabajo del API; los nuevos y actualizados clientes pueden obtener la nueva funcionalidad de la nueva versión del API. |
|
||||||
|
|
||||||
> Tip: referirse a [Semántica del versionado](http://semver.org/) |
|
||||||
para más información en el diseño del número de versión del API. |
|
||||||
|
|
||||||
Una manera común de implementar el versionado de la API es embeber el número de versión en las URLs de la API. |
|
||||||
Por ejemplo, `http://example.com/v1/users` se refiere al punto final `/users` de la versión 1 de la API. |
|
||||||
|
|
||||||
Otro método de versionado de la API, |
|
||||||
la cual está ganando predominancia recientemente, es poner el número de versión en las cabeceras de la petición HTTP. Esto se suele hacer típicamente a través la cabecera `Accept`: |
|
||||||
|
|
||||||
``` |
|
||||||
// vía parámetros |
|
||||||
Accept: application/json; version=v1 |
|
||||||
// vía de el tipo de contenido del proveedor |
|
||||||
Accept: application/vnd.company.myapp-v1+json |
|
||||||
``` |
|
||||||
|
|
||||||
Ambos métodos tienen sus pros y sus contras, y hay gran cantidad de debates sobre cada uno. Debajo puedes ver una estrategia |
|
||||||
práctica para el versionado de la API que es una mezcla de estos dos métodos: |
|
||||||
|
|
||||||
* Pon cada versión superior de la implementación de la API en un módulo separado cuyo ID es el número de la versión mayor (p.e. `v1`, `v2`). |
|
||||||
Naturalmente, las URLs de la API contendrán números de versión mayores. |
|
||||||
* Dentro de cada versión mayor (y por lo tanto, dentro del correspondiente módulo), usa la cabecera de HTTP `Accept` |
|
||||||
para determinar el número de la versión menor y escribe código condicional para responder a la menor versión como corresponde. |
|
||||||
|
|
||||||
Para cada módulo sirviendo una versión mayor, el módulo debe incluir las clases de recursos y y controladores |
|
||||||
que especifican la versión. Para separar mejor la responsabilidad del código, puedes conservar un conjunto común de |
|
||||||
clases base de recursos y controladores, y hacer subclases de ellas en cada versión individual del módulo. Dentro de las subclases, |
|
||||||
impementa el código concreto como por ejemplo `Model::fields()`. |
|
||||||
|
|
||||||
Tu código puede estar organizado como lo que sigue: |
|
||||||
|
|
||||||
``` |
|
||||||
api/ |
|
||||||
common/ |
|
||||||
controllers/ |
|
||||||
UserController.php |
|
||||||
PostController.php |
|
||||||
models/ |
|
||||||
User.php |
|
||||||
Post.php |
|
||||||
modules/ |
|
||||||
v1/ |
|
||||||
controllers/ |
|
||||||
UserController.php |
|
||||||
PostController.php |
|
||||||
models/ |
|
||||||
User.php |
|
||||||
Post.php |
|
||||||
v2/ |
|
||||||
controllers/ |
|
||||||
UserController.php |
|
||||||
PostController.php |
|
||||||
models/ |
|
||||||
User.php |
|
||||||
Post.php |
|
||||||
``` |
|
||||||
|
|
||||||
La configuración de tu aplicación puede tener este aspecto: |
|
||||||
|
|
||||||
```php |
|
||||||
return [ |
|
||||||
'modules' => [ |
|
||||||
'v1' => [ |
|
||||||
'basePath' => '@app/modules/v1', |
|
||||||
'controllerNamespace' => 'app\modules\v1\controllers', |
|
||||||
], |
|
||||||
'v2' => [ |
|
||||||
'basePath' => '@app/modules/v2', |
|
||||||
'controllerNamespace' => 'app\modules\v2\controllers', |
|
||||||
], |
|
||||||
], |
|
||||||
'components' => [ |
|
||||||
'urlManager' => [ |
|
||||||
'enablePrettyUrl' => true, |
|
||||||
'enableStrictParsing' => true, |
|
||||||
'showScriptName' => false, |
|
||||||
'rules' => [ |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => ['v1/user', 'v1/post']], |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => ['v2/user', 'v2/post']], |
|
||||||
], |
|
||||||
], |
|
||||||
], |
|
||||||
]; |
|
||||||
``` |
|
||||||
|
|
||||||
Como consecuencia del código anterior, `http://example.com/v1/users` devolverá la lista de usuarios en la versión 1, mientras |
|
||||||
`http://example.com/v2/users` devolverá la versión 2 de los usuarios. |
|
||||||
|
|
||||||
Gracias a los módulos, el código de las diferentes principales versiones puede ser aislado. Pero los módulos hacen posible |
|
||||||
reutilizar el código a través de los módulos vía clases base comunes y otros recursos compartidos. |
|
||||||
|
|
||||||
Para tratar con versiones menores, puedes tomar ventaja de la característica de negociación de contenido |
|
||||||
provista por el comportamiento (behavior) [[yii\filters\ContentNegotiator|contentNegotiator]]. El comportamiento `contentNegotiator` |
|
||||||
definirá la propiedad [[yii\web\Response::acceptParams]] cuando determina qué tipo |
|
||||||
de contenido soportar. |
|
||||||
|
|
||||||
Por ejemplo, si una petición es enviada con la cabecera HTTP `Accept: application/json; version=v1`, |
|
||||||
después de la negociación de contenido, [[yii\web\Response::acceptParams]] contendrá el valor `['version' => 'v1']`. |
|
||||||
|
|
||||||
Basado en la información de versión contenida en `acceptParams`, puedes escribir código condicional en lugares |
|
||||||
como acciones, clases de recursos, serializadores, etc. para proveer la funcionalidad apropiada. |
|
||||||
|
|
||||||
Dado que por definición las versiones menores requireren mantener la compatibilidad hacia atrás, con suerte no tendrás demasiadas |
|
||||||
comprobaciones de versión en tu código. De otra manera, probablemente puede ocurrir que necesites crear una versión mayor. |
|
@ -1,90 +0,0 @@ |
|||||||
Obsługa błędów |
|
||||||
============== |
|
||||||
|
|
||||||
Podczas obsługi żądania RESTfulowego API, w przypadku wystąpienia błędu w zapytaniu użytkownika lub gdy stanie się coś nieprzewidywanego |
|
||||||
z serwerem, możesz po prostu rzucić wyjątkiem, aby powiadomić użytkownika, że coś poszło nieprawidłowo. |
|
||||||
Jeśli możesz zidentyfikować przyczynę błędu (np. żądany zasób nie istnieje), powinieneś rozważyć rzucenie wyjątkiem razem z odpowiednim kodem statusu HTTP |
|
||||||
(np. [[yii\web\NotFoundHttpException|NotFoundHttpException]] odpowiada statusowi o kodzie 404). |
|
||||||
Yii wyśle odpowiedź razem z odpowiadającym jej kodem i treścią statusu HTTP. Yii dołączy również do samej odpowiedzi zserializowaną reprezentację |
|
||||||
wyjątku. Przykładowo: |
|
||||||
|
|
||||||
``` |
|
||||||
HTTP/1.1 404 Not Found |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{ |
|
||||||
"name": "Not Found Exception", |
|
||||||
"message": "The requested resource was not found.", |
|
||||||
"code": 0, |
|
||||||
"status": 404 |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Poniższa lista zawiera kody statusów HTTP, które są używane przez Yii REST framework: |
|
||||||
|
|
||||||
* `200`: OK. Wszystko działa w porządku. |
|
||||||
* `201`: Zasób został poprawnie stworzony w odpowiedzi na żądanie `POST`. Nagłówek `Location` zawiera URL kierujący do nowoutworzonego zasobu. |
|
||||||
* `204`: Żądanie zostało poprawnie przetworzone, ale odpowiedź nie zawiera treści (jak w przypadku żądania `DELETE`). |
|
||||||
* `304`: Zasób nie został zmodyfikowany. Można użyć wersji przetrzymywanej w pamięci podręcznej. |
|
||||||
* `400`: Nieprawidłowe żądanie. Może być spowodowane przez wiele czynników po stronie użytkownika, takich jak przekazanie nieprawidłowych danych JSON, |
|
||||||
nieprawidłowych parametrów akcji, itp. |
|
||||||
* `401`: Nieudana autentykacja. |
|
||||||
* `403`: Autoryzowany użytkownik nie ma uprawnień do danego punktu końcowego API. |
|
||||||
* `404`: Żądany zasób nie istnieje. |
|
||||||
* `405`: Niedozwolona metoda. Sprawdź nagłówek `Allow`, aby poznać dozwolone metody HTTP. |
|
||||||
* `415`: Niewspierany typ mediów. Żądany typ zawartości lub numer wersji jest nieprawidłowy. |
|
||||||
* `422`: Nieudana walidacja danych (dla przykładu w odpowiedzi na żądanie `POST`). Sprawdź treść odpowiedzi, aby poznać szczegóły błędu. |
|
||||||
* `429`: Zbyt wiele żądań. Żądanie zostało odrzucone z powodu osiagnięcia limitu użycia. |
|
||||||
* `500`: Wewnętrzny błąd serwera. To może być spowodowane wewnętrznymi błędami programu. |
|
||||||
|
|
||||||
|
|
||||||
## Modyfikowanie błędnej odpowiedzi <span id="customizing-error-response"></span> |
|
||||||
|
|
||||||
Czasem wymagane może być dostosowanie domyślnego formatu błędnej odpowiedzi. Dla przykładu, zamiast używać różnych statusów HTTP dla oznaczenia różnych błędów, |
|
||||||
można serwować zawsze status 200 i dodawać prawidłowy kod statusu HTTP jako część struktury JSON w odpowiedzi, jak pokazano to poniżej: |
|
||||||
|
|
||||||
``` |
|
||||||
HTTP/1.1 200 OK |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{ |
|
||||||
"success": false, |
|
||||||
"data": { |
|
||||||
"name": "Not Found Exception", |
|
||||||
"message": "The requested resource was not found.", |
|
||||||
"code": 0, |
|
||||||
"status": 404 |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Aby osiągnąć powyższy efekt, należy skonfigurować odpowiedź na event `beforeSend` dla komponentu `response` w aplikacji: |
|
||||||
|
|
||||||
```php |
|
||||||
return [ |
|
||||||
// ... |
|
||||||
'components' => [ |
|
||||||
'response' => [ |
|
||||||
'class' => 'yii\web\Response', |
|
||||||
'on beforeSend' => function ($event) { |
|
||||||
$response = $event->sender; |
|
||||||
if ($response->data !== null && Yii::$app->request->get('suppress_response_code')) { |
|
||||||
$response->data = [ |
|
||||||
'success' => $response->isSuccessful, |
|
||||||
'data' => $response->data, |
|
||||||
]; |
|
||||||
$response->statusCode = 200; |
|
||||||
} |
|
||||||
}, |
|
||||||
], |
|
||||||
], |
|
||||||
]; |
|
||||||
``` |
|
||||||
|
|
||||||
Powyższy kod zreformatuje odpowiedź (zarówno typu sukces jak i błąd), kiedy `suppress_response_code` zostanie przekazane jako parametr `GET`. |
|
@ -1,37 +0,0 @@ |
|||||||
Limit użycia |
|
||||||
============ |
|
||||||
|
|
||||||
W celu zapobiegnięcia nadużyciom, powinno się rozważyć wprowadzenie *limitu użycia* swojego API. Może to być na przykład ograniczenie |
|
||||||
do maksymalnie 100 zapytań do API dla każdego użytkownika w czasie 10 minut. Jeśli użytkownik przekroczy ten limit w zadanym czasie, |
|
||||||
należy zwrócić odpowiedź ze statusem 429 (oznaczającym "Zbyt dużo zapytań"). |
|
||||||
|
|
||||||
Aby ustalić limit użycia, [[yii\web\User::identityClass|klasa identyfikująca użytkownika]] powinna zaimplementować [[yii\filters\RateLimitInterface|RateLimitInterface]]. |
|
||||||
Interfejs ten wymaga dodania trzech metod: |
|
||||||
|
|
||||||
* `getRateLimit()`: zwraca maksymalną liczbę zapytań i okres czasu (np. `[100, 600]` oznacza maksymalnie 100 zapytań do API w czasie 600 sekund). |
|
||||||
* `loadAllowance()`: zwraca liczbę pozostałych dozwolonych zapytań z limitu i uniksowy znacznik czasu wskazujący datę ostatniego sprawdzenia limitu. |
|
||||||
* `saveAllowance()`: zapisuje liczbę pozostałych dozwolonych zapytań i aktualny uniksowy znacznik czasu. |
|
||||||
|
|
||||||
Do celów obsługi powyższych metod można wykorzystać dwie dodatkowe kolumny w bazie danych użytkowników dla liczby dokonanych połączeń i znacznika czasu. |
|
||||||
Po ustaleniu tych wartości, metody `loadAllowance()` i `saveAllowance()` mogą być poprawnie zaimplementowane do odczytu i zapisu tych wartości dla aktualnego |
|
||||||
zautoryzowanego użytkownika. Aby zwiększyć wydajność tego mechanizmu, należy rozważyć użycie pamięci podręcznej lub bazy typu NoSQL. |
|
||||||
|
|
||||||
Po zaimplemetowaniu wymaganego interfejsu, Yii automatycznie użyje [[yii\filters\RateLimiter|RateLimiter]], skonfigurowanego jako filtr akcji dla [[yii\rest\Controller|Controller]], |
|
||||||
aby pilnować limitu użycia API. Mechanizm rzuci wyjątek [[yii\web\TooManyRequestsHttpException|TooManyRequestsHttpException]], kiedy limit zostanie przekroczony. |
|
||||||
|
|
||||||
Po dodaniu limitu, każda odpowiedź będzie domyślnie zawierała następujące nagłówki HTTP, zawierające informacje o aktualnym użyciu limitu: |
|
||||||
|
|
||||||
* `X-Rate-Limit-Limit`, maksymalna liczba zapytań w zadanym okresie czasu, |
|
||||||
* `X-Rate-Limit-Remaining`, liczba pozostałych dozwolonych zapytań z limitu w aktualnym okresie czasu, |
|
||||||
* `X-Rate-Limit-Reset`, liczba sekund, którą należy odczekać, aby uzyskać ponownie maksymalną liczbę zapytań z limitu. |
|
||||||
|
|
||||||
Wysyłanie powyższych nagłówków można wyłączyć konfigurując [[yii\filters\RateLimiter::enableRateLimitHeaders|enableRateLimitHeaders]] w klasie kontrolera REST jak w poniższym przykładzie. |
|
||||||
|
|
||||||
```php |
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['rateLimiter']['enableRateLimitHeaders'] = false; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
@ -1,90 +0,0 @@ |
|||||||
Routing |
|
||||||
======= |
|
||||||
|
|
||||||
Po przygotowaniu klas zasobów i kontrolerów dostęp do nich można uzyskać w ten sam sposób, jak w przypadku zwykłej aplikacji, używając URL np. |
|
||||||
`http://localhost/index.php?r=user/create`. |
|
||||||
|
|
||||||
W praktyce zwykle chcemy skorzystać z opcji "ładnych" URLi i metod HTTP. |
|
||||||
Przykładowo żądanie `POST /users` może oznaczać wywołanie akcji `user/create`, co możemy uzyskać w łatwy sposób konfigurując |
|
||||||
[komponent aplikacji](structure-application-components.md) `urlManager` w pliku konfiguracyjnym jak poniżej: |
|
||||||
|
|
||||||
```php |
|
||||||
'urlManager' => [ |
|
||||||
'enablePrettyUrl' => true, |
|
||||||
'enableStrictParsing' => true, |
|
||||||
'showScriptName' => false, |
|
||||||
'rules' => [ |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => 'user'], |
|
||||||
], |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Porównując to z menadżerem URLi dla aplikacji Web, główną nowością tutaj jest użycie [[yii\rest\UrlRule|UrlRule]] do routingu RESTfulowych zasobów API. |
|
||||||
Ta specjalna klasa zasad URL stworzy cały zestaw potomnych zasad URL obsługujących routing i tworzenie URLi dla wyznaczonego kontrolera. |
|
||||||
Dla przykładu, kod powyżej jest zgrubnym odpowiednikiem następujących zasad: |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'PUT,PATCH users/<id>' => 'user/update', |
|
||||||
'DELETE users/<id>' => 'user/delete', |
|
||||||
'GET,HEAD users/<id>' => 'user/view', |
|
||||||
'POST users' => 'user/create', |
|
||||||
'GET,HEAD users' => 'user/index', |
|
||||||
'users/<id>' => 'user/options', |
|
||||||
'users' => 'user/options', |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
I poniższe punkty końcowe API są obsługiwane przez tę zasadę: |
|
||||||
|
|
||||||
* `GET /users`: lista wszystkich użytkowników strona po stronie; |
|
||||||
* `HEAD /users`: pokazuje streszczenie informacji listy użytkowników; |
|
||||||
* `POST /users`: tworzy nowego użytkownika; |
|
||||||
* `GET /users/123`: zwraca szczegóły na temat użytkownika 123; |
|
||||||
* `HEAD /users/123`: zwraca streszczenie informacji o użytkowniku 123; |
|
||||||
* `PATCH /users/123` i `PUT /users/123`: aktualizuje użytkownika 123; |
|
||||||
* `DELETE /users/123`: usuwa użytkownika 123; |
|
||||||
* `OPTIONS /users`: pokazuje obsługiwane metody dla punktu końcowego `/users`; |
|
||||||
* `OPTIONS /users/123`: pokazuje obsługiwane metody dla punktu końcowego `/users/123`. |
|
||||||
|
|
||||||
Możesz skonfigurować opcje `only` i `except`, aby wskazać listę akcji, które mają być odpowiednio: tylko obsługiwane lub pominięte. |
|
||||||
Przykładowo, |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'class' => 'yii\rest\UrlRule', |
|
||||||
'controller' => 'user', |
|
||||||
'except' => ['delete', 'create', 'update'], |
|
||||||
], |
|
||||||
``` |
|
||||||
|
|
||||||
Dodatkowo można dodać opcję `patterns` lub `extraPatterns`, aby zredefiniować istniejące wzorce lub dodać nowe obsługiwane przez tę zasadę. |
|
||||||
Dla przykładu, aby dodać obsługę nowej akcji `search` dla punktu końcowego `GET /users/search`, skonfiguruj opcję `extraPatterns` jak następuje, |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'class' => 'yii\rest\UrlRule', |
|
||||||
'controller' => 'user', |
|
||||||
'extraPatterns' => [ |
|
||||||
'GET search' => 'search', |
|
||||||
], |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Na pewno zwróciłeś uwagę na to, że ID kontrolera `user` występuje tu w formie mnogiej jako `users` dla URLi punktu końcowego. |
|
||||||
Dzieje się tak, ponieważ [[yii\rest\UrlRule|UrlRule]] automatycznie przechodzi na formę mnogą dla ID kontrolerów podczas tworzenia potomnych zasad URL. |
|
||||||
Zachowanie to można wyłączyć ustawiając [[yii\rest\UrlRule::pluralize|pluralize]] na `false`. |
|
||||||
|
|
||||||
> Info: forma mnoga ID kontrolerów jest tworzona poprzez metodę [[yii\helpers\Inflector::pluralize()|pluralize()]]. Uwzględnia ona specjalne zasady tworzenia form mnogich. |
|
||||||
> Dla przykładu, od słowa `box` zostanie utworzona liczba mnoga `boxes` a nie `boxs`. |
|
||||||
|
|
||||||
W przypadku, gdy mechanizm automatycznego tworzenia formy mnogiej nie spełnia Twoich oczekiwań, możesz również skonfigurować właściwość |
|
||||||
[[yii\rest\UrlRule::controller|controller]], aby bezpośrednio określić w jaki sposób nazwa użyta w punkcie końcowym URLi ma być zmapowana na ID kontrolera. |
|
||||||
Dla przykładu, poniższy kod mapuje nazwę `u` na ID kontrolera `user`. |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'class' => 'yii\rest\UrlRule', |
|
||||||
'controller' => ['u' => 'user'], |
|
||||||
] |
|
||||||
``` |
|
@ -1,112 +0,0 @@ |
|||||||
Wersjonowanie |
|
||||||
============= |
|
||||||
|
|
||||||
Cechą dobrego API jest jego *wersjonowanie*: zmiany i nowe funkcjonalności powinny być implementowane w nowych wersjach API, zamiast |
|
||||||
ciągłych modyfikacji jednej już istniejącej. W przeciwieństwie do aplikacji Web, nad którymi ma się pełną kontrolę zarówno po stronie |
|
||||||
klienta, jak i serwera, nad API zwykle nie posiada się kontroli po stronie klienta. Z tego powodu niezwykle istotnym jest, aby zachować |
|
||||||
pełną wsteczną kompatybilność (BC = backward compatibility), kiedy to tylko możliwe. Jeśli konieczne jest wprowadzenie zmiany, która |
|
||||||
może nie spełniać BC, należy wprowadzić ją w nowej wersji API, z kolejnym numerem. Istniejące klienty mogą wciąż używać starej, |
|
||||||
działającej wersji API, a nowe lub uaktualnione klienty mogą otrzymać nową funkcjonalność oferowaną przez kolejną wersję API. |
|
||||||
|
|
||||||
> Tip: Zapoznaj się z [Wersjonowaniem semantycznym](http://semver.org/lang/pl/), aby uzyskać więcej informacji na temat nazewnictwa |
|
||||||
numerów wersji. |
|
||||||
|
|
||||||
Jedną z często spotykanych implementacji wersjonowania API jest dodawanie numeru wersji w adresach URL API. |
|
||||||
Dla przykładu `http://example.com/v1/users` oznacza punkt końcowy `/users` API w wersji 1. |
|
||||||
|
|
||||||
Inną metodą wersjonowania API, która zyskuje ostatnio popularność, jest umieszczanie numeru wersji w nagłówkach HTTP żądania. Zwykle |
|
||||||
używa się do tego nagłówka `Accept`: |
|
||||||
|
|
||||||
``` |
|
||||||
// poprzez parametr |
|
||||||
Accept: application/json; version=v1 |
|
||||||
// poprzez dostarczany typ zasobu |
|
||||||
Accept: application/vnd.company.myapp-v1+json |
|
||||||
``` |
|
||||||
|
|
||||||
Obie metody mają swoje wady i zalety i wciąż prowadzone są dyskusje na ich temat. Poniżej prezentujemy strategię wersjonowania, która |
|
||||||
w praktyczny sposób łączy je obie: |
|
||||||
|
|
||||||
* Umieść każdą główną wersję implementacji API w oddzielnym module, którego ID odpowiada numerowi głównej wersji (np. `v1`, `v2`). |
|
||||||
Adresy URL API będą zawierały numery głównych wersji. |
|
||||||
* Wewnątrz każdej głównej wersji (i w związku z tym w każdym odpowiadającym jej module), użyj nagłówka HTTP `Accept`, aby określić |
|
||||||
pomniejszy numer wersji i napisz warunkowy kod odpowiadający temu numerowi. |
|
||||||
|
|
||||||
Każdy moduł obsługujący główną wersję powinien zawierać klasy zasobów i kontrolerów odpowiednie dla tej wersji. W celu lepszego |
|
||||||
rozdzielenia zadań kodu, możesz trzymać razem zestaw podstawowych wspólnych klas zasobów i kontrolerów w jednym miejscu i rozdzielać go |
|
||||||
na podklasy w każdym module wersji. W podklasie implementujesz bazowy kod, taki jak `Model::fields()`. |
|
||||||
|
|
||||||
Struktura Twojego kodu może wyglądać jak poniższa: |
|
||||||
|
|
||||||
``` |
|
||||||
api/ |
|
||||||
common/ |
|
||||||
controllers/ |
|
||||||
UserController.php |
|
||||||
PostController.php |
|
||||||
models/ |
|
||||||
User.php |
|
||||||
Post.php |
|
||||||
modules/ |
|
||||||
v1/ |
|
||||||
controllers/ |
|
||||||
UserController.php |
|
||||||
PostController.php |
|
||||||
models/ |
|
||||||
User.php |
|
||||||
Post.php |
|
||||||
Module.php |
|
||||||
v2/ |
|
||||||
controllers/ |
|
||||||
UserController.php |
|
||||||
PostController.php |
|
||||||
models/ |
|
||||||
User.php |
|
||||||
Post.php |
|
||||||
Module.php |
|
||||||
``` |
|
||||||
|
|
||||||
Konfiguracja Twojej aplikacji mogłaby wyglądać następująco: |
|
||||||
|
|
||||||
```php |
|
||||||
return [ |
|
||||||
'modules' => [ |
|
||||||
'v1' => [ |
|
||||||
'class' => 'app\modules\v1\Module', |
|
||||||
], |
|
||||||
'v2' => [ |
|
||||||
'class' => 'app\modules\v2\Module', |
|
||||||
], |
|
||||||
], |
|
||||||
'components' => [ |
|
||||||
'urlManager' => [ |
|
||||||
'enablePrettyUrl' => true, |
|
||||||
'enableStrictParsing' => true, |
|
||||||
'showScriptName' => false, |
|
||||||
'rules' => [ |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => ['v1/user', 'v1/post']], |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => ['v2/user', 'v2/post']], |
|
||||||
], |
|
||||||
], |
|
||||||
], |
|
||||||
]; |
|
||||||
``` |
|
||||||
|
|
||||||
Rezultatem powyższego kodu będzie skierowanie pod adresem `http://example.com/v1/users` do listy użytkowników w wersji 1, podczas gdy |
|
||||||
`http://example.com/v2/users` pokaże użytkowników w wersji 2. |
|
||||||
|
|
||||||
Dzięki podziałowi na moduły, kod różnych głównych wersji może być dobrze izolowany, ale jednocześnie wciąż możliwe jest ponowne |
|
||||||
wykorzystanie wspólnego kodu poprzez wspólną bazę klas i dzielonych zasobów. |
|
||||||
|
|
||||||
Aby prawidłowo obsłużyć pomniejsze numery wersji, możesz wykorzystać funkcjonalność negocjatora zawartości dostarczaną przez behavior |
|
||||||
[[yii\filters\ContentNegotiator|contentNegotiator]]. Ustawi on właściwość [[yii\web\Response::acceptParams]], kiedy już zostanie |
|
||||||
ustalone, który typ zasobów wspierać. |
|
||||||
|
|
||||||
Przykładowo, jeśli żądanie jest wysłane z nagłówkiem HTTP `Accept: application/json; version=v1`, po negocjacji zawartości |
|
||||||
[[yii\web\Response::acceptParams]] będzie zawierać wartość `['version' => 'v1']`. |
|
||||||
|
|
||||||
Bazując na informacji o wersji w `acceptParams`, możesz napisać obsługujący ją warunkowy kod w miejscach takich jak akcje, klasy |
|
||||||
zasobów, serializatory, itp., aby zapewnić odpowiednią funkcjonalność. |
|
||||||
|
|
||||||
Ponieważ pomniejsze wersje z definicji wymagają zachowania wstecznej kompatybilności, w kodzie nie powinno znaleźć się zbyt wiele |
|
||||||
miejsc, gdzie numer wersji będzie sprawdzany. W przeciwnym wypadku możliwe, że konieczne będzie utworzenie kolejnej głównej wersji API. |
|
@ -1,104 +0,0 @@ |
|||||||
Autenticação |
|
||||||
============== |
|
||||||
|
|
||||||
Ao contrário de aplicações Web, APIs RESTful são geralmente stateless, o que significa que as sessões ou os cookies não devem ser utilizados. Portanto, cada requisição deve vir com algum tipo de credencial de autenticação pois o estado de autenticação do usuário não pode ser mantido por sessões ou cookies. Uma prática comum é enviar um token de acesso secreto com cada solicitação para autenticar o usuário. Uma vez que um token de acesso pode ser utilizado para identificar de forma exclusiva e autenticar um usuário. **Solicitações de API devem sempre ser enviadas via HTTPS para evitar ataques man-in-the-middle (MitM)**. |
|
||||||
|
|
||||||
Existem diferentes maneiras de enviar um token de acesso: |
|
||||||
|
|
||||||
* [Autenticação Básica HTTP](http://en.wikipedia.org/wiki/Basic_access_authentication): o token de acesso é enviado como um nome de usuário. Isso só deve ser usado quando um token de acesso puder ser armazenado com segurança no lado do consumidor da API. Por exemplo, o consumidor API é um programa executado em um servidor. |
|
||||||
* Parâmetro de consulta da URL: o token de acesso é enviado como um parâmetro de consulta na URL da API, ex., `https://example.com/users?access-token=xxxxxxxx`. Como a maioria dos servidores Web manterão os parâmetros de consulta nos logs do servidor, esta abordagem deve ser utilizada principalmente para servir requisições `JSONP` que não pode usar cabeçalhos HTTP para enviar tokens de acesso. |
|
||||||
* [OAuth 2](http://oauth.net/2/): o token de acesso é obtido pelo consumidor a partir de um servidor de autorização e enviado para o servidor da API via [HTTP Bearer Tokens] (http://tools.ietf.org/html/rfc6750), de acordo com o protocolo OAuth2. |
|
||||||
|
|
||||||
Yii suporta todos os métodos de autenticação descritos acima. Você também pode criar facilmente um novo método de autenticação. |
|
||||||
|
|
||||||
Para ativar a autenticação nas suas APIs, siga os seguintes passos: |
|
||||||
|
|
||||||
1. Configure o [componente de aplicação](structure-application-components.md) `user`: |
|
||||||
- Defina a propriedade [[yii\web\User::enableSession|enableSession]] como `false`. |
|
||||||
- Defina a propriedade [[yii\web\User::loginUrl|loginUrl]] como `null` para mostrar o erro HTTP 403 em vez de redirecionar para a página de login. |
|
||||||
2. Especificar quais métodos de autenticação você planeja usar configurando o behavior `authenticator` na sua classe controller REST. |
|
||||||
3. Implemente [[yii\web\IdentityInterface::findIdentityByAccessToken()]] na sua [[yii\web\User::identityClass|classe de identidade do usuário]]. |
|
||||||
|
|
||||||
Passo 1 não é obrigatório, mas é recomendado para APIs RESTful stateless. Quando [[yii\web\User::enableSession|enableSession]] está marcado como falso, o status de autenticação de usuário NÃO será mantido entre as requisições usando sessões. Em lugar disso, autenticação será realizada para cada requisição, que é realizado no passo 2 e 3. |
|
||||||
|
|
||||||
> Dica: Você pode configurar [[yii\web\User::enableSession|enableSession]] do componente `user` |
|
||||||
> nas configurações da aplicação se você estiver desenvolvendo APIs RESTful para sua aplicação. Se você desenvolver |
|
||||||
> APIs RESTful como um módulo, você pode colocar a seguinte linha no método `init()` do módulo, conforme exemplo a seguir: |
|
||||||
> |
|
||||||
> ```php |
|
||||||
> public function init() |
|
||||||
> { |
|
||||||
> parent::init(); |
|
||||||
> \Yii::$app->user->enableSession = false; |
|
||||||
> } |
|
||||||
> ``` |
|
||||||
|
|
||||||
Por exemplo, para usar autenticação HTTP básica, você pode configurar o behavior `authenticator` como o seguinte: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\filters\auth\HttpBasicAuth; |
|
||||||
|
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['authenticator'] = [ |
|
||||||
'class' => HttpBasicAuth::className(), |
|
||||||
]; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Se você quiser dar suporte a todos os três métodos de autenticação explicado acima, você pode utilizar o `CompositeAuth` conforme mostrado a seguir: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\filters\auth\CompositeAuth; |
|
||||||
use yii\filters\auth\HttpBasicAuth; |
|
||||||
use yii\filters\auth\HttpBearerAuth; |
|
||||||
use yii\filters\auth\QueryParamAuth; |
|
||||||
|
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['authenticator'] = [ |
|
||||||
'class' => CompositeAuth::className(), |
|
||||||
'authMethods' => [ |
|
||||||
HttpBasicAuth::className(), |
|
||||||
HttpBearerAuth::className(), |
|
||||||
QueryParamAuth::className(), |
|
||||||
], |
|
||||||
]; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Cada elemento em `authMethods` deve ser o nome de uma classe de método de autenticação ou um array de configuração. |
|
||||||
|
|
||||||
|
|
||||||
Implementação de `findIdentityByAccessToken()` é específico por aplicação. Por exemplo, em cenários simples quando cada usuário só pode ter um token de acesso, você pode armazenar o token de acesso em uma coluna `access_token` na tabela `user`. O método pode então ser facilmente implementado na classe `User` como o seguinte: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\db\ActiveRecord; |
|
||||||
use yii\web\IdentityInterface; |
|
||||||
|
|
||||||
class User extends ActiveRecord implements IdentityInterface |
|
||||||
{ |
|
||||||
public static function findIdentityByAccessToken($token, $type = null) |
|
||||||
{ |
|
||||||
return static::findOne(['access_token' => $token]); |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Após a autenticação ser ativada, conforme descrito acima, para todas as requisições da API, o controller requisitado irá tentar autenticar o usuário no passo `beforeAction()`. |
|
||||||
|
|
||||||
Se a autenticação retornar com sucesso, o controller irá executar outras verificações (tais como limitação de taxa, autorização) e então executará a ação. As informações de identidade do usuário autenticado podem ser recuperadas através de `Yii::$app->user->identity`. |
|
||||||
|
|
||||||
Se a autenticação falhar, uma resposta HTTP com status 401 será enviado de volta junto com outros cabeçalhos apropriados (tal como um `WWW-Authenticate` cabeçalho HTTP para Autenticação Básica). |
|
||||||
|
|
||||||
|
|
||||||
## Autorização <span id="authorization"></span> |
|
||||||
|
|
||||||
Após um usuário se autenticar, você provavelmente vai querer verificar se ele ou ela tem a permissão para executar a ação do recurso solicitado. Este processo é chamado de *autorização* que é tratada em pormenor na seção de [Autorização](security-authorization.md). |
|
||||||
|
|
||||||
Se o seu controller estende de [[yii\rest\ActiveController]], você pode sobrescrever o método [[yii\rest\Controller::checkAccess()|checkAccess()]] para executar a verificação de autorização. O método será chamado pelas ações incorporadas fornecidas pelo [[yii\rest\ActiveController]]. |
|
||||||
|
|
@ -1,133 +0,0 @@ |
|||||||
Controllers (Controladores) |
|
||||||
=========== |
|
||||||
|
|
||||||
Depois de criar as classes de recursos e especificar como os dados de recursos devem ser formatados, a próxima coisa a fazer é criar ações do controller para expor os recursos para os usuários finais através das APIs RESTful. |
|
||||||
|
|
||||||
O Yii fornece duas classes básicas de controller para simplificar seu trabalho de criar ações RESTful: [[yii\rest\Controller]] e [[yii\rest\ActiveController]]. A diferença entre os dois controllers é que o último fornece um conjunto padrão de ações que são especificamente concebidos para lidar com recursos do [Active Record](db-active-record.md). Então, se você estiver usando [Active Record](db-active-record.md) e está confortável com as ações fornecidas, você pode considerar estender suas classes de controller de [[yii\rest\ActiveController]], que permitirá criar poderosas APIs RESTful com um mínimo de código. |
|
||||||
|
|
||||||
Ambas classes [[yii\rest\Controller]] e [[yii\rest\ActiveController]] fornecem os seguintes recursos, algumas das quais serão descritas em detalhes nas próximas seções: |
|
||||||
|
|
||||||
* Validação de Método HTTP; |
|
||||||
* [Negociação de conteúdo e formatação de dados](rest-response-formatting.md); |
|
||||||
* [Autenticação](rest-authentication.md); |
|
||||||
* [Limitação de taxa](rest-rate-limiting.md). |
|
||||||
|
|
||||||
O [[yii\rest\ActiveController]] oferece também os seguintes recursos: |
|
||||||
|
|
||||||
* Um conjunto de ações comumente necessárias: `index`, `view`, `create`, `update`, `delete`, `options`; |
|
||||||
* Autorização do usuário em relação à ação solicitada e recursos. |
|
||||||
|
|
||||||
|
|
||||||
## Criando Classes Controller <span id="creating-controller"></span> |
|
||||||
|
|
||||||
Ao criar uma nova classe de controller, uma convenção na nomenclatura da classe é usar o nome do tipo de recurso no singular. Por exemplo, para disponibilizar as informações do usuário, o controlador pode ser nomeado como `UserController`. Criar uma nova ação é semelhante à criação de uma ação de uma aplicação Web. A única diferença é que em vez de renderizar o resultado usando uma view e chamando o método `render()`, para ações RESTful você retorna diretamente os dados. O [[yii\rest\Controller::serializer|serializer]] e o [[yii\web\Response|objeto response]] vão converter os dados originais para o formato solicitado. Por exemplo: |
|
||||||
|
|
||||||
```php |
|
||||||
public function actionView($id) |
|
||||||
{ |
|
||||||
return User::findOne($id); |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Filtros <span id="filters"></span> |
|
||||||
|
|
||||||
A maioria dos recursos da API RESTful fornecidos por [[yii\rest\Controller]] são implementadas por [filtros](structure-filters.md). |
|
||||||
Em particular, os seguintes filtros serão executados na ordem em que estão listados: |
|
||||||
|
|
||||||
* [[yii\filters\ContentNegotiator|contentNegotiator]]: suporta a negociação de conteúdo, a ser explicado na seção [Formatação de Resposta](rest-response-formatting.md); |
|
||||||
* [[yii\filters\VerbFilter|verbFilter]]: suporta validação de métodos HTTP; |
|
||||||
* [[yii\filters\auth\AuthMethod|authenticator]]: suporta autenticação de usuários, que será explicado na seção [Autenticação](rest-authentication.md); |
|
||||||
* [[yii\filters\RateLimiter|rateLimiter]]: suporta limitação de taxa, que será explicado na seção |
|
||||||
[Limitação de taxa](rest-rate-limiting.md). |
|
||||||
|
|
||||||
Estes filtros são declarados no método [[yii\rest\Controller::behaviors()|behaviors()]]. |
|
||||||
Você pode sobrescrever esse método para configurar alguns filtros, desativar outros, ou adicionar seus próprios filtros. Por exemplo, se você precisar somente de autenticação básica de HTTP, poderá utilizar o seguinte código: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\filters\auth\HttpBasicAuth; |
|
||||||
|
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['authenticator'] = [ |
|
||||||
'class' => HttpBasicAuth::className(), |
|
||||||
]; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Estendendo `ActiveController` <span id="extending-active-controller"></span> |
|
||||||
|
|
||||||
Se a sua classe controller estende de [[yii\rest\ActiveController]], você deve configurar a propriedade [[yii\rest\ActiveController::modelClass|modelClass]] para ser o nome da classe de recurso que você pretende servir através deste controller. A classe deve estender de [[yii\db\ActiveRecord]]. |
|
||||||
|
|
||||||
|
|
||||||
### Customizando Ações <span id="customizing-actions"></span> |
|
||||||
|
|
||||||
Por padrão, o [[yii\rest\ActiveController]] fornece as seguintes ações: |
|
||||||
|
|
||||||
* [[yii\rest\IndexAction|index]]: recursos de lista página por página; |
|
||||||
* [[yii\rest\ViewAction|view]]: retorna os detalhes de um recurso especificado; |
|
||||||
* [[yii\rest\CreateAction|create]]: cria um novo recurso; |
|
||||||
* [[yii\rest\UpdateAction|update]]: atualiza um recurso existente; |
|
||||||
* [[yii\rest\DeleteAction|delete]]: excluir o recurso especificado; |
|
||||||
* [[yii\rest\OptionsAction|options]]: retorna os métodos HTTP suportados. |
|
||||||
|
|
||||||
Todas essas ações são declaradas através do método [[yii\rest\ActiveController::actions()|actions()]]. Você pode configurar essas ações ou desativar algumas delas, sobrescrevendo o método `actions()`, como mostrado a seguir: |
|
||||||
|
|
||||||
```php |
|
||||||
public function actions() |
|
||||||
{ |
|
||||||
$actions = parent::actions(); |
|
||||||
|
|
||||||
// desabilita as ações "delete" e "create" |
|
||||||
unset($actions['delete'], $actions['create']); |
|
||||||
|
|
||||||
// customiza a preparação do data provider com o método "prepareDataProvider()" |
|
||||||
$actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider']; |
|
||||||
|
|
||||||
return $actions; |
|
||||||
} |
|
||||||
|
|
||||||
public function prepareDataProvider() |
|
||||||
{ |
|
||||||
// preparar e retornar um data provider para a ação "index" |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Por favor, consulte as referências de classe para classes de ação individual para saber as opções de configuração que estão disponíveis. |
|
||||||
|
|
||||||
|
|
||||||
### Executando Verificação de Acesso <span id="performing-access-check"></span> |
|
||||||
|
|
||||||
Ao disponibilizar recursos por meio de APIs RESTful, muitas vezes você precisa verificar se o usuário atual tem permissão para acessar e manipular o(s) recurso(s) solicitado(s). Com o [[yii\rest\ActiveController]], isso pode ser feito sobrescrevendo o método [[yii\rest\ActiveController::checkAccess()|checkAccess()]] conforme a seguir: |
|
||||||
|
|
||||||
```php |
|
||||||
/** |
|
||||||
* Verifica os privilégios do usuário corrente. |
|
||||||
* |
|
||||||
* Este método deve ser sobrescrito para verificar se o usuário atual tem o privilégio |
|
||||||
* para executar a ação especificada diante do modelo de dados especificado. |
|
||||||
* se o usuário não tiver acesso, uma [[ForbiddenHttpException]] deve ser lançada. |
|
||||||
* |
|
||||||
* @param string $action o ID da ação a ser executada |
|
||||||
* @param \yii\base\Model $model o model a ser acessado. Se `null`, isso significa que nenhum model específico está sendo acessado. |
|
||||||
* @param array $params parâmetros adicionais |
|
||||||
* @throws ForbiddenHttpException se o usuário não tiver acesso |
|
||||||
*/ |
|
||||||
public function checkAccess($action, $model = null, $params = []) |
|
||||||
{ |
|
||||||
// verifica se o usuário pode acessar $action and $model |
|
||||||
// lança a ForbiddenHttpException se o acesso for negado |
|
||||||
if ($action === 'update' || $action === 'delete') { |
|
||||||
if ($model->author_id !== \Yii::$app->user->id) |
|
||||||
throw new \yii\web\ForbiddenHttpException(sprintf('You can only %s articles that you\'ve created.', $action)); |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
O método `checkAccess()` será chamado pelas ações padrões do [[yii\rest\ActiveController]]. Se você criar novas ações e também desejar executar a verificação de acesso, deve chamar esse método explicitamente nas novas ações. |
|
||||||
|
|
||||||
> Dica: Você pode implementar `checkAccess()` usando o [componente de Role-Based Access Control (RBAC)](security-authorization.md). |
|
||||||
|
|
@ -1,85 +0,0 @@ |
|||||||
Tratamento de Erros |
|
||||||
============== |
|
||||||
|
|
||||||
Ao manusear uma requisição da API RESTful, se existir um erro na requisição do usuário ou se alguma coisa inesperada acontecer no servidor, você pode simplesmente lançar uma exceção para notificar o usuário de que algo deu errado. |
|
||||||
Se você puder identificar a causa do erro (ex., o recurso requisitado não existe), você deve considerar lançar uma exceção juntamente com um código de status HTTP adequado (ex., [[yii\web\NotFoundHttpException]] representa um código de status 404). O Yii enviará a resposta juntamente com o código e o texto do status HTTP correspondente. O Yii também incluirá a representação serializada da exceção no corpo da resposta. Por exemplo: |
|
||||||
|
|
||||||
``` |
|
||||||
HTTP/1.1 404 Not Found |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{ |
|
||||||
"name": "Not Found Exception", |
|
||||||
"message": "The requested resource was not found.", |
|
||||||
"code": 0, |
|
||||||
"status": 404 |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
A lista a seguir descrimina os códigos de status HTTP que são usados pelo framework REST do Yii: |
|
||||||
|
|
||||||
* `200`: OK. Tudo funcionou conforme o esperado; |
|
||||||
* `201`: Um recurso foi criado com êxito em resposta a uma requisição `POST`. O cabeçalho `location` contém a URL que aponta para o recurso recém-criado; |
|
||||||
* `204`: A requisição foi tratada com sucesso e a resposta não contém nenhum conteúdo no corpo (por exemplo uma requisição `DELETE`); |
|
||||||
* `304`: O recurso não foi modificado. Você pode usar a versão em cache; |
|
||||||
* `400`: Requisição malfeita. Isto pode ser causado por várias ações por parte do usuário, tais como o fornecimento de um JSON inválido no corpo da requisição, fornecendo parâmetros inválidos, etc; |
|
||||||
* `401`: Falha de autenticação; |
|
||||||
* `403`: O usuário autenticado não tem permissão para acessar o recurso da API solicitado; |
|
||||||
* `404`: O recurso requisitado não existe; |
|
||||||
* `405`: Método não permitido. Favor verificar o cabeçalho `Allow` para conhecer os métodos HTTP permitidos; |
|
||||||
* `415`: Tipo de mídia não suportada. O número de versão ou o content type requisitado são inválidos; |
|
||||||
* `422`: Falha na validação dos dados (na resposta a uma requisição `POST`, por exemplo). Por favor, verifique o corpo da resposta para visualizar a mensagem detalhada do erro; |
|
||||||
* `429`: Excesso de requisições. A requisição foi rejeitada devido a limitação de taxa; |
|
||||||
* `500`: Erro interno do servidor. Isto pode ser causado por erros internos do programa. |
|
||||||
|
|
||||||
## Customizando Resposta de Erro<span id="customizing-error-response"></span> |
|
||||||
|
|
||||||
Às vezes você pode querer personalizar o formato de resposta de erro padrão. Por exemplo, em vez de confiar em usar diferentes status HTTP para indicar os diversos erros, você pode querer usar sempre o status 200 como resposta e colocar o código de status real como parte da estrutura JSON da resposta, como mostrado abaixo, |
|
||||||
|
|
||||||
``` |
|
||||||
HTTP/1.1 200 OK |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{ |
|
||||||
"success": false, |
|
||||||
"data": { |
|
||||||
"name": "Not Found Exception", |
|
||||||
"message": "The requested resource was not found.", |
|
||||||
"code": 0, |
|
||||||
"status": 404 |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Para atingir este objetivo, você pode responder o evento `beforeSend` do componente `response` na configuração da aplicação: |
|
||||||
|
|
||||||
```php |
|
||||||
return [ |
|
||||||
// ... |
|
||||||
'components' => [ |
|
||||||
'response' => [ |
|
||||||
'class' => 'yii\web\Response', |
|
||||||
'on beforeSend' => function ($event) { |
|
||||||
$response = $event->sender; |
|
||||||
if ($response->data !== null && Yii::$app->request->get('suppress_response_code')) { |
|
||||||
$response->data = [ |
|
||||||
'success' => $response->isSuccessful, |
|
||||||
'data' => $response->data, |
|
||||||
]; |
|
||||||
$response->statusCode = 200; |
|
||||||
} |
|
||||||
}, |
|
||||||
], |
|
||||||
], |
|
||||||
]; |
|
||||||
``` |
|
||||||
|
|
||||||
O código acima formatará a resposta (para ambas as respostas, bem-sucedidas e com falha) como explicado quando `suppress_response_code` é passado como um parâmetro `GET`. |
|
||||||
|
|
||||||
|
|
@ -1,193 +0,0 @@ |
|||||||
Introdução |
|
||||||
=========== |
|
||||||
|
|
||||||
O Yii fornece um conjunto de ferramentas para simplificar a tarefa de implementar APIs RESTful Web Service. Em particular, o Yii suporta os seguintes recursos sobre APIs RESTful: |
|
||||||
|
|
||||||
* Prototipagem rápida com suporte para APIs comuns de [Active Record](db-active-record.md); |
|
||||||
* Negociação de formato do Response (suporte JSON e XML por padrão); |
|
||||||
* Serialização de objeto configurável com suporte a campos de saída selecionáveis; |
|
||||||
* Formatação adequada para a coleção e dados e validação de erros; |
|
||||||
* Suporte a [HATEOAS](http://en.wikipedia.org/wiki/HATEOAS); |
|
||||||
* Roteamento eficiente com verificação dos verbs (métodos) HTTP; |
|
||||||
* Construído com suporte aos métodos `OPTIONS` e `HEAD`; |
|
||||||
* Autenticação e autorização; |
|
||||||
* Data caching e HTTP caching; |
|
||||||
* Limitação de taxa; |
|
||||||
|
|
||||||
|
|
||||||
Abaixo, utilizamos um exemplo para ilustrar como você pode construir um conjunto de APIs RESTful com um mínimo de codificação. |
|
||||||
|
|
||||||
Suponha que você deseja expor os dados do usuário via APIs RESTful. Os dados do usuário estão guardados na tabela `user` e você já criou a classe [active record](db-active-record.md) `app\models\User` para acessar os dados do usuário. |
|
||||||
|
|
||||||
|
|
||||||
## Criando um Controller (Controlador) <span id="creating-controller"></span> |
|
||||||
|
|
||||||
Primeiramente, crie uma classe [controller](structure-controllers.md) `app\controllers\UserController` como a seguir, |
|
||||||
|
|
||||||
```php |
|
||||||
namespace app\controllers; |
|
||||||
|
|
||||||
use yii\rest\ActiveController; |
|
||||||
|
|
||||||
class UserController extends ActiveController |
|
||||||
{ |
|
||||||
public $modelClass = 'app\models\User'; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
A classe controller estende de [[yii\rest\ActiveController]], que implementa um conjunto comum de ações RESTful. Especificando [[yii\rest\ActiveController::modelClass|modelClass]] |
|
||||||
como `app\models\User`, o controller sabe qual o model que pode ser usado para a recuperação e manipulação de dados. |
|
||||||
|
|
||||||
|
|
||||||
## Configurando Regras de URL <span id="configuring-url-rules"></span> |
|
||||||
|
|
||||||
|
|
||||||
Em seguida, modifique a configuração do componente `urlManager` na configuração da aplicação: |
|
||||||
|
|
||||||
```php |
|
||||||
'urlManager' => [ |
|
||||||
'enablePrettyUrl' => true, |
|
||||||
'enableStrictParsing' => true, |
|
||||||
'showScriptName' => false, |
|
||||||
'rules' => [ |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => 'user'], |
|
||||||
], |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
A configuração acima primeiramente adiciona uma regra de URL para o controller `user` de modo que os dados do usuário podem ser acessados e manipulados com URLs amigáveis e métodos HTTP significativos. |
|
||||||
|
|
||||||
|
|
||||||
## Ativando o Input via JSON <span id="enabling-json-input"></span> |
|
||||||
|
|
||||||
Para fazer a API aceitar dados no formato JSON, configure a propriedade [[yii\web\Request::$parsers|parsers]] do [componente de aplicação](structure-application-components.md) `request` para usar o [[yii\web\JsonParser]] para realizar input via JSON: |
|
||||||
|
|
||||||
```php |
|
||||||
'request' => [ |
|
||||||
'parsers' => [ |
|
||||||
'application/json' => 'yii\web\JsonParser', |
|
||||||
] |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
> Observação: A configuração acima é opcional. Sem esta configuração, a API só iria reconhecer os formatos de input `application/x-www-form-urlencoded` e `multipart/form-data`. |
|
||||||
|
|
||||||
|
|
||||||
## Testando <span id="trying-it-out"></span> |
|
||||||
|
|
||||||
Com o mínimo de esforço acima, você já terminou sua tarefa de criar as APIs RESTful para acessar os dados do usuário. As APIs que você criou incluem: |
|
||||||
|
|
||||||
* `GET /users`: listar todos os usuários página por página; |
|
||||||
* `HEAD /users`: mostrar a informações gerais da listagem de usuários; |
|
||||||
* `POST /users`: criar um novo usuário; |
|
||||||
* `GET /users/123`: retorna detalhes do usuário 123; |
|
||||||
* `HEAD /users/123`: mostra informações gerais do usuário 123; |
|
||||||
* `PATCH /users/123` e `PUT /users/123`: atualiza o usuário 123; |
|
||||||
* `DELETE /users/123`: deleta o usuário 123; |
|
||||||
* `OPTIONS /users`: mostra os métodos suportados em relação à URL `/users`; |
|
||||||
* `OPTIONS /users/123`: mostra os métodos suportados em relação à URL `/users/123`. |
|
||||||
|
|
||||||
> Observação: O Yii vai pluralizar automaticamente nomes de controllers para uso em URLs (também chamadas *endpoints*). |
|
||||||
> Você pode configurar isso usando a propriedade [[yii\rest\UrlRule::$pluralize]]. |
|
||||||
|
|
||||||
Você pode acessar suas APIs com o comando `curl` mostrado abaixo, |
|
||||||
|
|
||||||
``` |
|
||||||
$ curl -i -H "Accept:application/json" "http://localhost/users" |
|
||||||
|
|
||||||
HTTP/1.1 200 OK |
|
||||||
... |
|
||||||
X-Pagination-Total-Count: 1000 |
|
||||||
X-Pagination-Page-Count: 50 |
|
||||||
X-Pagination-Current-Page: 1 |
|
||||||
X-Pagination-Per-Page: 20 |
|
||||||
Link: <http://localhost/users?page=1>; rel=self, |
|
||||||
<http://localhost/users?page=2>; rel=next, |
|
||||||
<http://localhost/users?page=50>; rel=last |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
[ |
|
||||||
{ |
|
||||||
"id": 1, |
|
||||||
... |
|
||||||
}, |
|
||||||
{ |
|
||||||
"id": 2, |
|
||||||
... |
|
||||||
}, |
|
||||||
... |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Tente alterar o tipo de conteúdo para `application/xml` e você vai ver o resultado retornado em formato XML: |
|
||||||
|
|
||||||
``` |
|
||||||
$ curl -i -H "Accept:application/xml" "http://localhost/users" |
|
||||||
|
|
||||||
HTTP/1.1 200 OK |
|
||||||
... |
|
||||||
X-Pagination-Total-Count: 1000 |
|
||||||
X-Pagination-Page-Count: 50 |
|
||||||
X-Pagination-Current-Page: 1 |
|
||||||
X-Pagination-Per-Page: 20 |
|
||||||
Link: <http://localhost/users?page=1>; rel=self, |
|
||||||
<http://localhost/users?page=2>; rel=next, |
|
||||||
<http://localhost/users?page=50>; rel=last |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/xml |
|
||||||
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<response> |
|
||||||
<item> |
|
||||||
<id>1</id> |
|
||||||
... |
|
||||||
</item> |
|
||||||
<item> |
|
||||||
<id>2</id> |
|
||||||
... |
|
||||||
</item> |
|
||||||
... |
|
||||||
</response> |
|
||||||
``` |
|
||||||
|
|
||||||
O seguinte comando irá criar um novo usuário, enviando uma solicitação POST com os dados do usuário em formato JSON: |
|
||||||
|
|
||||||
``` |
|
||||||
$ curl -i -H "Accept:application/json" -H "Content-Type:application/json" -XPOST "http://localhost/users" -d '{"username": "example", "email": "user@example.com"}' |
|
||||||
|
|
||||||
HTTP/1.1 201 Created |
|
||||||
... |
|
||||||
Location: http://localhost/users/1 |
|
||||||
Content-Length: 99 |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{"id":1,"username":"example","email":"user@example.com","created_at":1414674789,"updated_at":1414674789} |
|
||||||
``` |
|
||||||
|
|
||||||
> Dica: Você também pode acessar suas APIs via navegador, digitando a URL `http://localhost/users`. No entanto, você pode precisar de alguns plugins do navegador para enviar cabeçalhos de solicitações específicas. |
|
||||||
|
|
||||||
Como você pode ver, no cabeçalho da resposta, há informações sobre a contagem total, número de páginas, etc. Há também links que permitem navegar para outras páginas de dados. Por exemplo, `http://localhost/users?page=2` lhe daria a próxima página dos dados de usuário. |
|
||||||
|
|
||||||
Usando os parâmetros `fields` e `expand`, você também pode especificar os campos que devem ser incluídos no resultado. Por exemplo, a URL `http://localhost/users?fields=id,email` só retornará os campos `id` e `email`. |
|
||||||
|
|
||||||
|
|
||||||
> Observação: Você deve ter notado que o resultado de `http://localhost/users` |
|
||||||
> inclui alguns campos confidenciais, |
|
||||||
> Tal como `password_hash`, `auth_key`. Você certamente não quer que |
|
||||||
> eles apareçam no resultado da sua API. |
|
||||||
> Você pode e deve filtrar esses campos, conforme descrito na seção |
|
||||||
> [Response Formatting](rest-response-formatting.md). |
|
||||||
|
|
||||||
|
|
||||||
## Resumo <span id="summary"></span> |
|
||||||
|
|
||||||
Usando o framework API RESTful do Yii, você implementa uma URL desses campos, conforme descrito na seção de ações do controller, um controller para organizar as ações que implementam as URLs para um único tipo de recurso. |
|
||||||
|
|
||||||
Os recursos são representados como modelos de dados, que se estendem a partir da classe [[yii\base\Model]]. Se você estiver trabalhando com bancos de dados (relacional ou NoSQL), é recomendado que você use [[yii\db\ActiveRecord|ActiveRecord]] para representar recursos. |
|
||||||
|
|
||||||
Você pode usar [[yii\rest\UrlRule]] para simplificar o roteamento para suas URLs da API. |
|
||||||
|
|
||||||
|
|
||||||
Embora não seja exigido, é recomendável que você desenvolva suas APIs RESTful como uma aplicação separada, diferente do seu frontend e backend para facilitar a manutenção. |
|
||||||
|
|
@ -1,38 +0,0 @@ |
|||||||
Limitador de Acesso |
|
||||||
============= |
|
||||||
|
|
||||||
Para prevenir abusos, você pode considerar a utilização de um *limitador de acesso* nas suas APIs. Por exemplo, você pode querer limitar o uso da API para cada usuário em no máximo 100 chamadas a cada 10 minutos. Se o número de solicitações recebidas por usuário ultrapassar este limite, uma resposta com status 429 (significa "Muitas Requisições") deve ser retornada. |
|
||||||
|
|
||||||
Para habilitar o limitador de acesso, a [[yii\web\User::identityClass|classe de identidade do usuário]] deve implementar [[yii\filters\RateLimitInterface]]. Esta interface requer a implementação de três métodos: |
|
||||||
|
|
||||||
* `getRateLimit()`: retorna o número máximo de pedidos permitidos e o período de tempo (ex., `[100, 600]` significa que pode haver, no máximo, 100 chamadas de API dentro de 600 segundo); |
|
||||||
* `loadAllowance()`: retorna o número restante de pedidos permitidos e a hora da última verificação; |
|
||||||
* `saveAllowance()`: salva tanto o número restante de requisições e a hora atual. |
|
||||||
|
|
||||||
Você pode usar duas colunas na tabela de usuários para registrar estas informações. Com esses campos definidos, então `loadAllowance()` e `saveAllowance()` podem ser implementados para ler e guardar os valores das duas colunas correspondentes ao atual usuário autenticado. |
|
||||||
Para melhorar o desempenho, você também pode considerar armazenar essas informações em um cache ou armazenamento NoSQL. |
|
||||||
|
|
||||||
Uma vez que a classe de identidade do usuário estiver com a interface necessária implementada, o Yii automaticamente usará a classe [[yii\filters\RateLimiter]] configurada como um filtro da ação para o [[yii\rest\Controller]] realizar a verificação da limitação do acesso. O limitador de acesso lançará uma exceção [[yii\web\TooManyRequestsHttpException]] quando o limite for excedido. |
|
||||||
|
|
||||||
Você pode configurar o limitador de acesso da seguinte forma em suas classes controller REST: |
|
||||||
|
|
||||||
```php |
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = |
|
||||||
parent::behaviors(); |
|
||||||
$behaviors['rateLimiter']['enableRateLimitHeaders'] |
|
||||||
= false; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Quando o limitador de acesso está habilitado, por padrão a cada resposta será enviada com o seguinte cabeçalho HTTP contendo a informação da atual taxa de limitação: |
|
||||||
|
|
||||||
* `X-Rate-Limit-Limit`, o número máximo permitido de pedidos em um período de tempo; |
|
||||||
* `X-Rate-Limit-Remaining`, o número de pedidos restantes no período de tempo atual; |
|
||||||
* `X-Rate-Limit-Reset`, o número de segundos de espera a fim de obter o número máximo de pedidos permitidos. |
|
||||||
|
|
||||||
Você pode desativar esses cabeçalhos, configurando [[yii\filters\RateLimiter::enableRateLimitHeaders]] para `false`, como mostrado no exemplo acima. |
|
||||||
|
|
||||||
|
|
@ -1,192 +0,0 @@ |
|||||||
Recursos |
|
||||||
========= |
|
||||||
|
|
||||||
APIs RESTful tratam de como acessar e manipular *recursos*. Você pode ver recursos como [models](structure-models.md) no paradigma MVC. |
|
||||||
|
|
||||||
Embora não haja restrição na forma de representar um recurso, no Yii você normalmente representaria recursos como objetos de [[yii\base\Model]] ou de uma classe filha (ex. [[yii\db\ActiveRecord]]), pelas seguintes razões: |
|
||||||
|
|
||||||
* [[yii\base\Model]] implementa a interface [[yii\base\Arrayable]], que permite que você personalize como você deseja expor dados de recursos através das APIs RESTful. |
|
||||||
* [[yii\base\Model]] suporta [validação de dados de entrada](input-validation.md), que é importante se as suas APIs RESTful precisarem suportar entrada de dados. |
|
||||||
* [[yii\db\ActiveRecord]] fornece acesso poderoso a banco de dados com suporte a manipulação dos dados, o que o torna um ajuste perfeito se seus dados de recursos estiverem armazenado em bases de dados. |
|
||||||
|
|
||||||
Nesta seção, vamos principalmente descrever como uma classe de recurso que se estende de [[yii\base\Model]] (ou alguma classe filha) pode especificar quais os dados podem ser retornados via APIs RESTful. Se a classe de recurso não estender de [[yii\base\Model]], então todas as suas variáveis públicas serão retornadas. |
|
||||||
|
|
||||||
|
|
||||||
## Campos <span id="fields"></span> |
|
||||||
|
|
||||||
Ao incluir um recurso em uma resposta da API RESTful, o recurso precisa ser serializado em uma string. O Yii quebra este processo em duas etapas. Primeiro, o recurso é convertido em um array utilizando [[yii\rest\Serializer]]. Por último, o array é serializado em uma string no formato solicitado (ex. JSON, XML) através do [[yii\web\ResponseFormatterInterface|response formatters]]. O primeiro passo é o que você deve centrar-se principalmente no desenvolvimento de uma classe de recurso. |
|
||||||
|
|
||||||
Sobrescrevendo [[yii\base\Model::fields()|fields()]] e/ou [[yii\base\Model::extraFields()|extraFields()]], você pode especificar quais os dados, chamados *fields*, no recurso podem ser colocados no array. |
|
||||||
A diferença entre estes dois métodos é que o primeiro especifica o conjunto padrão de campos que devem ser incluídos no array, enquanto que o último especifica campos adicionais que podem ser incluídos no array, se um usuário final solicitá-los via o parâmetro de pesquisa `expand`. Por exemplo: |
|
||||||
|
|
||||||
``` |
|
||||||
// retorna todos os campos declarados em fields() |
|
||||||
http://localhost/users |
|
||||||
|
|
||||||
// retorna apenas os campos id e email, desde que estejam declarados em fields() |
|
||||||
http://localhost/users?fields=id,email |
|
||||||
|
|
||||||
// retorna todos os campos de fields() e o campo profile se este estiver no extraFields() |
|
||||||
http://localhost/users?expand=profile |
|
||||||
|
|
||||||
// retorna apenas o campo id, email e profile, desde que estejam em fields() e extraFields() |
|
||||||
http://localhost/users?fields=id,email&expand=profile |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
### Sobrescrevendo `fields()` <span id="overriding-fields"></span> |
|
||||||
|
|
||||||
Por padrão, [[yii\base\Model::fields()]] retorna todos os atributos do model como campos, enquanto [[yii\db\ActiveRecord::fields()]] só retorna os atributos que tenham sido preenchidos a partir do DB. |
|
||||||
|
|
||||||
Você pode sobrescrever `fields()` para adicionar, remover, renomear ou redefinir campos. O valor do retorno de `fields()` deve ser um array. As chaves do array são os nomes dos campos e os valores são as definições dos campos correspondentes, que podem ser tanto nomes de propriedade/atributo ou funções anônimas retornando o valor do campo correspondente. No caso especial de um nome de um campo for o mesmo que sua definição de nome de atributo, você pode omitir a chave do array. Por exemplo: |
|
||||||
|
|
||||||
```php |
|
||||||
// explicitamente lista todos os campos, |
|
||||||
// melhor usado quando você quer ter certeza de que as alterações |
|
||||||
// na sua tabela ou atributo do model não causaram alterações |
|
||||||
// nos seus campos (Manter compatibilidade da API). |
|
||||||
public function fields() |
|
||||||
{ |
|
||||||
return [ |
|
||||||
// Nome do campo é igual ao nome do atributo |
|
||||||
'id', |
|
||||||
// nome do campo é "email", o nome do atributo correspondente é "email_address" |
|
||||||
'email' => 'email_address', |
|
||||||
// nome do campo é "name", seu valor é definido por um PHP callback |
|
||||||
'name' => function ($model) { |
|
||||||
return $model->first_name . ' ' . $model->last_name; |
|
||||||
}, |
|
||||||
]; |
|
||||||
} |
|
||||||
|
|
||||||
// filtrar alguns campos, melhor usado quando você deseja herdar a implementação do pai |
|
||||||
// e deseja esconder alguns campos confidenciais. |
|
||||||
public function fields() |
|
||||||
{ |
|
||||||
$fields = parent::fields(); |
|
||||||
|
|
||||||
// remove campos que contém informações confidenciais |
|
||||||
unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']); |
|
||||||
|
|
||||||
return $fields; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
> Aviso: Como o padrão é ter todos os atributos de um model incluídos |
|
||||||
> no resultados da API, você deve examinar os seus dados para certificar-se de que |
|
||||||
> eles não contenham informações confidenciais. |
|
||||||
> Se existirem tais informações, você deve sobrescrever `fields()` para filtrá-los. |
|
||||||
> No exemplo acima, nós escolhemos filtrar `auth_key`, |
|
||||||
> `password_hash` e `password_reset_token`. |
|
||||||
|
|
||||||
|
|
||||||
### Sobrescrevendo `extraFields()` <span id="overriding-extra-fields"></span> |
|
||||||
|
|
||||||
Por padrão, o [[yii\base\Model::extraFields()]] não retorna nada, enquanto o [[yii\db\ActiveRecord::extraFields()]] retorna os nomes das relações que foram populadas a partir do DB. |
|
||||||
|
|
||||||
O formato do retorno dos dados do `extraFields()` é o mesmo de `fields()`. Geralmente, `extraFields()` é mais usado para especificar os campos cujos valores são objetos. Por exemplo, dada a seguinte declaração de campo, |
|
||||||
|
|
||||||
```php |
|
||||||
public function fields() |
|
||||||
{ |
|
||||||
return ['id', 'email']; |
|
||||||
} |
|
||||||
|
|
||||||
public function extraFields() |
|
||||||
{ |
|
||||||
return ['profile']; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
o request com `http://localhost/users?fields=id,email&expand=profile` pode retornar o seguinte dados em formato JSON: |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
{ |
|
||||||
"id": 100, |
|
||||||
"email": "100@example.com", |
|
||||||
"profile": { |
|
||||||
"id": 100, |
|
||||||
"age": 30, |
|
||||||
} |
|
||||||
}, |
|
||||||
... |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Links <span id="links"></span> |
|
||||||
|
|
||||||
[HATEOAS](http://en.wikipedia.org/wiki/HATEOAS) é uma abreviação de “Hypermedia as the Engine of Application State”, que promove as APIs Restfull retornarem informações para permitir aos clientes descobrirem quais ações são suportadas pelos recursos retornados. O sentido de HATEOAS é retornar um conjunto de hiperlinks em relação às informações quando os recursos de dados são servidos pelas APIs. |
|
||||||
|
|
||||||
Suas classes de recursos podem suportar HATEOAS implementando a interface [[yii\web\Linkable]]. Esta interface contém um único método [[yii\web\Linkable::getLinks()|getLinks()]] que deve retornar uma lista de [[yii\web\Link|links]]. |
|
||||||
Tipicamente, você deve retornar pelo menos o link `self` representando a URL para o mesmo objeto de recurso. Por exemplo: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\db\ActiveRecord; |
|
||||||
use yii\web\Link; |
|
||||||
use yii\web\Linkable; |
|
||||||
use yii\helpers\Url; |
|
||||||
|
|
||||||
class User extends ActiveRecord implements Linkable |
|
||||||
{ |
|
||||||
public function getLinks() |
|
||||||
{ |
|
||||||
return [ |
|
||||||
Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true), |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Quando o objeto `User` for retornado em uma resposta, será composto de um elemento `_links` representando os links relacionados ao *user*, por exemplo: |
|
||||||
|
|
||||||
``` |
|
||||||
{ |
|
||||||
"id": 100, |
|
||||||
"email": "user@example.com", |
|
||||||
// ... |
|
||||||
"_links" => { |
|
||||||
"self": { |
|
||||||
"href": "https://example.com/users/100" |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Collections (Coleções) <span id="collections"></span> |
|
||||||
|
|
||||||
Objetos de recursos podem ser agrupados em *collections*. Cada collection contém uma lista de objetos de recurso do mesmo tipo. |
|
||||||
|
|
||||||
Embora os collections podem ser representados como arrays, normalmente, é preferível representá-los como [data providers](output-data-providers.md). Isto porque data providers suportam ordenação e paginação de recursos, que é um recurso comumente necessário para APIs RESTful retornarem collections. Por exemplo, ação a seguir retorna um data provider sobre o recurso *post*: |
|
||||||
|
|
||||||
```php |
|
||||||
namespace app\controllers; |
|
||||||
|
|
||||||
use yii\rest\Controller; |
|
||||||
use yii\data\ActiveDataProvider; |
|
||||||
use app\models\Post; |
|
||||||
|
|
||||||
class PostController extends Controller |
|
||||||
{ |
|
||||||
public function actionIndex() |
|
||||||
{ |
|
||||||
return new ActiveDataProvider([ |
|
||||||
'query' => Post::find(), |
|
||||||
]); |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Quando um data provider está enviando uma resposta com a API RESTful, o [[yii\rest\Serializer]] pegará a página atual de recursos e a serializa como um array de objetos de recurso. Adicionalmente, o [[yii\rest\Serializer]] também incluirá as informações de paginação pelo seguinte cabeçalho HTTP: |
|
||||||
|
|
||||||
* `X-Pagination-Total-Count`: O número total de recursos; |
|
||||||
* `X-Pagination-Page-Count`: O número de páginas; |
|
||||||
* `X-Pagination-Current-Page`: A página atual (a primeira página é 1); |
|
||||||
* `X-Pagination-Per-Page`: O numero de recursos em cada página; |
|
||||||
* `Link`: Um conjunto de links de navegação, permitindo que o cliente percorra os recursos página por página. |
|
||||||
|
|
||||||
Um exemplo pode ser encontrado na seção [Introdução](rest-quick-start.md#trying-it-out). |
|
||||||
|
|
||||||
|
|
@ -1,139 +0,0 @@ |
|||||||
Formatando Respostas |
|
||||||
=================== |
|
||||||
|
|
||||||
Ao manipular uma requisição da API RESTful, a aplicação normalmente realiza as seguintes etapas que estão relacionadas com a formatação da resposta: |
|
||||||
|
|
||||||
1. Determinar diversos fatores que podem afetar o formato da resposta, tais como tipo de mídia, idioma, versão, etc. Este processo também é conhecido como [negociação de conteúdo (*content negotiation*)](http://en.wikipedia.org/wiki/Content_negotiation). |
|
||||||
2. Converter objetos de recursos em arrays, como descrito na seção [Recursos](rest-resources.md). Isto é feito por [[yii\rest\Serializer]]. |
|
||||||
3. Converte arrays em uma string no formato como determinado pela etapa de negociação de conteúdo. Isto é feito pelos [[yii\web\ResponseFormatterInterface|formatadores de respostas]] registrados na propriedade [[yii\web\Response::formatters|formatters]] do [componente de aplicação](structure-application-components.md) `response`. |
|
||||||
|
|
||||||
|
|
||||||
## Negociação de Conteúdo <span id="content-negotiation"></span> |
|
||||||
|
|
||||||
O Yii suporta a negociação de conteúdo através do filtro [[yii\filters\ContentNegotiator]]. A classe base de controller API RESTful [[yii\rest\Controller]] está equipado com este filtro sob o nome de `contentNegotiator`. O filtro fornece negociação de formato de resposta, bem como negociação de idioma. Por exemplo, se uma requisição da API RESTful tiver o seguinte cabeçalho, |
|
||||||
|
|
||||||
``` |
|
||||||
Accept: application/json; q=1.0, */*; q=0.1 |
|
||||||
``` |
|
||||||
|
|
||||||
ele obterá uma resposta em formato JSON, como o seguinte: |
|
||||||
|
|
||||||
``` |
|
||||||
$ curl -i -H "Accept: application/json; q=1.0, */*; q=0.1" "http://localhost/users" |
|
||||||
|
|
||||||
HTTP/1.1 200 OK |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
X-Powered-By: PHP/5.4.20 |
|
||||||
X-Pagination-Total-Count: 1000 |
|
||||||
X-Pagination-Page-Count: 50 |
|
||||||
X-Pagination-Current-Page: 1 |
|
||||||
X-Pagination-Per-Page: 20 |
|
||||||
Link: <http://localhost/users?page=1>; rel=self, |
|
||||||
<http://localhost/users?page=2>; rel=next, |
|
||||||
<http://localhost/users?page=50>; rel=last |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
[ |
|
||||||
{ |
|
||||||
"id": 1, |
|
||||||
... |
|
||||||
}, |
|
||||||
{ |
|
||||||
"id": 2, |
|
||||||
... |
|
||||||
}, |
|
||||||
... |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Por baixo dos panos, antes de uma ação do controlador API RESTful ser executada, o filtro [[yii\filters\ContentNegotiator]] verificará o `Accept` do cabeçalho HTTP na requisição e definirá o [[yii\web\Response::format|response format]] para `'json'`. Após a ação ser executada e retornar o objeto resultante de recursos ou coleção, [[yii\rest\Serializer]] converterá o resultado em um array. E finalmente, [[yii\web\JsonResponseFormatter]] irá serializar o array em uma string JSON e incluí-la no corpo da resposta. |
|
||||||
|
|
||||||
Por padrão, APIs RESTful suportam tanto os formatos JSON quanto XML. Para suportar um novo formato, você deve configurar a propriedade [[yii\filters\ContentNegotiator::formats|formats]] do filtro `contentNegotiator` como mostrado no exemplo a seguir em suas classes do controlador da API: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\web\Response; |
|
||||||
|
|
||||||
public function behaviors() |
|
||||||
{ |
|
||||||
$behaviors = parent::behaviors(); |
|
||||||
$behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_HTML; |
|
||||||
return $behaviors; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
As chaves da propriedade `formats` são os tipos MIME suportados, enquanto os valores são os nomes de formato de resposta correspondentes que devem ser suportados em |
|
||||||
[[yii\web\Response::formatters]]. |
|
||||||
|
|
||||||
|
|
||||||
## Serializando Dados <span id="data-serializing"></span> |
|
||||||
|
|
||||||
Como foi descrito acima, [[yii\rest\Serializer]] é a peça central responsável pela conversão de objetos de recursos ou coleções em arrays. Ele reconhece objetos que implementam a interface [[yii\base\ArrayableInterface]] bem como [[yii\data\DataProviderInterface]]. O primeiro é aplicado principalmente pelos objetos de recursos, enquanto o último se aplica mais a coleções de recursos. |
|
||||||
|
|
||||||
Você pode configurar o serializador, definindo a propriedade [[yii\rest\Controller::serializer]] com um array de configuração. |
|
||||||
Por exemplo, às vezes você pode querer ajudar a simplificar o trabalho de desenvolvimento do cliente, incluindo informações de paginação diretamente no corpo da resposta. Para fazê-lo, configure a propriedade [[yii\rest\Serializer::collectionEnvelope]] como a seguir: |
|
||||||
|
|
||||||
```php |
|
||||||
use yii\rest\ActiveController; |
|
||||||
|
|
||||||
class UserController extends ActiveController |
|
||||||
{ |
|
||||||
public $modelClass = 'app\models\User'; |
|
||||||
public $serializer = [ |
|
||||||
'class' => 'yii\rest\Serializer', |
|
||||||
'collectionEnvelope' => 'items', |
|
||||||
]; |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Você pode, então, obter a seguinte resposta para a url `http://localhost/users`: |
|
||||||
|
|
||||||
``` |
|
||||||
HTTP/1.1 200 OK |
|
||||||
Date: Sun, 02 Mar 2014 05:31:43 GMT |
|
||||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y |
|
||||||
X-Powered-By: PHP/5.4.20 |
|
||||||
X-Pagination-Total-Count: 1000 |
|
||||||
X-Pagination-Page-Count: 50 |
|
||||||
X-Pagination-Current-Page: 1 |
|
||||||
X-Pagination-Per-Page: 20 |
|
||||||
Link: <http://localhost/users?page=1>; rel=self, |
|
||||||
<http://localhost/users?page=2>; rel=next, |
|
||||||
<http://localhost/users?page=50>; rel=last |
|
||||||
Transfer-Encoding: chunked |
|
||||||
Content-Type: application/json; charset=UTF-8 |
|
||||||
|
|
||||||
{ |
|
||||||
"items": [ |
|
||||||
{ |
|
||||||
"id": 1, |
|
||||||
... |
|
||||||
}, |
|
||||||
{ |
|
||||||
"id": 2, |
|
||||||
... |
|
||||||
}, |
|
||||||
... |
|
||||||
], |
|
||||||
"_links": { |
|
||||||
"self": { |
|
||||||
"href": "http://localhost/users?page=1" |
|
||||||
}, |
|
||||||
"next": { |
|
||||||
"href": "http://localhost/users?page=2" |
|
||||||
}, |
|
||||||
"last": { |
|
||||||
"href": "http://localhost/users?page=50" |
|
||||||
} |
|
||||||
}, |
|
||||||
"_meta": { |
|
||||||
"totalCount": 1000, |
|
||||||
"pageCount": 50, |
|
||||||
"currentPage": 1, |
|
||||||
"perPage": 20 |
|
||||||
} |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
@ -1,85 +0,0 @@ |
|||||||
Roteamento |
|
||||||
======= |
|
||||||
|
|
||||||
Com as classes de recurso e controller prontas, você pode acessar os recursos utilizando uma URL como `http://localhost/index.php?r=user/create`, semelhante ao que você pode fazer com aplicações Web normais. |
|
||||||
|
|
||||||
Na prática, normalmente você desejará utilizar URLs amigáveis e tirar proveito dos métodos HTTP. |
|
||||||
Por exemplo, uma requisição `POST /users` seria o mesmo que a ação `user/create`. |
|
||||||
Isto pode ser feito facilmente através da configuração do [componente de aplicação](structure-application-components.md) `urlManager` conforme mostrado a seguir: |
|
||||||
|
|
||||||
```php |
|
||||||
'urlManager' => [ |
|
||||||
'enablePrettyUrl' => true, |
|
||||||
'enableStrictParsing' => true, |
|
||||||
'showScriptName' => false, |
|
||||||
'rules' => [ |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => 'user'], |
|
||||||
], |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Em comparação com o gerenciamento de URL para aplicações Web, a principal novidade acima é o uso de [[yii\rest\UrlRule]] para rotear requisições API RESTful. Esta classe especial criará um conjunto de regras de URL filhas para dar suporte ao roteamento e a criação de URL para o controller especificado. |
|
||||||
Por exemplo, o código acima é mais ou menos equivalente às seguintes regras: |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'PUT,PATCH users/<id>' => 'user/update', |
|
||||||
'DELETE users/<id>' => 'user/delete', |
|
||||||
'GET,HEAD users/<id>' => 'user/view', |
|
||||||
'POST users' => 'user/create', |
|
||||||
'GET,HEAD users' => 'user/index', |
|
||||||
'users/<id>' => 'user/options', |
|
||||||
'users' => 'user/options', |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
E as seguintes URLs (também chamadas de *endpoints*) da API são suportados por esta regra: |
|
||||||
|
|
||||||
* `GET /users`: lista todos os usuários página por página; |
|
||||||
* `HEAD /users`: mostrar a informações gerais da listagem de usuários; |
|
||||||
* `POST /users`: cria um novo usuário; |
|
||||||
* `GET /users/123`: retorna detalhes do usuário 123; |
|
||||||
* `HEAD /users/123`: mostrar a informações gerais do usuário 123; |
|
||||||
* `PATCH /users/123` and `PUT /users/123`: atualiza o usuário 123; |
|
||||||
* `DELETE /users/123`: deleta o usuário 123; |
|
||||||
* `OPTIONS /users`: exibe os métodos suportados pela URL `/users`; |
|
||||||
* `OPTIONS /users/123`: exibe os métodos suportados pela URL `/users/123`. |
|
||||||
|
|
||||||
Você pode configurar as opções `only` e `except` para listar explicitamente quais ações são suportadas ou quais ações devem ser desativadas, respectivamente. Por exemplo, |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'class' => 'yii\rest\UrlRule', |
|
||||||
'controller' => 'user', |
|
||||||
'except' => ['delete', 'create', 'update'], |
|
||||||
], |
|
||||||
``` |
|
||||||
|
|
||||||
Você também pode configurar `patterns` ou `extraPatterns` para redefinir padrões existentes ou adicionar novos padrões suportados por esta regra. Por exemplo, para acessar a uma nova ação `search` pela URL `GET /users/search`, configure a opção `extraPatterns` como a seguir, |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'class' => 'yii\rest\UrlRule', |
|
||||||
'controller' => 'user', |
|
||||||
'extraPatterns' => [ |
|
||||||
'GET search' => 'search', |
|
||||||
], |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
Você deve ter notado que o ID `user` de controller aparece no plural como `users` na extremidade das URLs. Isto acontece porque [[yii\rest\UrlRule]] pluraliza os IDs de controllers automaticamente na criação de regras de URLs filhas. |
|
||||||
Você pode desabilitar este comportamento configurando [[yii\rest\UrlRule::pluralize]] para `false`. |
|
||||||
|
|
||||||
> Observação: A pluralização dos IDs de controllers são feitas pelo método [[yii\helpers\Inflector::pluralize()]]. O método respeita as regras especiais de pluralização. Por exemplo, a palavra `box` será pluralizada para `boxes` em vez de `boxs`. |
|
||||||
|
|
||||||
|
|
||||||
Caso a pluralização automática não encontre uma opção para a palavra requerida, você pode configurar a propriedade [[yii\rest\UrlRule::controller]] para especificar explicitamente como mapear um nome para ser usado como uma URL para um ID de controller. Por exemplo, o seguinte código mapeia o nome `u` para o ID `user` de controller. |
|
||||||
|
|
||||||
```php |
|
||||||
[ |
|
||||||
'class' => 'yii\rest\UrlRule', |
|
||||||
'controller' => ['u' => 'user'], |
|
||||||
] |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
@ -1,94 +0,0 @@ |
|||||||
Versionamento |
|
||||||
========== |
|
||||||
|
|
||||||
Uma boa API é *versionada*: Mudanças e novos recursos são implementados em novas versões da API em vez de alterar continuamente apenas uma versão. Diferente de aplicações Web, com a qual você tem total controle do código de ambos os lados cliente e servidor, APIs são destinadas a ser utilizadas por clientes além de seu controle. Por esta razão, a compatibilidade (BC) entre as APIs deve ser mantida sempre que possível. Se uma mudança que pode quebrar esta compatibilidade é necessária, você deve introduzi-la em uma nova versão de API e subir o número da versão. Os clientes existentes podem continuar a usar a versão antiga da API; e os clientes novos ou atualizados podem obter a nova funcionalidade na nova versão da API. |
|
||||||
|
|
||||||
> Dica: Consulte o artigo [Semantic Versioning](http://semver.org/) para obter mais informações sobre como projetar números de versão da API. |
|
||||||
|
|
||||||
Uma maneira comum de implementar versionamento de API é incorporar o número da versão nas URLs da API. Por exemplo, `http://example.com/v1/users` representa o terminal `/users` da API versão 1. |
|
||||||
|
|
||||||
Outro método de versionamento de API, que tem sido muito utilizado recentemente, é colocar o número da versão nos cabeçalhos das requisições HTTP. Isto é tipicamente feito através do cabeçalho `Accept`: |
|
||||||
|
|
||||||
``` |
|
||||||
// Através de um parâmetro |
|
||||||
Accept: application/json; version=v1 |
|
||||||
// através de um vendor content type |
|
||||||
Accept: application/vnd.company.myapp-v1+json |
|
||||||
``` |
|
||||||
|
|
||||||
Ambos os métodos tem seus prós e contras, e há uma série de debates sobre cada abordagem. A seguir, você verá uma estratégia prática para o controle de versão de API que é uma mistura dos dois métodos: |
|
||||||
|
|
||||||
* Coloque cada versão principal de implementação da API em um módulo separado cuja identificação é o número de versão principal (ex. `v1`, `v2`). Naturalmente, as URLs da API irão conter os números da versão principal. |
|
||||||
* Dentro de cada versão principal (e, assim, dentro do módulo correspondente), utilize o cabeçalho `Accept` da requisição HTTP para determinar o número de versão secundária e escrever código condicional para responder às versões menores em conformidade. |
|
||||||
|
|
||||||
Para cada módulo destinado a uma versão principal, deve incluir o recurso e a classe de controller destinados a esta versão específica. |
|
||||||
Para melhor separar a responsabilidade do código, você pode manter um conjunto comum de classes base de recursos e de controller e criar subclasses delas para cada versão individual do módulo. Dentro das subclasses, implementar o código concreto, tais como `Model::fields()`. |
|
||||||
|
|
||||||
Seu código pode ser organizado da seguinte maneira: |
|
||||||
|
|
||||||
``` |
|
||||||
api/ |
|
||||||
common/ |
|
||||||
controllers/ |
|
||||||
UserController.php |
|
||||||
PostController.php |
|
||||||
models/ |
|
||||||
User.php |
|
||||||
Post.php |
|
||||||
modules/ |
|
||||||
v1/ |
|
||||||
controllers/ |
|
||||||
UserController.php |
|
||||||
PostController.php |
|
||||||
models/ |
|
||||||
User.php |
|
||||||
Post.php |
|
||||||
Module.php |
|
||||||
v2/ |
|
||||||
controllers/ |
|
||||||
UserController.php |
|
||||||
PostController.php |
|
||||||
models/ |
|
||||||
User.php |
|
||||||
Post.php |
|
||||||
Module.php |
|
||||||
``` |
|
||||||
|
|
||||||
A configuração da sua aplicação seria algo como: |
|
||||||
|
|
||||||
```php |
|
||||||
return [ |
|
||||||
'modules' => [ |
|
||||||
'v1' => [ |
|
||||||
'class' => 'app\modules\v1\Module', |
|
||||||
], |
|
||||||
'v2' => [ |
|
||||||
'class' => 'app\modules\v2\Module', |
|
||||||
], |
|
||||||
], |
|
||||||
'components' => [ |
|
||||||
'urlManager' => [ |
|
||||||
'enablePrettyUrl' => true, |
|
||||||
'enableStrictParsing' => true, |
|
||||||
'showScriptName' => false, |
|
||||||
'rules' => [ |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => ['v1/user', 'v1/post']], |
|
||||||
['class' => 'yii\rest\UrlRule', 'controller' => ['v2/user', 'v2/post']], |
|
||||||
], |
|
||||||
], |
|
||||||
], |
|
||||||
]; |
|
||||||
``` |
|
||||||
|
|
||||||
Como resultado do código acima, `http://example.com/v1/users` retornará a lista de usuários na versão 1, enquanto `http://example.com/v2/users` retornará a lista de usuários na versão 2. |
|
||||||
|
|
||||||
Graças aos módulos, o código para diferentes versões principais pode ser bem isolado. Entretanto esta abordagem torna possível a reutilização de código entre os módulos através de classes bases comuns e outros recursos partilhados. |
|
||||||
|
|
||||||
Para lidar com números de subversões, você pode tirar proveito da negociação de conteúdo oferecida pelo behavior [[yii\filters\ContentNegotiator|contentNegotiator]]. O behavior `contentNegotiator` irá configurar a propriedade [[yii\web\Response::acceptParams]] que determinará qual versão é suportada. |
|
||||||
|
|
||||||
Por exemplo, se uma requisição é enviada com o cabeçalho HTTP `Accept: application/json; version=v1`, Após a negociação de conteúdo, [[yii\web\Response::acceptParams]] terá o valor `['version' => 'v1']`. |
|
||||||
|
|
||||||
Com base na informação da versão em `acceptParams`, você pode escrever um código condicional em lugares tais como ações, classes de recursos, serializadores, etc. para fornecer a funcionalidade apropriada. |
|
||||||
|
|
||||||
|
|
||||||
Uma vez que subversões por definição devem manter compatibilidades entre si, esperamos que não haja muitas verificações de versão em seu código. De outra forma, você pode precisar criar uma nova versão principal. |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue