Rest
El módulo REST implementa un CRUD básico,
con un sistema de autenticación, directamente comprobable en la parte de administración.
REST y enrutamiento
El enrutador es esencial para el módulo REST, ya que REST (Respresentation State Transfer) se basa en URL y métodos HTTP.
Nota
Por razones de rendimiento, las rutas REST se almacenan en caché independientemente de otras rutas.
Por lo tanto, es necesario iniciar el enrutador de una manera particular para activar las rutas REST y no obtener un error 404 recurrente.
El router se inicia en services.php
.
Sin activación de rutas REST:
...
Router::start();
Para habilitar rutas REST en una aplicación que también tiene una parte no REST:
...
Router::startAll();
Para activar sólo las rutas Rest:
Router::startRest();
Es posible iniciar el enrutamiento condicionalmente (este método sólo será más eficiente si el número de rutas es grande en cualquiera de las partes):
...
if($config['isRest']()){
Router::startRest();
}else{
Router::start();
}
Recursos REST
Un controlador REST puede asociarse directamente a un modelo.
Nota
Si no tiene a mano una base de datos mysql, puede descargar ésta: messagerie.sql
Creación
Con devtools:
Ubiquity rest RestUsersController -r=User -p=/rest/users
O con webtools:
Vaya a la sección REST y elija Añadir un nuevo recurso:
El controlador creado :
1 namespace controllers;
2
3 /**
4 * Rest Controller RestUsersController
5 * @route("/rest/users","inherited"=>true,"automated"=>true)
6 * @rest("resource"=>"models\\User")
7 */
8 class RestUsersController extends \Ubiquity\controllers\rest\RestController {
9
10 }
Dado que los atributos automated y inherited de la ruta están en true, el controlador tiene las rutas por defecto de la clase padre.
The base controller RestController is not standardized, it should be considered as an example for data interrogation.
Interfaz de prueba
Webtools ofrecen una interfaz para consultar datos:
Obtener una instancia
Se puede acceder a una instancia de usuario por su clave principal (id):
Inclusión de miembros asociados: la organización del usuario
Inclusión de miembros asociados: organización, conexiones y grupos del usuario
Obtener varias instancias
Obtener todas las instancias:
Establecer una condición:
Incluidos los miembros asociados:
Añadir una instancia
Los datos se envían mediante el método POST, con un tipo de contenido definido en application/x-www-form-urlencoded
:
Añada los parámetros de nombre y dominio pulsando el botón parámetros:
La adición requiere una autenticación, por lo que se genera un error, con el estado 401:
La interfaz de administración permite simular la autenticación por defecto y obtener un token, solicitando el método connect:
El token se envía automáticamente en las siguientes solicitudes.
A continuación, se puede insertar el registro.
Actualización de una instancia
La actualización sigue el mismo esquema que la inserción.
Borrar una instancia
Personalización
Rutas
Por supuesto, es posible personalizar y simplificar las rutas.
En este caso, es preferible utilizar la herencia de la clase RestBaseController, y no habilitar las rutas automáticas.
1namespace controllers;
2
3use models\Organization;
4
5/**
6 * Rest Controller for organizations
7 *
8 * @route("/orgas")
9 * @rest
10 */
11class RestOrgas extends \Ubiquity\controllers\rest\RestBaseController {
12
13 public function initialize() {
14 $this->model = Organization::class;
15 parent::initialize();
16 }
17
18 /**
19 *
20 * @get
21 */
22 public function index() {
23 $this->_get();
24 }
25
26 /**
27 *
28 * @get("{keyValues}")
29 */
30 public function get($keyValues) {
31 $this->_getOne($keyValues);
32 }
33
34 /**
35 *
36 * @post("/")
37 */
38 public function add() {
39 $this->_add();
40 }
41
42 /**
43 *
44 * @patch("{keyValues}")
45 */
46 public function update(...$keyValues) {
47 $this->_update(...$keyValues);
48 }
49
50 /**
51 *
52 * @delete("{keyValues}")
53 */
54 public function delete(...$keyValues) {
55 $this->_delete(...$keyValues);
56 }
57}
Tras reinicializar la caché, la interfaz de prueba muestra las rutas accesibles:
Modificación de los datos enviados
By overriding
Es posible modificar los datos enviados a los métodos update y add, para añadir, modificar o borrar el valor de los campos antes de enviarlos.
Ya sea sobredefiniendo el método getDatas:
...
protected function getDatas() {
$datas = parent::getDatas();
unset($datas['aliases']);// Remove aliases field
return $datas;
}
Con eventos
O bien de forma más global actuando sobre los eventos de descanso:
use Ubiquity\events\EventsManager;
use Ubiquity\events\RestEvents;
use Ubiquity\controllers\rest\RestBaseController;
...
EventsManager::addListener(RestEvents::BEFORE_INSERT, function ($o, array &$datas, RestBaseController $resource) {
unset($datas['aliases']);// Remove aliases field
});
Autentificación
Ubiquity REST implementa una autenticación Oauth2 con tokens Bearer. |br|Sólo los métodos con la anotación ``@authorization`` requieren la autenticación, estos son los métodos de modificación (añadir, actualizar y eliminar). |br|
/**
* Update an instance of $model selected by the primary key $keyValues
* Require members values in $_POST array
* Requires an authorization with access token
*
* @param array $keyValues
* @authorization
* @route("methods"=>["patch"])
*/
public function update(...$keyValues) {
$this->_update ( ...$keyValues );
}
El método connect de un controlador REST establece la conexión y devuelve un nuevo token.
Corresponde al desarrollador anular este método para gestionar una posible autenticación con nombre de usuario y contraseña.
Simulación de una conexión con inicio de sesión
En este ejemplo, la conexión consiste simplemente en enviar una variable de usuario por el método post.
Si se proporciona el usuario, el método connect
de la instancia $server
devuelve un token válido que se almacena en sesión (la sesión actúa aquí como base de datos).
1 namespace controllers;
2
3 use Ubiquity\utils\http\URequest;
4 use Ubiquity\utils\http\USession;
5
6 /**
7 * Rest Controller RestOrgas
8 * @route("/rest/orgas","inherited"=>true,"automated"=>true)
9 * @rest("resource"=>"models\\Organization")
10 */
11 class RestOrgas extends \Ubiquity\controllers\rest\RestController {
12
13 /**
14 * This method simulate a connection.
15 * Send a <b>user</b> variable with <b>POST</b> method to retreive a valid access token
16 * @route("methods"=>["post"])
17 */
18 public function connect(){
19 if(!URequest::isCrossSite()){
20 if(URequest::isPost()){
21 $user=URequest::post("user");
22 if(isset($user)){
23 $tokenInfos=$this->server->connect ();
24 USession::set($tokenInfos['access_token'], $user);
25 $tokenInfos['user']=$user;
26 echo $this->_format($tokenInfos);
27 return;
28 }
29 }
30 }
31 throw new \Exception('Unauthorized',401);
32 }
33 }
Para cada solicitud con autenticación, es posible recuperar el usuario conectado (se añade aquí en las cabeceras de respuesta) :
1 namespace controllers;
2
3 use Ubiquity\utils\http\URequest;
4 use Ubiquity\utils\http\USession;
5
6 /**
7 * Rest Controller RestOrgas
8 * @route("/rest/orgas","inherited"=>true,"automated"=>true)
9 * @rest("resource"=>"models\\Organization")
10 */
11 class RestOrgas extends \Ubiquity\controllers\rest\RestController {
12
13 ...
14
15 public function isValid($action){
16 $result=parent::isValid($action);
17 if($this->requireAuth($action)){
18 $key=$this->server->_getHeaderToken();
19 $user=USession::get($key);
20 $this->server->_header('active-user',$user,true);
21 }
22 return $result;
23 }
24 }
Utilice la interfaz webtools para probar la conexión:
Personalización
Api tokens
Es posible personalizar la generación de tokens anulando el método getRestServer
:
1 namespace controllers;
2
3 use Ubiquity\controllers\rest\RestServer;
4 class RestOrgas extends \Ubiquity\controllers\rest\RestController {
5
6 ...
7
8 protected function getRestServer(): RestServer {
9 $srv= new RestServer($this->config);
10 $srv->setTokenLength(32);
11 $srv->setTokenDuration(4800);
12 return $srv;
13 }
14 }
Orígenes y CORS permitidos
Compartición de recursos entre orígenes (CORS)
Si accede a su api desde otro sitio, es necesario configurar CORS.
En este caso, para peticiones de tipo PATCH
, PUT
, DELETE
, tu api debe definir una ruta que permita a CORS realizar su control pre-petición mediante el método OPTIONS
.
1 class RestOrgas extends \Ubiquity\controllers\rest\RestController {
2
3 ...
4
5 /**
6 * @options('{url}')
7 */
8 public function options($url='') {}
9 }
Orígenes permitidos
Los orígenes permitidos permiten definir los clientes que pueden acceder al recurso en caso de una petición entre dominios definiendo la cabecera de respuesta Access-Control-Allow-Origin.
Este campo de cabecera es devuelto por el método OPTIONS
.
1 class RestOrgas extends \Ubiquity\controllers\rest\RestController {
2
3 ...
4
5 protected function getRestServer(): RestServer {
6 $srv= new RestServer($this->config);
7 $srv->setAllowedOrigin('http://mydomain/');
8 return $srv;
9 }
10 }
Es posible autorizar varios orígenes:
1 class RestOrgas extends \Ubiquity\controllers\rest\RestController {
2
3 ...
4
5 protected function getRestServer(): RestServer {
6 $srv= new RestServer($this->config);
7 $srv->setAllowedOrigins(['http://mydomain1/','http://mydomain2/']);
8 return $srv;
9 }
10 }
Respuesta
Para cambiar el formato de las respuestas, es necesario crear una clase que herede de ResponseFormatter
.
Nos inspiraremos en HAL, y cambiaremos el formato de las respuestas por:
añadir un enlace a sí mismo para cada recurso
añadir un atributo
_embedded
para las coleccioneseliminación del atributo
data
para los recursos únicos
1 namespace controllers\rest;
2
3 use Ubiquity\controllers\rest\ResponseFormatter;
4 use Ubiquity\orm\OrmUtils;
5
6 class MyResponseFormatter extends ResponseFormatter {
7
8 public function cleanRestObject($o, &$classname = null) {
9 $pk = OrmUtils::getFirstKeyValue ( $o );
10 $r=parent::cleanRestObject($o);
11 $r["links"]=["self"=>"/rest/orgas/get/".$pk];
12 return $r;
13 }
14
15 public function getOne($datas) {
16 return $this->format ( $this->cleanRestObject ( $datas ) );
17 }
18
19 public function get($datas, $pages = null) {
20 $datas = $this->getDatas ( $datas );
21 return $this->format ( [ "_embedded" => $datas,"count" => \sizeof ( $datas ) ] );
22 }
23 }
A continuación, asigna MyResponseFormatter
al controlador REST anulando el método getResponseFormatter
:
1 class RestOrgas extends \Ubiquity\controllers\rest\RestController {
2
3 ...
4
5 protected function getResponseFormatter(): ResponseFormatter {
6 return new MyResponseFormatter();
7 }
8 }
Comprueba los resultados con los métodos getOne y get:
APIs
A diferencia de los recursos REST, los controladores API son multirrecursos.
SimpleRestAPI
JsonApi
Ubiquity implementa la especificación jsonApi con la clase JsonApiRestController
.
JsonApi es utilizado por ``EmberJS <https://api.emberjs.com/ember-data/release/classes/DS.JSONAPIAdapter>`_and otros.
ver https://jsonapi.org/ para más.
Creación
Con devtools:
Ubiquity restapi JsonApiTest -p=/jsonapi
O con webtools:
Vaya a la sección REST y elija Añadir un nuevo recurso:
Prueba la api en webtools:
Links
La ruta links (método index) devuelve la lista de urls disponibles:
Obtener una matriz de objetos
Por defecto, se incluyen todos los miembros asociados:
Incluidos los miembros asociados
debe utilizar el parámetro include de la solicitud:
URL |
Descripción |
---|---|
|
No se incluyen miembros asociados |
|
Incluir la organización |
|
Incluir la organización y las conexiones |
|
Incluir los grupos y su organización |
Filtrado de instancias
debe utilizar el parámetro filter de la solicitud,
el parámetro filter corresponde a la parte where de una sentencia SQL:
URL |
Descripción |
---|---|
|
Sin filtro |
|
Devuelve todos los usuarios llamados Benjamin |
|
Devuelve todos los usuarios cuyo nombre empieza por B |
|
Devuelve todos los usuarios suspendidos cuyo apellido empiece por ca |
Paginación
debe utilizar los parámetros page[number] y page[size] de la solicitud:
URL |
Descripción |
---|---|
|
Sin paginación |
|
Mostrar la primera página (el tamaño de página es 1) |
|
Mostrar la primera página (el tamaño de página es 10) |
Añadir una instancia
Los datos, contenidos en data[attributes]
, se envían mediante el método POST, con un tipo de contenido definido en application/json; charset=utf-8
.
Añada sus parámetros haciendo clic en el botón parameters:
La adición requiere una autenticación, por lo que se genera un error, con el estado 401 si el token está ausente o caducado.
Borrar una instancia
El borrado requiere el método DELETE, y el uso del id del objeto a borrar: