IMPORTANT: Per accedir als fitxer de subversion: http://acacha.org/svn (sense password). Poc a poc s'aniran migrant els enllaços. Encara però funciona el subversion de la farga però no se sap fins quan... (usuari: prova i la paraula de pas 123456)

Introducció

En aquest article se realitzarà un exemple complet de Codeigniter RestServer i RestClient.

Instal·lació

Fem un fork dels següents dos projectes:

Fork RestServer.

Fem un clonatge dels dos forks fets anteriorment en local:

$ cd /usr/share
$ sudo git clone https://github.com/nicolaeturcan/codeigniter-restserver
$ sudo git clone https://github.com/nicolaeturcan/codeigniter-restclient

Jerarquia de directoris de l'exemple RestServer:

$ cd codeigniter-restserver
$ tree -d
.
├── application
│   ├── cache
│   ├── config
│   ├── controllers
│   │   ├── api
│   │   └── unit_tests
│   ├── core
│   ├── errors
│   ├── helpers
│   ├── hooks
│   ├── language
│   │   └── english
│   ├── libraries
│   ├── logs
│   ├── models
│   ├── third_party
│   └── views
└── system
    ├── core
    ├── database
    │   └── drivers
    │       ├── cubrid
    │       ├── mssql
    │       ├── mysql
    │       ├── mysqli
    │       ├── oci8
    │       ├── odbc
    │       ├── pdo
    │       ├── postgre
    │       ├── sqlite
    │       └── sqlsrv
    ├── fonts
    ├── helpers
    ├── language
    │   └── english
    └── libraries
        ├── Cache
        │   └── drivers
        └── javascript

Com podem veure, l'exemple té una estructura CodeIgniter instal·lada. Això està fer per a reforçar l'exemple i permetre tenir una demo amb la qual jugar abans d'integrar-ho a l'aplicació existent.

Configuració RestServer

Obrim el fitxer de configuració application/config/config.php i establim la base_url. Com que és un exemple només farem servir el RestServer en local:

Configuració de la base_url.

Per a que l'exemple pugui funcionar amb la URL indicada a config.php, l'hem de publicar amb l'ajuda de l'apache2.

$ /etc/apache2/sites-available

Copiem el fitxer per defecte amb un nom que faci una descripció mínima del que publicarem:

$ sudo cp 000-default.conf 003-restserver.conf

Afegim l'alias al final del fitxer indicant-li la ruta del RestServer:

$ sudo geany 003-restserver.conf
Alias /restserver /usr/share/codeigniter-restserver

Habilitem el site creat:

$ sudo a2ensite 003-restserver.conf

Reiniciem o fem un reload de l'apache2:

$ sudo service apache2 reload

Ara ja podem accedir a la demo:

Demo local.


URLs

Després de tota la configuració anterior, ara ja estem preparats per carregar la nostra instal·lació CodeIgniter REST, i fer una ullada a la demo.

La demo per defecte disposa d'uns exemples de d'enllaços al controlador example_api, que es pot trobar a application/controllers/example_api.php. La primera URL és molt simple:

Estructura URL.

Aquesta URL s'assembla molt a qualsevol altre URL de CodeIgniter amb un controlador i un mètode, però enseguida ens podem donar compte de que en aquesta diagrama el mètode que indiquem realment es un recurs. Amb REST tot va sobre recursos amb els qual se pot interactuar mitjançant peticions HTTP de forma que puguis consultar, afegir, eliminar, editar o busca recursos.

El format per defecte de la sortida és en XML, cosa que es pot canviar en el fitxer de configuració o indicar-ho a l'hora de fer la petició mitjançant una URL passant per paràmetre el format que ens interessa:

Estructura URL passant paràmetres.

En CodeIgniter normalment no se pot passar per paràmetres més d'un valors, però amb el REST s'accepta qualsevol nombre de paràmetres en qualsevol ordre. Per tal de que això funcioni, s'ha de passar al nom del paràmetre seguit pel valor de dos en dos.

Mitjançant el paràmetre reservat format del final de la URL podem canviar el format de sortida de les dades sol·licitades sense cap problema.

Exemple per defecte:

Sol·licitud per defecte en format xml.

Exemple indicat el format json:

Sol·licitud indicant el format json.

Exemple indicant el format html:

Sol·licitud indicant el format html.

