ok

img

RESTful

WEB SERVICES

L'essere umano immette e ricava dati nel software attraverso interfacce fisiche (tastiera, mouse, ecc) e digitali (GUI: bottoni, checkboxes, campi di testo, ecc).
Nel campo informatico, tuttavia, vi è l'esigenza di consentire, non solo agli esseri umani, ma anche alle stesse applicazioni di interagire reciprocamente.
I web services sono gli strumenti (sempre programmi) che organizzano e disciplinano la comunicazione fra applicazioni collegate alla rete (ecco perchè web-service).
L'interazione tra applicazioni avviene tramite il consueto schema client-server.
Il server gestisce le risorse (informazioni) e le condivide con l'esterno mediante il web-service consentendo la comunicazione fra applicazioni "eterogenee" (ad es con piattaforme differenti), purchè collegate in rete.
Il client effettua una richiesta di una risorsa al web-service che (valutata l'ammissibilità della richiesta) restituisce la risorsa al client.

RESTFUL

E' possibile realizzare web-services mediante uno stile architetturale che è chiamato RESTful.
Client e web-service possono essere scritti anche con linguaggi differenti (ad es Java e PHP) ma sono comunque capaci di comunicare tra loro attraverso un linguaggio comune: le richieste HTTP.
Il client, attraverso i 4 principali metodi di richiesta HTTP (POST, GET, PUT, DELETE) dirette al web-service, è in grado di realizzare effetti CRUD sulle risorse (ad es un database).

POST -> CREATE
GET -> READ
PUT -> UPDATE
DELETE -> DELETE

Le richieste HTTP si compongono di:
un metodo (POST, GET, PUT, DELETE) per realizzare gli effetti CRUD,
un indirizzo (URI) che rappresenta la risorsa su cui operare,
un body ad es con i valori da immettere nel database.

esempio di applicazione RESTful:
database: test;
tabella: persone;
vogliamo inserire un nuovo record usando il metodo HTTP POST.
La request ha questa struttura:
la request HTTP può essere realizzata usando le librerie cURL (con PHP o altro linguaggio)
es codice PHP:
//
// client:
//
$method = "POST";
$url = "http://www.../.../test/persone/";
$body = "nome=Anna&cognome=Sala&luogo=Verona&data=10/10/82";
$n = substr_count($body, "="); // il numero delle variabili presenti nella stringa body è misurato attraverso il numero degli "="
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, $n);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
print($result);
Il web-service:
riceve la richiesta;
valuta la correttezza della richiesta;
realizza l'operazione CRUD inserendo una nuova riga.
Ciò che non vediamo, naturalmente, è il processo di trasformazione della richiesta http in un comando per il database:
INSERT INTO persone (nome, cognome, luogo, data) VALUES ('Anna', 'Sala', 'Verona', '10/10/82');
Nei metodi GET e DELETE è inutile passare il body (non è un errore ma non serve a niente), sono sufficienti il metodo e l'indirizzo (nel caso del GET nella forma ?var1=10&var2=12

Progetto

Viene creato il database e lo chiamiamo webservice. Viene creata la cartella webservice, al cui interno sono poste le cartelle client e persone.
Nella cartella client troviamo i file index.php e request.php.
Nella cartella persone troviamo i file index.php e db.php.
//
// client/request.php
//
$method = "GET";
$url = "localhost/webservice/persone/?id=1";
$body = "";


//
// client/index.php
//
class Client
{
    private $url;
    private $method;
    private $body;
    private $num;

    public function __construct($method, $url, $body)
    {
        $this->url = $url;
        $this->method = $method;
        $this->body = $body;
        $this->num = substr_count($this->body, "=");
    }

    private function createTable()
    {
        // conn
        $conn = new mysqli("localhost", "root", "", "webservice");
        if($conn->connect_error){die("Connection failed: " . $conn->connect_error);}
        // table
        $sql = "CREATE TABLE persone (id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,numero varchar(64),cognome varchar(64),nome varchar(64),nato_il varchar(10),atto varchar(64),a varchar(64),cittadinanza varchar(64),residenza varchar(64),via varchar(64),stato_civile varchar(64),professione varchar(64),statura varchar(3),capelli varchar(64),occhi varchar(64),segni_particolari varchar(64),data varchar(10),scadenza varchar(10));";
        if($conn->query($sql) === TRUE){echo "OK";}else{echo "Error: " . $conn->error;}
        // insert
        $sql = "INSERT INTO persone (numero,cognome,nome,nato_il,atto,a,cittadinanza,residenza,via,stato_civile,professione,statura,capelli,occhi,segni_particolari,data,scadenza) VALUES ('QW1578923','Esposito','Vincenzo','17/06/1996','1/56/A1996','Salerno','Italiana','Salerno','via Dalmazia 15','stato libero','omesso','175','castani','castani','','22/04/2016','22/02/2027')";
        if($conn->query($sql) === TRUE){echo "OK";}else{echo "Error: " . $conn->error;}
        $sql = "INSERT INTO persone (numero,cognome,nome,nato_il,atto,a,cittadinanza,residenza,via,stato_civile,professione,statura,capelli,occhi,segni_particolari,data,scadenza) VALUES ('AE1578651','Rossi','Antonio','27/03/1971','105/5/s71','Roma','italiana','Roma','via xx settembre 19','sposato','impiegato','169','neri','scuri','','02/08/2015','02/06/2026')";
        if($conn->query($sql) === TRUE){echo "OK";}else{echo "Error: " . $conn->error;}
        $sql = "INSERT INTO persone (numero,cognome,nome,nato_il,atto,a,cittadinanza,residenza,via,stato_civile,professione,statura,capelli,occhi,segni_particolari,data,scadenza) VALUES ('WE1231244','Sala','Mario','23/12/1980','12/3332','Verona','italiana','Verona','via Veneto 15','stato libero','omesso','179','castani','marroni','','13/10/2000','13/10/2010')";
        if($conn->query($sql) === TRUE){echo "OK";}else{echo "Error: " . $conn->error;}
        //
        $conn->close();
    }

