Diferència entre revisions de la pàgina «Web Services»
Línia 1: | Línia 1: | ||
== Introducció == | == Introducció == | ||
− | |||
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 18: | Línia 17: | ||
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: | Mètodes REST: | ||
Línia 27: | Línia 26: | ||
* DELETE: sol servir per eliminar un element de la BBDD | * DELETE: sol servir per eliminar un element de la BBDD | ||
* custom: es poden definir per l'usuari | * custom: es poden definir per l'usuari | ||
+ | |||
<br> | <br> | ||
− | |||
== Especificació d'un Web Service == | == 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. | 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. | ||
− | En aquest cas tenim un exemple d'un xat. | + | En aquest cas tenim un exemple d'un xat. 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 === | === Obtenir llista de canals i missatges === | ||
Línia 66: | Línia 66: | ||
|} | |} | ||
+ | <br> | ||
=== Enviar un missatge === | === Enviar un missatge === | ||
Línia 105: | Línia 106: | ||
| missatge amb detalls de l'execució | | missatge amb detalls de l'execució | ||
|} | |} | ||
+ | |||
+ | <br> | ||
== Exemple en Python i CherryPy == | == Exemple en Python i CherryPy == | ||
Línia 116: | Línia 119: | ||
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 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. | 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. | * Dintre de MongoDB utilitzarem la '''BBDD "xats"''' per emmagatzemar les converses. | ||
* Cada '''col·lecció''' dintre de "xats" serà un "canal" o "conversa". | * Cada '''col·lecció''' dintre de "xats" serà un "canal" o "conversa". | ||
− | |||
<syntaxhighlight lang="python"> | <syntaxhighlight lang="python"> | ||
Línia 199: | Línia 199: | ||
Fixeu-vos en què: | Fixeu-vos en què: | ||
+ | * La classe Xat() implementa els mètodes REST ( '''PUT''' i '''GET''' ). 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 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 indicant que la resposta també és JSON. | * El ''framwork'' s'encarrega de posar headers indicant que la resposta també és JSON. | ||
− | + | <br> | |
+ | |||
+ | == Testejant el WS amb cURL == | ||
La comanda curl (cal instal·lar-la) ens serveix per cridar a URLs i testejar els WS. | La comanda curl (cal instal·lar-la) ens serveix per cridar a URLs i testejar els WS. | ||
Línia 223: | Línia 227: | ||
<br> | <br> | ||
− | == | + | == Més sobre web services == |
− | + | Teniu un altre exemple de WS implementat en Python i Pyramid per un xat aquí: https://github.com/lacetans/jsonxat_ws | |
− | + | <br> | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− |
Revisió del 12:30, 17 març 2015
Contingut
Introducció
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 utiltizar 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
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.
En aquest cas tenim un exemple d'un xat. 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
atribut | valor (exemple) | comentari |
---|---|---|
canal | principal | canal de xat del què volem rebre els missatges |
atribut | 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
atribut | valor (exemple) | comentari |
---|---|---|
canal | principal | canal de xat al què volem enviar el missatge |
missatge | "hola, com anem per aquí?" | cos dels missatge a enviar |
atribut | valor | comentari |
---|---|---|
servei | "xat" | nom del servei que estem utilitzant (sempre serà "xat") |
canal | "principal" | canal de xat al què hem volgut enviar el missatge |
status | true/false | si la petició s'ha resolt OK (gravar missatge) |
missatge | "ERROR: JSON incorrecte" "missatge enregistrat correctament" |
missatge amb detalls de l'execució |
Exemple en Python i CherryPy
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 ( PUT i GET ). 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 delmain
(é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
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
Teniu un altre exemple de WS implementat en Python i Pyramid per un xat aquí: https://github.com/lacetans/jsonxat_ws