Al donar-li tant al desenvolupador de l'API, com a l'aplicació client el fet de poder triar el format de dades que vol utilitzar, fa que l'API s'obri a un públic molt més ampli i que es pogui utilitzar amb més idiomes i sistemes de programació. Els tres formats vists anteriorment no són els únics suportats. L'API REST pot utilitzar també:

  • xml - suportat gairebé qualsevol llenguatge de programació.
  • json - molt útil per a aplicacions JavaScript i cada vegada més per a les de PHP.
  • csv - utilitzat per les programes de fulls de càlcul.
  • html - genera una taula HTML senzilla.
  • php - representació de codi PHP.
  • serialize - dades serialitzats que poden ser convertits de tornada en PHP.

S'ha de reconèixer que l'afegiment del format a la URL no és una forma molt característica del REST per canviar formats, però permet fer proves des del navegador de forma fàcil i sense utilitzar el cURL, simplement realitzant peticions GET simples en l'API.

Si ens interessa canviar el format per defecte de xml a un altre hem d'editar el fitxer rest.php que el podrem trobat a application/config/ dins del nostre projecte de la següent manera:

$config['rest_default_format'] = 'json';

Codi

Tot el codi que ens interessa del nostre Rest Server el podrem trobar a application/controllers/api/example.php. Al obrir-lo observarem unes quantes diferencies dels controladors normals.

REST_Controller

Com que al controlador del Rest Server hem de fer algunes coses diferents d'un controlador normal de codeigniter, necessitarem implementar la llibreria REST_Controller que conté la seva pròpia lògica relacionada amb el REST. Exemple:

<?php
class Example_api extends Controller {
}

Sense oblidar-nos d'indicar el APPPATH:

<?php
require(APPPATH'.libraries/REST_Controller.php');
class Example_api extends REST_Controller {
}

Recursos

Una vegada el controlador Rest està configurat, és hora d'omplir-lo amb mètodes o "recursos. Bàsicament, el que hem de fer és combinar els noms dels recursos i els verbs HTP per tal de crear un nom de mètode Rest. D'aquesta depenent quina petició farem, sabrem a quin mètode accedirà. Exemple:

<?php
require(APPPATH'.libraries/REST_Controller.php');
 
class Example_api extends REST_Controller {
 
    function user_get()
    {
        // Respon amb informació existent de l'usuari.
    }
 
    function user_put()
    {
        // Modifica un usuari ja existent amb informació nova. 
    }
 
    function user_post()
    {
        // Crea un usuari nou.
    }
 
    function user_delete()
    {
        // Esborra un usuari.
    }
}

Encara que la demo del RestServer vingui amb una Demo de CodeIgniter integrada, els fitxers més importants son:

  1. application/config/rest.php
  2. application/libraries/REST_Controller.php
Estructura de la demo.

Models

Si volem accedir a dades d'una base de dades el més adequat serà utilitzar mètodes dels nostres models per tal de consultar, inserir, modificar o esborrar.

Normalment, per a poder utilitzar els models s'afegeix el següent dins del constructor si s'utilitza el mateix dins de tot el controlador, o dins de cada mètode si cadascú en té d'un model diferent:

$this->load->model("User_model");

GET

S'utilitza per a consultar informació sobre un recurs existent. Normalment s'utilitza pels navegadors al introduir una URL o en fer clic en un enllaç de manera que va molt bé per anar a buscar l'informació sobre un dels seus recursos REST.

Exemple del mètode get del controlador:

    function user_get()
    {
        if(!$this->get('id'))
        {
        	$this->response(NULL, 400);
        }

        $user = $this->User_model->get_user( $this->get('id') );
    	
        if($user)
        {
            $this->response($user, 200); // 200 being the HTTP response code
        }

        else
        {
            $this->response(array('error' => 'User could not be found'), 404);
        }
    }

Com podem observar, per a consultar les dades de l'usuari, se passa un id al mètode get_user del model "User_model":

 public function get_user($id)
    {
        $this->db->select('id, name, surname');
        $this->db->where('id',$id);
        $query = $this->db->get('user');

        if($query->num_rows() == 1){
            return $query->result();
        }
    }

POST

S'utilitza per a crear un recurs o per a editar-ne un d'existent amb la informació introduïda. Els navegadors utilitzen les peticions POST per a enviar la majoria dels dels formularis d'Internet.

Exemple del mètode post del controlador que en aquest cas s'utilitza per a introduir un usuari nou:

   function new_user_post()
    {        
        if($this->post("name") || $this->post("surname")){
            
            $result = $this->User_model->add_user($this->post("name"),$this->post("surname"));
            
            if($result === FALSE)
            {
                $this->response(array('status' => 'failed'));
            }

            else
            {
                $this->response(array('status' => 'success'));
            }
        }
    }