    private function deleteTable()
    {
        $conn = new mysqli("localhost", "root", "", "webservice");
        if($conn->connect_error){die("Connection failed: " . $conn->connect_error);}
        $sql = "DROP TABLE persone;";
        if($conn->query($sql) === TRUE){echo "OK";}else{echo "Error: " . $conn->error;}
        $conn->close();
    }

    public function reset()
    {
        $this->deleteTable();
        $this->createTable();
    }

    private function run()
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->url);
        curl_setopt($ch, CURLOPT_POST, $this->num);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->method);
        if($this->body)
        {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $this->body);
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $result = curl_exec($ch);
        curl_close($ch);
        $json = json_decode($result, true);
        print($result);
    }
}

include ("request.php");
$q = new Client($method, $url, $body);
$q->run();
Il file request.php contiene le 3 variabili della request: il metodo, l'indirizzo e il corpo.

Il metodo createTable del file client.php costruisce la tabella persone e vi inserisce 3 record.
Il metodo deleteTable distrugge la tabella, ma è un metodo privato e non può essere usato al di fuori della classe.
Il metodo reset distrugge e ricostruisce la tabella con i 3 record.
Il metodo run esegue la request, i cui dati ($method, $url, $body) sono estratti dal file request.php.
//
// persone/db.php
//
class Database
{
    public $conn;
    private $servername;
    private $username;
    private $password;
    private $db;
    private $table;
    public $column;
    function __construct($servername, $username, $password)
    { 
        $this->servername = $servername;
        $this->username = $username;
        $this->password = $password;
    }

    public function connection()
    {
        $this->conn = new mysqli($this->servername, $this->username, $this->password, $this->db);
        if($this->conn->connect_error) 
        {
            die("Connection failed: " . $this->conn->connect_error);
        }
    }

    public function setDatabase($db)
    {
        $this->db = $db;
        $this->conn->select_db($db);
    }

    public function setTable($table)
    {
        $this->table = $table;
        $this->column = $this->getColumnNames();
    }

    public function query($sql)
    {
        $result = $this->conn->query($sql);
        if(!$result)
        {
            die("Connection failed: " . $this->conn->error);
        }
        return $result;
    }

    public function select($where)
    {
        if(!$where){$where = "1";}
        $sql = "SELECT * FROM $this->table WHERE $where;";
        return $this->query($sql)->fetch_all(MYSQLI_ASSOC);
    }

    public function insert($columns, $values)
    {
        $sql = "INSERT INTO $this->table ($columns) VALUES ($values);";
        return $this->query($sql);
    }

    public function update($set, $where)
    {
        $sql = "UPDATE $this->table SET $set WHERE $where;";
        return $this->query($sql);
    }

    public function delete($where)
    {
        $sql = "DELETE FROM $this->table WHERE $where;";
        return $this->query($sql);
    }

    public function getColumns()
    {
        $sql = "SHOW COLUMNS FROM $this->table";
        $result = $this->query($sql);
        return $result->num_rows;
    }

    public function getColumnNames()
    {
        $res = $this->query("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '" . $this->db . "' AND TABLE_NAME = '" . $this->table . "';");
        while($row = $res->fetch_assoc())
        {
            $result[] = $row;
        }
        return array_column($result, 'COLUMN_NAME');
    }

    public function close()
    {
        $this->conn->close();
    }
}
Non c'è molto da dire. Il file db.php contiene i metodi per la connessione al database e per l'esecuzione delle operazioni CRUD.
Infine abbiamo il file con il web-service vero e proprio.
//
// persone/index.php
//
include("db.php");

class Server
{    
    private $method;
    private $uri;
    private $body;
    private $conn;
    private $db;
    private $table;
    private $path = array();
    
    public function __construct()
    {
        $this->method = $_SERVER['REQUEST_METHOD'];
        $this->uri = $_SERVER['REQUEST_URI'];
        $this->body = file_get_contents('php://input');
        $this->dir();
        $this->fun();
    }

    private function dir()
    {
        $this->path = array_values(array_filter(explode("/", parse_url($this->uri, PHP_URL_PATH))));
        $this->db = $this->path[0]; // database
        $this->table = $this->path[1]; // table
        $this->query = parse_url($this->uri, PHP_URL_QUERY); // query
    }

