Diferència entre revisions de la pàgina «Web Services»

De Cacauet Wiki
Salta a la navegació Salta a la cerca
 
(Hi ha 33 revisions intermèdies del mateix usuari que no es mostren)
Línia 1: Línia 1:
 +
Potser esteu buscant un altre article:
 +
[[Web Services: exemple Escacs]]
 +
  
 
== Introducció ==
 
== Introducció ==
 +
Els [https://ca.wikipedia.org/wiki/Servei_web serveis web], simplificant, és una manera de realitzar '''crides a funcions remotes''' mitjançant el protocol HTTP. És la forma més còmoda per al programador web d'anar a buscar dades a un servidor, ja sigui des d'un altre servidor o des d'un client.
 +
 +
Al tractar-se, com hem dit, de "crides a funcions remotes", solem anomenar-los [https://ca.wikipedia.org/wiki/Interf%C3%ADcie_de_programaci%C3%B3_d'aplicacions API : Application Programming Interface].
 +
 +
Des de la línia de comandes Unix podem fer una d'aquestes crides a través de la comanda '''curl''' (call URL). Per exemple, per obtenir el HTML d'una web simple, podríem fer:
 +
$ curl http://cacauet.org
 +
 +
La comanda curl permet fer crides més complexes amb mètodes POST, GET, PUT, DELETE, etc. i adjuntar dades i ''headers''. Consulta les possibilitats amb:
 +
$ man curl
 +
 +
Però no només podem obtenir HTML. El servidor ens podria retornar una dada concreta, per exemple, el nº de vots obtingut per un partit en unes eleccions o bé la quantitat de "likes" d'un post.
 +
 +
Normalment, per tal d'ordenar correctament dades més complexes, s'acaben retornant les dades en un d'aquests dos formats ([https://www.w3schools.com/js/js_json_xml.asp veure JSON vs XML]).
 +
* XML
 +
* JSON
 +
  
 +
=== Caracteristiques ===
 
Un '''servei web''' o '''web service''' ve a ser un servei més del sistema informàtic (o sigui, un sistema d'intercanvi d'informació), però amb algunes peculiaritats:
 
Un '''servei web''' o '''web service''' ve a ser un servei més del sistema informàtic (o sigui, un sistema d'intercanvi d'informació), però amb algunes peculiaritats:
  
Línia 9: Línia 29:
 
* Sol aplicar-se a sistemes navegables (enllaços/hipervincles).
 
* Sol aplicar-se a sistemes navegables (enllaços/hipervincles).
 
* Sol estar al port 80
 
* Sol estar al port 80
* Sol utiltizar una arquitectura [http://en.wikipedia.org/wiki/Representational_state_transfer REST o REpresentational State Transfer].
+
* Sol utilitzar una arquitectura [http://en.wikipedia.org/wiki/Representational_state_transfer REST o REpresentational State Transfer].
 
** Arquitectura client-servidor
 
** Arquitectura client-servidor
 
** Sense estat
 
** Sense estat
Línia 18: Línia 38:
  
 
Avantatges:
 
Avantatges:
* Estandarització
+
* Estandarització: sol utilitzar-se '''JSON (JavaScript Object Notation)''' com a format d'intercanvi de dades. És molt còmode i més comprimit que en altres formats com XML.
* ...
+
* Convé utilitzar les llibreries estàndard de JSON per no haver de reimplementar el ''parsing'' de les dades a enviar i rebre.
 +
 
 +
Mètodes REST:
 +
* GET: no s'envien dades (tot i que sí es poden posar en la URL)
 +
* POST: s'envien dades adjuntes
 +
* PUT: sol servir per guardar un element a la BBDD
 +
* DELETE: sol servir per eliminar un element de la BBDD
 +
* custom: es poden definir per l'usuari
 +
 
 +
<br>
 +
 
 +
== Exemple d'accés a un web service extern (Musicbrainz) ==
 +
Normalment accedit amb la comanda ''curl'' des de la pròpia ''shell''. Primer cal instal·lar-la:
 +
$ sudo apt install curl
 +
 
 +
Exploreu els següents recursos:
 +
* API (respon en XML): https://musicbrainz.org/doc/Development/XML_Web_Service/Version_2
 +
* Crides en JSON: https://musicbrainz.org/doc/Development/JSON_Web_Service
 +
* Per obtenir les dades en JSON cal que adjunteu el //header// de curl "Accept:application/json"
 +
* Per fer //pretty print// d'un JSON va molt bé "empipar-lo" a aquesta comanda:<code>$ curl ... | python3 -m json.tool</code>
 +
 
 +
Exemple:
 +
<syntaxhighlight lang="bash">
 +
$ curl -H 'Accept:application/json' http://musicbrainz.org/ws/2/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da?inc=aliases | python3 -m json.tool
 +
</syntaxhighlight>
 +
 
 +
=== Crides cURL en PHP ===
 +
En el context de PHP, posem que volem obtenir dades per al nostre servidor. Podem utilitzar la llibreria curl, pel que caldrà que instal·leu el paquet php-curl:
 +
$ sudo apt install php-curl
 +
 
 +
El mateix exemple anterior el cridaríem des de PHP d'aquesta manera:
 +
 
 +
<syntaxhighlight lang="php">
 +
// inicialitzem la crida cURL
 +
$url = "http://musicbrainz.org/ws/2/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da?inc=aliases";
 +
$c = curl_init( $url );
 +
 
 +
// Ajustem headers perquè ens retorni la info en format JSON
 +
// tb afegim User-Agent (identificador de client, si no Musicbrainz no funciona)
 +
curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type:application/json','Accept:application/json','User-Agent:Laravel/5.7'));
 +
// ajustos perquè ens retorni les dades sobre una variable
 +
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
 +
 
 +
// cridem per obtenir les dades
 +
$res = curl_exec($c);
 +
 
 +
// transformem les dades a un array associatiu de PHP
 +
$dades = json_decode($res,true);
 +
 
 +
// mostrem nom ($dades ja és un array associatiu PHP)
 +
echo $dades["aliases"][0]["name"];
 +
</syntaxhighlight>
 +
 
 +
== Especificació d'un Web Service ==
 +
Abans de posar-nos a treballar cal fer una correcta especificació. En aquest cas encara és més important ja que el codi estarà separat en 2 blocs: client (JS) i servidor (WS). Sol ser molt comú repartir en especialistes de cada part els dos blocs, i perquè el projecte funcioni cal que l'especificació sigui clara.
 +
 
 +
Tenim 2 exemples:
 +
* [[Serveis web amb Laravel]]
 +
* Un xat a continuació
 +
 
 +
Veureu que cada servei o ''endpoint'' necessita una especificació de les dades que necessita que li entrem (enviar) i quines ens donarà.
 +
 
 +
 
 +
=== Obtenir llista de canals i missatges ===
 +
 
 +
Method: GET
 +
URL: http://exemple.com/api/xat
 +
 
 +
{| border="1" class="wikitable" style="border-collapse:collapse;"
 +
!colspan="3"| GET &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; http://exemple.com/api/xat
 +
|-
 +
!colspan="3"| REQUEST (entrada ''plain text'')
 +
|-
 +
! Paràmetre
 +
! Valor
 +
! Comentari
 +
|-
 +
| canal
 +
| string
 +
| canal de xat del què volem rebre els missatges
 +
|-
 +
!colspan="3"| RESPONSE (JSON)
 +
|-
 +
! Paràmetre
 +
! Valor
 +
! Comentari
 +
|-
 +
| status
 +
| true/false
 +
| ens informa si tot ha anat bé
 +
|-
 +
| missatges
 +
| [ {"nick":"pere","missatge":"hola!"} , {...} , {...} ]
 +
| llista de missatges del canal
 +
|-
 +
| missatge
 +
| "canal inexistent"<br>"error intern"
 +
| detalls del status de la comanda
 +
|}
 +
 
 +
<br>
 +
 
 +
=== Enviar un missatge ===
 +
{| border="1" class="wikitable" style="border-collapse:collapse;"
 +
!colspan="3"| POST http://exemple.com/api/xat
 +
|-
 +
!colspan="3"| REQUEST (entrada JSON)
 +
|-
 +
! atribut
 +
! valor (exemple)
 +
! comentari
 +
|-
 +
| canal
 +
| string
 +
| canal de xat al què volem enviar el missatge
 +
|-
 +
| nick
 +
| string
 +
| nom del remitent
 +
|-
 +
| missatge
 +
| string
 +
| cos dels missatge a enviar
 +
|-
 +
!colspan="3"| RESPONSE (JSON)
 +
|-
 +
! atribut
 +
! valor
 +
! comentari
 +
|-
 +
| servei
 +
| "xat"
 +
| nom del servei que estem utilitzant (sempre serà "xat")
 +
|-
 +
| canal
 +
| string
 +
| canal de xat al què hem volgut enviar el missatge
 +
|-
 +
| status
 +
| true/false
 +
| si la petició s'ha resolt OK (gravar missatge)
 +
|-
 +
| missatge
 +
| text
 +
| missatge amb detalls de l'execució, per ex:<br> "ERROR: JSON incorrecte"<br>"missatge enregistrat correctament"
 +
|}
  
 
<br>
 
<br>
  
 
== Exemple en Python i CherryPy ==
 
== Exemple en Python i CherryPy ==
En Python solem utiltizar la llibreria json o bé simplejson.
+
En Python solem utiltizar la llibreria ''json'' o bé ''simplejson''.
 
+
Els mètodes més importants d'aquesta llibreria son:
Els mètodes més importants son:
 
 
* json.loads
 
* json.loads
 
* json.dumps
 
* json.dumps
  
En moltes ocasions, els propis "renderers" del nostre ''framework'' ens faran aquesta feina, tot i que estan utilitzant aquesta llibreria.
+
Fes un cop d'ull al [http://www.cherrypy.org framework CherryPy] per desenvolupament ràpid en Python, l'utilitzarem per fer algunes pràctiques.
 +
 
 +
En moltes ocasions, els propis "renderers" del nostre ''framework'' ens faran la feina de traduir i importar els objecte JSON, tot i que en el fons estaran utilitzant aquesta llibreria.
 +
 
 +
En aquest exemple utilitzem CherryPy i [[MongoDB]] com a base de dades. La [http://api.mongodb.org/python/current/tutorial.html llibreria Pymongo] ens serveix per accedir des de Python a MongoDB.
 +
 
 +
* Dintre de MongoDB utilitzarem la '''BBDD "xats"''' per emmagatzemar les converses.
 +
* Cada '''col·lecció''' dintre de "xats" serà un "canal" o "conversa".
  
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
# http://www.cherrypy.org/
 
# http://www.cherrypy.org/
# http://docs.cherrypy.org/en/latest/tutorial/REST.html
+
 
+
import cherrypy
import cherrypy, json
+
import json
 
+
import pymongo
 +
import datetime
 +
 
class Xat:
 
class Xat:
 
     exposed = True
 
     exposed = True
 
      
 
      
     # llistar missatges
+
    def __init__(self):
 +
        # inicialitzem la DB de canals
 +
        self.client = pymongo.MongoClient()
 +
        self.db = self.client.xats
 +
 +
     # llistar missatges o canals (parametre canal per get)
 +
    # tipus http://.../api/xat?canal=test
 
     def GET(self,*args,**kwargs):
 
     def GET(self,*args,**kwargs):
 +
        resposta = {}
 
         # processem parametres
 
         # processem parametres
 
         canal = kwargs.get("canal")
 
         canal = kwargs.get("canal")
         # preparem resposta
+
         if not canal:
         resposta = {}
+
            # mostrem llista de canals (col.leccions)
        resposta["status"] = False
+
            include_sys_cols = False
        resposta["missatge"] = "Servei no implementat (encara). Canal=" + str(canal)
+
            canals = self.db.collection_names(include_sys_cols)
 +
            resposta["canals"] = canals
 +
         else:
 +
            # responem llista de missatges del canal
 +
            missatges = self.db[canal].find({})
 +
            msgs = []
 +
            for msg in missatges:
 +
                msg["_id"] = str(msg["_id"])
 +
                msg["timestamp"] = str(msg["timestamp"])
 +
                msgs.append( msg )
 +
            if not msgs:
 +
                return json.dumps({"status":False,"missatge":"Canal inexistent"})
 +
            resposta["missatges"] = msgs
 +
        resposta["status"] = True
 +
        print resposta
 
         return json.dumps( resposta )
 
         return json.dumps( resposta )
       
+
 
     # enviar missatges
 
     # enviar missatges
 
     @cherrypy.tools.json_in()
 
     @cherrypy.tools.json_in()
 
     def POST(self,*args,**kwargs):
 
     def POST(self,*args,**kwargs):
        dades = cherrypy.request.json
 
        # "dades" conte elements JSON deserialitzats amb dicts, llista, etc.
 
 
         resposta = {}
 
         resposta = {}
         resposta["status"] = True
+
         try:
        resposta["missatge"] = "Missatge llegit correctament: " + str(dades)
+
            dades = cherrypy.request.json
 +
            # "dades" conte elements JSON deserialitzats
 +
            canal = dades["canal"]
 +
            doc = {}
 +
            doc["missatge"] = dades["missatge"]
 +
            doc["nick"] = dades["nick"]
 +
            doc["timestamp"] = datetime.datetime.now()
 +
            self.db[canal].save( doc )
 +
            resposta["status"] = True
 +
            resposta["missatge"] = "Missatge guardat correctament."
 +
        except:
 +
            resposta["status"] = False
 +
            resposta["missatge"] = "Error indefinit"
 
         return json.dumps( resposta )
 
         return json.dumps( resposta )
 
+
 
if __name__ == '__main__':
 
if __name__ == '__main__':
 
     cherrypy.tree.mount(
 
     cherrypy.tree.mount(
Línia 70: Línia 274:
 
     cherrypy.engine.start()
 
     cherrypy.engine.start()
 
     cherrypy.engine.block()
 
     cherrypy.engine.block()
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Utilitza la següent crida cURL per testejar:
 
$ curl http://localhost:8080/api/xat -d '"hola"' -H "Content-Type: application/json" -X POST
 
El -X POST és opcional ja que sempre que li posem un "-d" (dades) ja s'assumeix que és POST.
 
  
 
Fixeu-vos en què:
 
Fixeu-vos en què:
* Les dades d'entrada ens les parseja el framework: <pre>dades = cherrypy.request.json</pre>
+
* La classe Xat() implementa els mètodes REST ( '''GET''' i '''POST''' ). Podriem implementar altres ''mètodes restful'' (PUT, DELETE) creant els pertinents mètodes a la classe. El ''framework'' ja crida un o altre depenent del tipus de ''header'' que rebi en el ''request''.
 +
* Per configurar aquest comportament del ''framework'' hem hagut d'utilitzar el <code>MethodDispatcher()</code> dins del <code>main</code> (és diferent de la configuració ràpida habitual de CherryPy).
 +
* Les dades d'entrada ens les parseja el framework mitjançant el ''decorator'' <code>@cherrypy.tools.json_in()</code>. Així podem utiltizar les dades directament en JSON sense fer un <strike><code>json.loads</code></strike>: <pre>dades = cherrypy.request.json</pre>
 
* Les dades de sortida les hem de transformar a JSON <pre>return json.dumps</pre>
 
* Les dades de sortida les hem de transformar a JSON <pre>return json.dumps</pre>
*: El ''framwork'' s'encarrega de posar headers.
+
* El ''framwork'' s'encarrega de posar headers indicant que la resposta també és JSON.
  
 
<br>
 
<br>
  
== Exemple en Python i Pyramid ==
+
== Testejant el WS amb cURL ==
Teniu el codi íntegre a: https://github.com/lacetans/jsonxat_ws
+
La comanda curl (cal instal·lar-la) ens serveix per cridar a URLs i testejar els WS.
 +
 
 +
Per veure la llista de canals:
 +
$ curl http://localhost:8080/api/xat
 +
 
 +
Per veure els missatges d'un canal:
 +
$ curl http://localhost:8080/api/xat?canal=DAW_MASTERS
  
Fixa't en què en aquest cas:
+
I per enviar un missatge:
* El ''framework'' ja ens parseja l'entrada:<pre>dades = request.json_body</pre>
+
$ curl http://localhost:8080/api/xat \
*: O sigui, no cal fer un json.loads
+
  -d '{"canal":"DAW_MASTERS","missatge":"Benvinguts al xat DAW MASTERS!!","nick":"Enricus"}' \
* També el propi ''framwork'' ens parseja la sortida, fent un return d'un diccionari Python que es tradueix automàticament a JSON.
+
  -H "Content-Type: application/json" -X POST
*: El punt clau per realitzar això és el '''renderer''':<pre>@view_config(route_name='xat_set_missatge_ws', renderer='jsonp')</pre>
 
  
<syntaxhighlight lang="python">
+
Fiexu-vos en què:
@view_config(route_name='xat_set_missatge_ws', renderer='jsonp')
+
* Si només hi ha la URL s'assumeix que és una crida GET.
def xat_set_missatge_ws(request):
+
* Passem els paràmetres GET com habitualment (dins la pròpia URL amb un ? i separats per &).
    try:
+
* Per poder enviar dades per POST (-d) cal afegir els ''headers'' adequats per indicar que la informació és en format JSON, és a dir: <code>-H "Content-Type: application/json"</code>
        # ho intentem per GET (JSONP)
+
* El -X POST és opcional ja que sempre que li posem un "-d" (dades) ja s'assumeix que és POST.
        remitent = request.GET.get("remitent")
+
 
        missatge = request.GET.get("missatge")
+
<br>
        if remitent and missatge:
+
 
            if remitent.strip() and missatge.strip():
+
== Més sobre web services ==
                linia = remitent + "\t" + missatge
+
* [[Serveis web amb Laravel]]
        else:
+
* [[Serveis Web amb Opendata]]
            # ho intentm per POST (JSON)
+
 
            dades = request.json_body
+
Exemples:
            linia = dades["remitent"] + "\t" + dades["missatge"]  
+
* Utilitzant Python i Pyramid per un xat: https://github.com/lacetans/jsonxat_ws
        if "<" in linia or "\n" in linia:
+
* [[Web Services: exemple Escacs]]
            return {
+
 
                "servei":"xat",
+
<br>
                "canal":"principal",
+
 
                "status":False,
+
== CherryPy en producció ==
                "missatge":"ERROR: caracters ilegals"
+
Per posar una app feta amb CherryPy en producció, podem utilitzar qualsevol servidor [[WSGI|compatible amb WSGI]].
            }
+
 
        file = open(STORAGE_FILENAME,"a+")
+
Tens [https://infla.cat/wiki/Django:_desplegament exemples de deployment Python aquí] (Django).
        file.write(linia+"\n");
+
 
        file.close()
+
El punt clau és canviar el típic ''quickstart'' de la app CherryPy pel punt d'entrada ''application'' d'aquesta manera:
           
+
application = cherrypy.Application( CherryApp(), script_name=None, config=None)
    except ValueError:
 
        return {"servei":"xat",
 
"canal":"principal",
 
"status":False,
 
"missatge":"ERROR: JSON incorrecte"
 
        }
 
    except Exception as e:
 
        print type(e).__name__
 
        print e.args
 
        return {"servei":"xat",
 
"canal":"principal",
 
"status":False,
 
"missatge":"ERROR desonegut: " + type(e).__name__
 
        }
 
   
 
    return { "servei":"xat",
 
"canal":"principal",
 
"status":True,
 
"missatge":"missatge enregistrat correctament"
 
    }
 
  
</syntaxhighlight>
+
Això darrer ho hem tret d'aquí: http://tools.cherrypy.org/wiki/ModWSGI
 +
<br>

Revisió de 15:06, 21 març 2019

Potser esteu buscant un altre article: Web Services: exemple Escacs


Introducció[modifica]

Els serveis web, simplificant, és una manera de realitzar crides a funcions remotes mitjançant el protocol HTTP. És la forma més còmoda per al programador web d'anar a buscar dades a un servidor, ja sigui des d'un altre servidor o des d'un client.

Al tractar-se, com hem dit, de "crides a funcions remotes", solem anomenar-los API : Application Programming Interface.

Des de la línia de comandes Unix podem fer una d'aquestes crides a través de la comanda curl (call URL). Per exemple, per obtenir el HTML d'una web simple, podríem fer:

$ curl http://cacauet.org

La comanda curl permet fer crides més complexes amb mètodes POST, GET, PUT, DELETE, etc. i adjuntar dades i headers. Consulta les possibilitats amb:

$ man curl

Però no només podem obtenir HTML. El servidor ens podria retornar una dada concreta, per exemple, el nº de vots obtingut per un partit en unes eleccions o bé la quantitat de "likes" d'un post.

Normalment, per tal d'ordenar correctament dades més complexes, s'acaben retornant les dades en un d'aquests dos formats (veure JSON vs XML).

  • XML
  • JSON


Caracteristiques[modifica]

Un servei web o web service ve a ser un servei més del sistema informàtic (o sigui, un sistema d'intercanvi d'informació), però amb algunes peculiaritats:

  • Utilitza el protocol HTTP per intercanviar les dades.
    • Utilitza els mètodes HTTP: GET, POST, PUT, DELETE (inspirat en el CRUD de les BBDD).
    • Pot tenir altres mètodes (arbitraris) però no és tan usual.
  • Sol aplicar-se a sistemes navegables (enllaços/hipervincles).
  • Sol estar al port 80
  • Sol utilitzar una arquitectura REST o REpresentational State Transfer.
    • Arquitectura client-servidor
    • Sense estat
    • Cachejable
    • Per capes
    • Codi "on demand": compartit (al client, p.ex. javascript)
    • Interfície uniforme

Avantatges:

  • Estandarització: sol utilitzar-se JSON (JavaScript Object Notation) com a format d'intercanvi de dades. És molt còmode i més comprimit que en altres formats com XML.
  • Convé utilitzar les llibreries estàndard de JSON per no haver de reimplementar el parsing de les dades a enviar i rebre.

Mètodes REST:

  • GET: no s'envien dades (tot i que sí es poden posar en la URL)
  • POST: s'envien dades adjuntes
  • PUT: sol servir per guardar un element a la BBDD
  • DELETE: sol servir per eliminar un element de la BBDD
  • custom: es poden definir per l'usuari


Exemple d'accés a un web service extern (Musicbrainz)[modifica]

Normalment accedit amb la comanda curl des de la pròpia shell. Primer cal instal·lar-la:

$ sudo apt install curl

Exploreu els següents recursos:

Exemple:

$ curl -H 'Accept:application/json' http://musicbrainz.org/ws/2/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da?inc=aliases | python3 -m json.tool

Crides cURL en PHP[modifica]

En el context de PHP, posem que volem obtenir dades per al nostre servidor. Podem utilitzar la llibreria curl, pel que caldrà que instal·leu el paquet php-curl:

$ sudo apt install php-curl

El mateix exemple anterior el cridaríem des de PHP d'aquesta manera:

// inicialitzem la crida cURL
$url = "http://musicbrainz.org/ws/2/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da?inc=aliases";
$c = curl_init( $url );

// Ajustem headers perquè ens retorni la info en format JSON
// tb afegim User-Agent (identificador de client, si no Musicbrainz no funciona)
curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type:application/json','Accept:application/json','User-Agent:Laravel/5.7'));
// ajustos perquè ens retorni les dades sobre una variable
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);

// cridem per obtenir les dades
$res = curl_exec($c);

// transformem les dades a un array associatiu de PHP
$dades = json_decode($res,true);

// mostrem nom ($dades ja és un array associatiu PHP)
echo $dades["aliases"][0]["name"];

Especificació d'un Web Service[modifica]

Abans de posar-nos a treballar cal fer una correcta especificació. En aquest cas encara és més important ja que el codi estarà separat en 2 blocs: client (JS) i servidor (WS). Sol ser molt comú repartir en especialistes de cada part els dos blocs, i perquè el projecte funcioni cal que l'especificació sigui clara.

Tenim 2 exemples:

Veureu que cada servei o endpoint necessita una especificació de les dades que necessita que li entrem (enviar) i quines ens donarà.


Obtenir llista de canals i missatges[modifica]

Method: GET URL: http://exemple.com/api/xat

GET       http://exemple.com/api/xat
REQUEST (entrada plain text)
Paràmetre Valor Comentari
canal string canal de xat del què volem rebre els missatges
RESPONSE (JSON)
Paràmetre Valor Comentari
status true/false ens informa si tot ha anat bé
missatges [ {"nick":"pere","missatge":"hola!"} , {...} , {...} ] llista de missatges del canal
missatge "canal inexistent"
"error intern"
detalls del status de la comanda


Enviar un missatge[modifica]

POST http://exemple.com/api/xat
REQUEST (entrada JSON)
atribut valor (exemple) comentari
canal string canal de xat al què volem enviar el missatge
nick string nom del remitent
missatge string cos dels missatge a enviar
RESPONSE (JSON)
atribut valor comentari
servei "xat" nom del servei que estem utilitzant (sempre serà "xat")
canal string canal de xat al què hem volgut enviar el missatge
status true/false si la petició s'ha resolt OK (gravar missatge)
missatge text missatge amb detalls de l'execució, per ex:
"ERROR: JSON incorrecte"
"missatge enregistrat correctament"


Exemple en Python i CherryPy[modifica]

En Python solem utiltizar la llibreria json o bé simplejson. Els mètodes més importants d'aquesta llibreria son:

  • json.loads
  • json.dumps

Fes un cop d'ull al framework CherryPy per desenvolupament ràpid en Python, l'utilitzarem per fer algunes pràctiques.

En moltes ocasions, els propis "renderers" del nostre framework ens faran la feina de traduir i importar els objecte JSON, tot i que en el fons estaran utilitzant aquesta llibreria.

En aquest exemple utilitzem CherryPy i MongoDB com a base de dades. La llibreria Pymongo ens serveix per accedir des de Python a MongoDB.

  • Dintre de MongoDB utilitzarem la BBDD "xats" per emmagatzemar les converses.
  • Cada col·lecció dintre de "xats" serà un "canal" o "conversa".
# http://www.cherrypy.org/
 
import cherrypy
import json
import pymongo
import datetime
 
class Xat:
    exposed = True
    
    def __init__(self):
        # inicialitzem la DB de canals
        self.client = pymongo.MongoClient()
        self.db = self.client.xats
 
    # llistar missatges o canals (parametre canal per get)
    # tipus http://.../api/xat?canal=test
    def GET(self,*args,**kwargs):
        resposta = {}
        # processem parametres
        canal = kwargs.get("canal")
        if not canal:
            # mostrem llista de canals (col.leccions)
            include_sys_cols = False
            canals = self.db.collection_names(include_sys_cols)
            resposta["canals"] = canals
        else:
            # responem llista de missatges del canal
            missatges = self.db[canal].find({})
            msgs = []
            for msg in missatges:
                msg["_id"] = str(msg["_id"])
                msg["timestamp"] = str(msg["timestamp"])
                msgs.append( msg )
            if not msgs:
                return json.dumps({"status":False,"missatge":"Canal inexistent"})
            resposta["missatges"] = msgs
        resposta["status"] = True
        print resposta
        return json.dumps( resposta )
 
    # enviar missatges
    @cherrypy.tools.json_in()
    def POST(self,*args,**kwargs):
        resposta = {}
        try:
            dades = cherrypy.request.json
            # "dades" conte elements JSON deserialitzats
            canal = dades["canal"]
            doc = {}
            doc["missatge"] = dades["missatge"]
            doc["nick"] = dades["nick"]
            doc["timestamp"] = datetime.datetime.now()
            self.db[canal].save( doc )
            resposta["status"] = True
            resposta["missatge"] = "Missatge guardat correctament."
        except:
            resposta["status"] = False
            resposta["missatge"] = "Error indefinit"
        return json.dumps( resposta )
 
if __name__ == '__main__':
    cherrypy.tree.mount(
        Xat(), '/api/xat',
        {'/':
            {'request.dispatch': cherrypy.dispatch.MethodDispatcher()}
        }
    )
    cherrypy.engine.start()
    cherrypy.engine.block()


Fixeu-vos en què:

  • La classe Xat() implementa els mètodes REST ( GET i POST ). Podriem implementar altres mètodes restful (PUT, DELETE) creant els pertinents mètodes a la classe. El framework ja crida un o altre depenent del tipus de header que rebi en el request.
  • Per configurar aquest comportament del framework hem hagut d'utilitzar el MethodDispatcher() dins del main (és diferent de la configuració ràpida habitual de CherryPy).
  • Les dades d'entrada ens les parseja el framework mitjançant el decorator @cherrypy.tools.json_in(). Així podem utiltizar les dades directament en JSON sense fer un json.loads:
    dades = cherrypy.request.json
  • Les dades de sortida les hem de transformar a JSON
    return json.dumps
  • El framwork s'encarrega de posar headers indicant que la resposta també és JSON.


Testejant el WS amb cURL[modifica]

La comanda curl (cal instal·lar-la) ens serveix per cridar a URLs i testejar els WS.

Per veure la llista de canals:

$ curl http://localhost:8080/api/xat

Per veure els missatges d'un canal:

$ curl http://localhost:8080/api/xat?canal=DAW_MASTERS

I per enviar un missatge:

$ curl http://localhost:8080/api/xat \
  -d '{"canal":"DAW_MASTERS","missatge":"Benvinguts al xat DAW MASTERS!!","nick":"Enricus"}' \
  -H "Content-Type: application/json" -X POST

Fiexu-vos en què:

  • Si només hi ha la URL s'assumeix que és una crida GET.
  • Passem els paràmetres GET com habitualment (dins la pròpia URL amb un ? i separats per &).
  • Per poder enviar dades per POST (-d) cal afegir els headers adequats per indicar que la informació és en format JSON, és a dir: -H "Content-Type: application/json"
  • El -X POST és opcional ja que sempre que li posem un "-d" (dades) ja s'assumeix que és POST.


Més sobre web services[modifica]

Exemples:


CherryPy en producció[modifica]

Per posar una app feta amb CherryPy en producció, podem utilitzar qualsevol servidor compatible amb WSGI.

Tens exemples de deployment Python aquí (Django).

El punt clau és canviar el típic quickstart de la app CherryPy pel punt d'entrada application d'aquesta manera:

application = cherrypy.Application( CherryApp(), script_name=None, config=None)

Això darrer ho hem tret d'aquí: http://tools.cherrypy.org/wiki/ModWSGI