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

De Cacauet Wiki
Salta a la navegació Salta a la cerca
Línia 24: Línia 24:
  
 
== 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.com 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.
 +
 
  
 +
=== Exemple: Xat ===
 
<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 106:
 
     cherrypy.engine.start()
 
     cherrypy.engine.start()
 
     cherrypy.engine.block()
 
     cherrypy.engine.block()
 +
</syntaxhighlight>
  
</syntaxhighlight>
+
Utilitza les següents crides cURL per testejar i veure els canals:
 +
$ curl http://localhost:8080/api/xat -H "Content-Type: application/json"
 +
 
 +
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
  
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.
 
El -X POST és opcional ja que sempre que li posem un "-d" (dades) ja s'assumeix que és POST.
  

Revisió del 11:33, 17 març 2015

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ó
  • ...


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.


Exemple: Xat

# 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()

Utilitza les següents crides cURL per testejar i veure els canals:

$ curl http://localhost:8080/api/xat -H "Content-Type: application/json"

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

El -X POST és opcional ja que sempre que li posem un "-d" (dades) ja s'assumeix que és POST.

Fixeu-vos en què:

  • Les dades d'entrada ens les parseja el framework:
    dades = cherrypy.request.json
  • Les dades de sortida les hem de transformar a JSON
    return json.dumps
    El framwork s'encarrega de posar headers.


Exemple en Python i Pyramid

Teniu el codi íntegre a: https://github.com/lacetans/jsonxat_ws

Fixa't en què en aquest cas:

  • El framework ja ens parseja l'entrada:
    dades = request.json_body
    O sigui, no cal fer un json.loads
  • També el propi framwork ens parseja la sortida, fent un return d'un diccionari Python que es tradueix automàticament a JSON.
    El punt clau per realitzar això és el renderer:
    @view_config(route_name='xat_set_missatge_ws', renderer='jsonp')
@view_config(route_name='xat_set_missatge_ws', renderer='jsonp')
def xat_set_missatge_ws(request):
    try:
        # ho intentem per GET (JSONP)
        remitent = request.GET.get("remitent")
        missatge = request.GET.get("missatge")
        if remitent and missatge:
            if remitent.strip() and missatge.strip():
                linia = remitent + "\t" + missatge 
        else:
            # ho intentm per POST (JSON)
            dades = request.json_body
            linia = dades["remitent"] + "\t" + dades["missatge"] 
        if "<" in linia or "\n" in linia:
            return {
                "servei":"xat",
                "canal":"principal",
                "status":False,
                "missatge":"ERROR: caracters ilegals"
            }
        file = open(STORAGE_FILENAME,"a+")
        file.write(linia+"\n");
        file.close()
            
    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"
    }

Més sobre web services

...