Com podem observar, per a crear un usuari nou, se li passen tots els seus camps al mètode add_user del model "User_model":

    function add_user($name, $surname) 
    {
        $data = array(
            "name" => $name,
            "surname" => $surname
        );
        
        return $this->db->insert("user", $data);
    }

PUT

Les peticions PUT son menys usades que les altres i no està suportat per la majoria dels navegadors. S'utilitzen per a editar o modificar un recurs.

Exemple del mètode PUT del controlador:

 public function user_put()
    {
        $id = $this->put('id');
        $data = array(
            'name'=>$this->put('name'),
            'surname'=>$this->put('surname')
        );
            
        $result = $this->User_model->update_user($id,$data);
         
        if($result === false){
            $this->response(array("status" => "failed"));
        }else{
            $this->response(array("status" => "success"));
        }
    } 

Com podem observar, per a modificar un usuari existent, se li passa tots els seus camps al mètode add_user del model "User_model", que buscarà l'usuari per id i modificarà els camps editats:

 function update_user($id, $data){

        $this->db->where('id',$id);
        return $this->db->update('user',$data);
    }

DELETE

Tampoc se sol utilitzat molt sovint, i com ja podem intuir, s'utilitza per eliminar un recurs.

Exemple del mètode DELETE del controlador:

    function user_delete()
    {
    	$result = $this->User_model->delete_user( $this->get('id') );
        
        if($result === false){
            $this->response(array("status" => "failed"));
        }else{
            $this->response(array("status" => "success"));
        }    
    }

Com podem observar, per a eliminar un usuari, se li passa el seu id al mètode delete_user del model "User_model", que buscarà l'usuari per id i l'esborrarà de la base de dades:

public function delete_user($id)
    {
        $this->db->where('id',$id);
        return $this->db->delete('user');
    } 

Seguretat

Usuari:TNicolae/Skeleton#Autenticaci.C3.B3


Exemple REST-SERVER Fucional

= Configuració base de dades

Ens connectem a la base de dades:

$ mysql -h localhost -uroot -p

Creem una base de dades i una taula l'exemple:

CREATE database users;
USE users;
 CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `surname` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

Inserim unes dades de prova:

INSERT INTO `users`.`user` (`id`, `name`, `surname`) VALUES ('1', 'Luffy', 'Monkey D.');
INSERT INTO `users`.`user` (`id`, `name`, `surname`) VALUES ('2', 'Zorro', 'Roronoa');
INSERT INTO `users`.`user` (`id`, `name`, `surname`) VALUES ('3', 'Nami', );

Configurem el fitxer application/config/database.php amb l'informació necessària per a poder-se connectar.

$db['default']['hostname'] = 'localhost'; 
$db['default']['username'] = 'root';
$db['default']['password'] = 'password';
$config['mysql_masterpassword'] ='password';
$db['default']['database'] = 'users';

Controlador

Dins del la carpeta application/controllers/api/ creem un fitxer user_api.php amb el següent contingut:

<?php defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * Example
 *
 * This is an example of a few basic user interaction methods you could use
 * all done with a hardcoded array.
 *
 * @package		CodeIgniter
 * @subpackage	Rest Server
 * @category	Controller
 * @author		Phil Sturgeon
 * @link		http://philsturgeon.co.uk/code/
 * @editedby    Turcan Nicolae
*/

// This can be removed if you use __autoload() in config.php OR use Modular Extensions
require APPPATH.'/libraries/REST_Controller.php';

class User_api extends REST_Controller
{
	function __construct()
    {
        // Construct our parent class
        parent::__construct();
        
        $this->load->model("User_model");

        
        // Configure limits on our controller methods. Ensure
        // you have created the 'limits' table and enabled 'limits'
        // within application/config/rest.php
        $this->methods['user_get']['limit'] = 500; //500 requests per hour per user/key
        $this->methods['user_post']['limit'] = 100; //100 requests per hour per user/key
        $this->methods['user_delete']['limit'] = 50; //50 requests per hour per user/key
    }
    
    // Get one user by id.
    function user_get()
    {
        if(!$this->get('id'))
        {
        	$this->response(NULL, 400);
        }

        $user = $this->User_model->get_user( $this->get('id') );
    	
        if($user)
        {
            $this->response($user, 200); // 200 being the HTTP response code
        }

        else
        {
            $this->response(array('error' => 'User could not be found'), 404);
        }
    }
    