    private function connection()
    {
        $this->conn = new Database("localhost", "root", "", $this->db);
        $this->conn->connection();
        $this->conn->setDatabase($this->db);
        $this->conn->setTable($this->table);
    }

    private function close()
    {
        $this->conn->close();
    }

    private function fun()
    {
        $this->connection();
        $data = "";
        if($this->method === "GET")
        {
            $where = "1";
            if(strlen($this->query) > 0)
            {
                $where = str_replace("&", " AND ", $this->query); // trasforma il path in where
            }
            $data = $this->conn->select($where);
        }
        else if($this->method === "POST")
        {
            if(strlen($this->query) == 0) // empty path
            {
                $columns = "";
                $values = "";
                foreach($_POST as $key => $val)
                {
                    $columns .= $key . ",";
                    $values .= "'" . $val . "',";
                }
                $columns = substr($columns, 0, -1);
                $values = substr($values, 0, -1);
                $this->conn->insert($columns, $values);
                $data = "{\"result\": \"ok\"}";
            }
            else // error
            {
                $data = "{\"result\": \"error\"}";
            }
        }
        else if($this->method === "PUT")
        {
            if(count($this->path) > 2)
            {
                $where = $this->path[2] . "='" . $this->path[3] . "'";
                $set = str_replace("&", ",", $this->body);
                $this->conn->update($set, $where);
                $data = "{\"result\": \"ok\"}";
            }
            else
            {
                $data = "{\"result\": \"error\"}";
            }
        }
        else if($this->method === "DELETE")
        {
            if(count($this->path) > 2)
            {
                $where = $this->path[2] . "='" . $this->path[3] . "'";
                $this->conn->delete($where);
                $data = "{\"result\": \"ok\"}";
            }
            else
            {
                $data = "{\"result\": \"error\"}";
            }
        }
        else
        {
            $data = "{\"result\": \"error\"}";
        }
        $this->close();
        $this->ok($data);
    }

    private function ok($data)
    {
        http_response_code(200);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($data); // return json
    }

    private function error()
    {
        http_response_code(404);
        include("../404.php");
        die();
    }
}

new Server();
Il costruttore estrae il metodo, l'indirizzo e il corpo della richiesta. Quindi scompone ulteriormente l'url per ricavarne i vari segmenti (nome del database, nome della tabella e la query).
Il metodo fun passa in rassegna i vari metodi, effettua i controlli di validità, esegue l'operazione sul db e formula la risposta da ritornare al client.

Sia chiaro... in questo esempio non è stato affrontato il problema della sicurezza (che esula dalla trattazione). Solitamente vengono introdotte delle forme di protezione in modo che solo le richieste HTTP legittime (di provata provenienza e rispettose di certe caratteristiche) possano avere effetto e modificare il database. Insomma l'articolo serve a mostrare per somme linee il meccanismo e lo scopo di un web-service di tipo Restful, senza affrontare gli aspetti più problematici.

Biografia

Mi chiamo Cosimo Saccone e sono un programmatore napoletano di 44 anni con oltre 35 anni di esperienza nella programmazione (BASIC, Assembly). Realizzo progetti e programmi utilizzando i principali e più diffusi linguaggi (C, C++, PHP, Javascript, HTML) e software per la grafica (Photoshop, Illustrator, 3dsMax). Anche se la grafica rappresenta il mio principale settore di interesse, non disdegno il lavoro di back-end e di organizzazione dati e sono attento agli aspetti di efficienza e di risparmio delle risorse tipica della programmazione di basso livello (specie nel settore della grafica 3d). Realizzo siti internet, applicativi desktop e servizi di vario tipo. Ho una buona conoscenza della libreria OpenGL per lo sviluppo di applicazioni 3d interattive in C/C++. Cerco di adottare uno stile di programmazione fortemente ordinato e modulare. Possiedo, inoltre, una buona capacità di elaborazione della documentazione. Ho vari hobbies tra cui la pittura, la ginnastica e le lunghe passeggiate in solitudine.

facebook instagram youtube
HTML5 Template create by Cosimo Saccone 2022

Al fine di migliorare l’esperienza di navigazione sul nostro sito noi di cosimosaccone.com e i nostri partner selezionati elaboriamo i dati personali, compreso l’indirizzo IP e le pagine visitate, in relazione alla tua navigazione nei contenuti del sito, per i seguenti scopi:

Accesso alle informazioni
Dati precisi di geolocalizzazione
Misurazione del pubblico
Pubblicità e contenuti personalizzati
Ti ricordiamo che il termine cookie si riferisce a una porzione di dati inviati al tuo browser da un web server. Il cookie viene memorizzato sul tuo dispositivo e riconosciuto dal sito web che lo ha inviato in ogni navigazione successiva. Se vuoi saperne di più e compiere una scelta diversa, come il rifiuto del trattamento dei tuoi dati personali, clicca qui sulla nostra privacy policy. Potrai sempre modificare le tue scelte e impostare i singolo cookies selezionando il bottone qui sotto.
OK