Qualche tempo fa, per la demo di un progetto, ho dovuto scrivere qualche riga di codice per implementare un webservice che ricevesse chiamate in modalità RESTful e restituisse un JSON come risposta. Premesso che probabilmente ci sarebbero stati almeno altri 10 linguaggi per fare la stessa cosa, ho deciso di usare PHP. Si, perché PHP sarà poco elegante, non permetterà prestazioni da paura ma è semplice da usare e in poche righe di codice mi ha permesso di mettere in piedi un servizio minimo che faceva proprio quello che volevo.

Io, in pratica, volevo qualcosa che se invocato così:

1
http://webservices.corradoignoti.it/nomeservizio/showAllArticles

mi restituisse un JSON con le informazioni volute. In questo caso tutti gli articoli presenti per quel servizio.

Primo passo: configurare Apache. Senza nessuna configurazione cercherebbe nella directory “nomeservizio” del virtual host webservices.corradoignoti.it il file showAllArticles. Utilizzando mod-rewrite è possibile fare in modo che tutte le chiamate del tipo descritto, possano essere redirette ad uno script, nel mio caso getList.php. Le direttive di mod_rewrite possono essere messe in .htaccess, a patto che nella configurazione del virtualhost sia presente la direttiva

1
Options AllowOverride
1
2
3
4
5
6
7
8
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /nomeservizio/
RewriteRule ^getList\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /nomeservizio/getList.php [L]
</IfModule>

Utilizzando questa configurazione posso fare in modo che Apache esegua lo script getList.php senza ignorando tutto quello che viene scritto dopo “/nomeservizio/. Questo mi permette di fare chiamate del tipo:

1
2
http://webservices.corradoignoti.it/nomeservizio/showArticleByCat/4
http://webservices.corradoignoti.it/nomeservizio/showArticleByID/253

Diamo un’occhiata, invece, allo script vero e proprio. La funzione principale è questa:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function parseRequestURI($uri) {
        list ($baseaddress, $directory, $command, $param) = explode ('/', $uri);
     
        switch ($command) {
                case 'showArticleByID':
                        echo doShowArticolebyID ($param);
                        break;
                       
                case 'showAllArticles':
                        echo doShowAllArticles();
                        break;
                       
                case 'showArticleByCat':
                        echo doShowArticleByCat ($param);
                        break;
                       
                default:
                        echo ("Error in command");
        }
}

L’URL viene presa e scomposta, le parti che ci interessano sono il comando (showArticleByID, showArticleByCat, ecc, ecc) e gli eventuali parametri che ad esso vengono passati. Il blocco switch…case invoca la funzione che serve per il comando richiesto.
Un esempio su tutti, in caso di showArticleByCat la funzione invocata è la seguente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function doShowArticleByCat($type_id) {
        $result = mysql_query ("select * from articles where type_id=$type_id order by pubdate");
       
        $retVal = array();
       
        while ($row = mysql_fetch_assoc($result)) {
                $json_object = array();
                $json_object['id'] = $row['id'];
                $json_object['type_id'] = $row['type_id'];
                $json_object['pubdate'] = $row['pubdate'];
                $json_object['title'] = $row['title'];
                $json_object['abstract'] = $row['abstract'];
                $json_object['body'] = $row['body'];

                array_push($retVal, $json_object);
        }        
        return json_encode ($retVal);
}

In pratica, per ogni articolo estratto, viene generato un oggetto JSON, alla fine tutti gli oggetti vengo racchiusi in un array utilizzato come valore di ritorno della funzione. Alla fine un

1
echo doShowArticleByCat ($param);

permette di restituire il formato testuale dell’array di oggetti JSON appena estratti.