    // Create a new user.
    function new_user_post()
    {        
        if($this->post("name") || $this->post("surname")){
            
            $result = $this->User_model->add_user($this->post("name"),$this->post("surname"));
            
            if($result === FALSE)
            {
                $this->response(array('status' => 'failed'));
            }

            else
            {
                $this->response(array('status' => 'success'));
            }
        }
    }
    
    // Update an user by id.
    public function user_put()
    {
        $id = $this->put('id');
        $data = array(
            'name'=>$this->put('name'),
            'surname'=>$this->put('surname')
        );
            
        $result = $this->User_model->update_user($id,$data);
         
        if($result === false){
            $this->response(array("status" => "failed"));
        }else{
            $this->response(array("status" => "success"));
        }
    } 
    
    // Delete an user by id.
    function user_delete()
    {
    	$result = $this->User_model->delete_user( $this->get('id') );
        
        if($result === false){
            $this->response(array("status" => "failed"));
        }else{
            $this->response(array("status" => "success"));
        }    
    }
    
    // Gets all users from the table
    function users_get()
    {
        $users = $this->User_model->get_all();
        
        if($users)
        {
            $this->response($users, 200); // 200 being the HTTP response code
        }

        else
        {
            $this->response(array('error' => 'Couldn\'t find any users!'), 404);
        }
    }
    
        
    public function send_post()
	{
		var_dump($this->request->body);
	}


	public function send_put()
	{
		var_dump($this->put('foo'));
	}
}

Model

Dins d'application/models/ creem el fitxer user_model.php amb el següent contingut:

<?php

class User_model extends CI_Model {

    function __construct() {
        parent::__construct();
        $this->load->database();
        $this->load->dbutil(); 
    }
    
    public function get_user($id)
    {
        $this->db->select('id, name, surname');
        $this->db->where('id',$id);
        $query = $this->db->get('user');

        if($query->num_rows() == 1){
            return $query->result();
        }
    }

    function get_all() 
    {
        $query = $this->db->get('user');
                
        if($query->num_rows() > 0)
        {
            return $query->result();
        }
    }
    
    function add_user($name, $surname) 
    {
        $data = array(
            "name" => $name,
            "surname" => $surname
        );
        
        return $this->db->insert("user", $data);
    }
    
    function update_user($id, $data){

        $this->db->where('id',$id);
        return $this->db->update('user',$data);
    }
    
    public function delete_user($id)
    {
        $this->db->where('id',$id);
        return $this->db->delete('user');
    } 
}
?>

Comprovacions del funcionament de l'API

Comprovacions amb POSTMAN

GET

Mostrem totes les dades de la taula user:

Usuaris de la taula User.

Mostrem una fila en concret de la taula user:

Mostrem només un usuari de la taula User.
POST

Creació de dades a la taula user:

Introdució d'usuaris a la taula User.

Podem veure el nou usuari introduït:

Comprovem la introducció del nou usuari.
PUT

Modificació d'un usuari ja existent:

Modificació d'un usuari.

Podem veure l'usuari modificat:

Comprovem la introducció del nou usuari.
DELETE

Eliminem un recurs de la taula user:

Eliminació d'un usuari.

Podem veure que l'usuari efectivament ja no existeix:

Comprovem que l'usuari s'ha esborrat satisfactòriament.


REST client library

És una biblioteca de CodeIgniter que fa que sigui més fàcil de fer ús dels serveis REST/API de com Twitter, Facebook i Flickr, ja siguin públics o privats accedit amb una autenticació bàsica HTTP/Digest.

Instal·lació

Fem un fork del projecte:

Fem un clonatge del fork fet anteriorment en local:

$ cd /usr/share
$ sudo git clone https://github.com/nicolaeturcan/codeigniter-restclient

Per a no tindre que configurar l'apache un altre cop, simplement copiarem les llibreries necessàries al servidor:

$ cd /usr/share/codeigniter-restclient/libraries
$ sudo cp Rest.php /usr/share/codeigniter-restserver/application/libraries/

Baixem la llibreria Curl:

$ sudo wget http://getsparks.org/static/archives/curl/curl-1.3.0.zip
$ unzip curl-1.3.0.zip curl

Copiem el fitxer Curl.php dins dels llibreries del Rest Server:

$ sudo cp libraries/Curl.php /usr/share/codeigniter-restclient/libraries/

Dins dels Controladors creem un per a fer les proves rest-client:

$ touch user_rest_client.php

Configurem en el constructor afegint les llibreries necessàries i dades d'autentificació: <?php defined('BASEPATH') OR exit('No direct script access allowed');

class User_Rest_Client extends CI_Controller{

	function __construct()
        {
             parent::__construct();
             $this->load->library('Rest');
             $this->load->library('Curl');
             $this->load->config('rest_client');
             $config = array('server' =>'http://localhost/restserver/index.php/',
                        //'api_key' => $this->config->item('api_key'),
                        //'api_name' => 'X-API-KEY'
                       );
        
            $this->rest->initialize($config);
    }


GET

Mètode per a mostrar un usuari si s'indica el seu id i sinó, llavors tots els usuaris existents:

function get_user($id = null){   
       $get_user_url = 'api/user_api/user/id/';

     	if($id != null){
     		$user = $this->rest->get($get_user_url, array('id' => $id));
        }
         
        echo json_encode($user);
    }

Per a accedir anirem a les següents URLs: http://localhost/restserver/index.php/api/user_rest_client/get_user/ http://localhost/restserver/index.php/api/user_rest_client/get_user/1

PUT

Mètode per a introduir un usuari si s'indica el seu id i sinó, llavors tots els usuaris existents:

    function add_user(){

        $user = array(            
            'name'=>'add',
            'surname'=>'succesfully'
            );

        $user_add_url = 'api/user_api/user';
        $response = $this->rest->put($user_add_url,$user);
        
        echo json_encode($response); 
    }

Per a accedir anirem a les següents URLs:

http://localhost/restserver/index.php/api/user_rest_client/add_user/

POST

Mètode per a modificar un usuari existent:

    function update_user(){
        $update_user_url = 'api/user_api/user';

        $id = 10;
        $column = 'name';
        $name = 'Update_name';
        $data = array(
            'id' => $id,
            $column => $name);
             
        $response = $this->rest->post($update_user_url,$data);
        echo json_encode($response);
    }

Per a accedir anirem a les següents URLs:

http://localhost/restserver/index.php/api/user_rest_client/update_user/

DELETE

Mètode per a esborrar un usuari existent:

    function delete_user($id = null){
        $delete_user_url = 'api/user_api/user';

        if($id){
            $response = $this->rest->delete($delete_user_url.'/id/'.$id);
            echo json_encode($response);
            
        }else{
           $message = $this->rest->delete($delete_user_url.'/id/');
           echo json_encode($message);
        }
    }

Per a accedir anirem a les següents URLs:

http://localhost/restserver/index.php/api/user_rest_client/delete_user/

Exemple Complet

<?php defined('BASEPATH') OR exit('No direct script access allowed');


class User_Rest_Client extends CI_Controller{


	function __construct()
    {
       parent::__construct();
        $this->load->library('Rest');
        $this->load->library('Curl');
        $this->load->config('rest_client');
        $config = array('server' =>'http://localhost/restserver/index.php/',
                        'api_key' => $this->config->item('api_key'),
                        'api_name' => 'X-API-KEY'
                       );
        
        $this->rest->initialize($config);
    }

    public function index(){
        $user = $this->rest->get('api/user_api/users/');
        echo json_encode($user);
        
    }
    
     function get_user($id = null){   
       $get_user_url = 'api/user_api/user/id/';

     	if($id != null){
     		$user = $this->rest->get($get_user_url, array('id' => $id));
        }
         
        echo json_encode($user);
    }

    function update_user(){
        $update_user_url = 'api/user_api/user';

        $id = 200;
        $column = 'name';
        $name = 'Update_name';
        $data = array(
            'id'=>$id,
            $column=>$name);
             
        $response = $this->rest->post($update_user_url,$data);
        echo json_encode($response);
    }

    function delete_user($id = null){
        $delete_user_url = 'api/user_api/user';

        if($id){
            $response = $this->rest->delete($delete_user_url.'/id/'.$id);
            echo json_encode($response);
            
        }else{
           $message = $this->rest->delete($delete_user_url.'/id/');
           echo json_encode($message);
        }
    }

    function add_user(){

        $user = array(            
            'name'=>'add',
            'surname'=>'succesfully'
            );

        $user_add_url = 'api/user_api/user';
        $response = $this->rest->put($user_add_url,$user);
        
        echo json_encode($response); 
    }
 }
 ?>

Vegeu també

Enllaços externs