12 thoughts on “Servizi REST e PHP

  1. Complimenti, bell’articolo!
    Non sono molto pratico con i Web Services, e dovrei realizzare qualcosa di simile.
    L’unica cosa in più che dovrei fare consiste nella possibilità di aggiungere (POST) gli articoli.
    Come dovrei fare per autenticare il client?
    Mi puoi aiutare? grazie.

  2. Grazie per i complimenti. Per l’autenticazione molto dipende dal progetto.
    Se usi https la cara e buona basic authentication protrebbe essere assolutamente una buona soluzione. Grazie all’uso dell’SSL potresti autenticare utilizzando i certificati.
    Il sistema di autenticazione che userai dipende anche dalla complessità dell’applicativo e dai dati che devi proteggere. Se vuoi utilizzare un metodo standard ed testato di autenticazione puoi utilizzare OAuth2 o OAuth. Esistono diverse implementazioni pronte all’uso, scritte in PHP.

  3. Ciao,
    articolo utilissimo e molto chiaro!
    Ti faccio una domanda probabilmente molto “stupida”:
    ma perchè anche il mod_rewrite?
    L’hai usato solo per facilitare l’utilizzo da parte dell’utente
    o ci sono altri motivi?

  4. Ciao Corrado, grazie per l’articolo, l’ho trovato molto formativo dal momento che anche io dovrò presto implementare un webservice RESTful molto semplice.

    Tuttavia, vorrei fare notare che il servizio che descrivi NON è RESTful.

    Il concetto base delle applicazioni “REST” è infatti esporre un set di risorse attraverso un’interfaccia uniforme (in questo caso data dai metodi HTTP quali GET, POST, etc…).
    I client del tuo servizio, oltre che alla risorsa da manipolare (ossia la lista di tutti i tuoi articoli) stanno convenendo attraverso l’URL

    http://webservices.corradoignoti.it/nomeservizio/showAllArticles

    anche l’operazione da compiere (“show”): questo è uno stile RPC, non REST!
    Diventa difficile anche mantenere il codice pHP che lo implementa (es: la switch/case nel PHP…che accade se aggiungi un nuovo metodo nell’URL? devi cambiare il codice per includerlo!)
    Ma nessun problema, la soluzione pienamente RESTful è semplice:

    URL: /nomeservizio/articles
    METHOD: GET

    cosi il tuo script PHP si dovrebbe limitare a gestire le richieste GET su una particolare risorsa (es: tutti gli articoli, un singolo articolo, etc..)
    Insomma: è meglio esporre attraverso gli URL le risorse del tuo webservice e lasciare che i client le manipolino usando i metodi dell’interfaccia standard HTTP.

    Ciao!

  5. Non sono completamente d’accordo ma pubblico il commento che, solo in parte condivido.
    Il caso era solo un caso di studio e come tutti i casi analoghi assolutamente perfettibile. PHP non è senz’altro la tecnologia per la gestione di questo tipo di servizi. Le considerazioni da fare sarebbero veramente molte.

  6. Ciao e complimenti per l’articolo.
    Avrei un quesito da porti…
    Nell’ottica di realizzare un webservice che accetti delle requests dal client in formato json,(es. richieste di dati memorizzati in un database) le elabori e successivamente dia delle response sempre in formato json,ti chiedo: un webservice di tipo RESTFUL si può prestare a tale scopo?
    In alternativa quale protocollo potrei usare? Puoi darmi dei suggerimenti?
    Grazie infinite!

  7. Un appunto: come ti han fatto notare, l’implementazione proposta e’ RPC e non RESTful – tale spirito ammette solo sostantivi nelle route, e verbi [GET / POST / PUT / DELETE] come metodo HTTP. Circa l’inadeguatezza di PHP si puo’ esser d’accordo nella misura in cui PHP e’ inadeguato a tutto 😛 [e lo dico usandolo ancora come swiss army knife], ma usando soluzioni moderne come Symfony, Laravel o klein si possono produrre webservice onesti.

    Anyway keep it up!

  8. Molto dipende da cosa deve fare esattamente il tuo progetto. Direi che comunque è una delle strade che puoi seguire.

  9. Ok… è ufficiale che devo uscire con un post un po’ più chiaro sui servizi REST/RPC…. Mo come trovo un po’ di tempo lo scrivo. 🙂

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *