Cliente Web Service REST con Python y requests

De ChuWiki

Vamos a hacer un ejemplo de cliente REST usando el módulo requests de Python. Este módulo es general y sirve para hacer llamadas HTTP a un servidor. Pero en este ejemplo nos centraremos en usarlo como cliente REST. Para probarlo necesitas levantar el servidor REST de Web Services REST con Python y Flask. El código completo de ejemplo tanto del servidor REST como de este cliente lo tienes en python REST. El código de este ejemplo está ahí dentro en rest-client-requests.py

Cualquier duda o sugerencia sobre este ejemplo o de Python en general, suelo antender en este foro de Python

Módulo requests: Conceptos básicos[editar]

El módulo requests viene con la instalación estándar de Python, así que está listo para poder usarse. Este módulo facilita la llamada a servidores HTTP para obtener su contenido, subir información, etc. En este ejemplo lo usaremos para hacer llamdas GET, POST, PUT y DELETE a un servidor REST.

Para usarlo, simplemente debemos hacer el import en nuestro código

import requests

Antes de meternos con las llamadas a un web service REST vamos a ver su uso básico. Haremos una llamda POST en la que haya datos en el cuerpo de la petición HTTP, parámetros en la URL y un parámetro en la cabecera HTTP. El código de la llamada será el siguiente

import requests


if __name__ == '__main__':
    my_headers = {"Content-Type": "application/json"}
    my_data = '{"key1": "value1", "key2": "value2"}'
    my_params = {"par1": "value1", "par2": "value2"}
    response = requests.post("http://127.0.0.1:5000/sample", headers=my_headers, data=my_data, params=my_params)
    print(response.content)
    print(response.status_code)

En my_headers guardamos los parámetros que queramos que vayan en el encabezado de nuestra petición HTTP. Es un dict con clave/valor correspondientes a los parámetros de la cabecera. En nuestro ejemplo, como querremos que los datos del cuerpo de la petición HTTP sean JSON, lo indicamos poniendo que el Content-Type es application/json

En my_data guardamos los datos que queremos enviar en el cuerpo de la petición HTTP, en este caso, un texto JSON.

En my_params guardamos los parámetros que queremos que formen parte de la URL estilo "?par1=value1&par2=value2". Nuevamente, guardamos esto como un dict con pares clave/valor.

Con todo esto, la llamada es simple. Se llama a request.post() porque queremos que la petición sea POST. Como parámetros de esta función pasamos:

  • La URL donde está el web service POST. No ponemos los parámetros de la URL
  • con headers=my_headers pasamos nuestro dict con los parámetros de la cabecera HTTP.
  • con data=my_data pasamos los datos que queramos que vayan en el cuerpo de la petición HTTP
  • con params=my_params pasamos los parámetros que forman parte de la URL

El restultado de la llamda, que es lo que nos devuelve el servidor, lo guardamos en la variable response. response.content devuelve los datos de la respuesta del servidor. response.status_code devuelve el código de estado resultado de la petición, indicando si todo ha ido bien, si ha habido algún error y qué tipo de error.

Conociento esto, ahora sí, vamoa a hacer llamadas a nusetro servidor REST. El servidor REST que usamos de ejemplo tiene una lista de usuarios y nos permite consultar, modificar, añadir y borrar usuarios. Vamos a ir haciendo todas estas llamadas.

Llamada HTTP GET[editar]

El código para obtener toda la lista de usuarios puede ser el siguiente

imort requests
...
    print("--GET--")
    result = requests.get("http://127.0.0.1:5000/users")

    if 200 == result.status_code:
        print(result.status_code)
        print(result.content)
        user = result.json()
        print(user)
        print(user[1]['name'])
    else:
        print("Ha habido algun error en la llamada")
        print(result.status_code)

La llamada para obtener la lista de usuarios es una llamada HTTP GET, así que usamos requests.get() pasando como parámetro la URL donde está el web service REST que devuelve la lista de usuarios. Guardamos el resultado de la llamada en la variable result. Esta variable contienen mucha información. result.status_code devuelve el código HTTP resultado de la llamda. Si todo ha ido bien, debe ser un código 200. Así que ponemos un if para seguir tratando los resultaods. En la parte del else se muestra el código de error en pantalla y no se tratan los datos. En caso de que todo haya ido bien, en result.content tenemos la info devuelta por el servidor, es decir, la lista de usuarios. Esta lista viene como un array de bytes con el texto JSON. La salida del programa hasta este punto es

200
b'[{"name": "Juan", "age": 11}, {"name": "Pedro", "age": 23}, {"name": "Maria", "age": 33}]'

Ahora nos queda convertir ese array de bytes con texto en una lista de usuarios. Cada usuario será un dict que podamos consutar fácilmente. Para ello, usamos la función result.json() que nos convierte el contenido a una lista con dict dentro correspondientes a cadsa usuario. La salida del print() es

[{'name': 'Juan', 'age': 11}, {'name': 'Pedro', 'age': 23}, {'name': 'Maria', 'age': 33}]

Aunque visualmente se parece mucho a la salida anterior, con user podemos hacer llamadas estilo print(user[1]['name']) para obtener el nombre del usuario 1 de la lista. Recuerda que las listas empiezan en el elemento 0, por lo que el usuario 1 será "Pedro". Esto no podemos hacerlo con un string ni con un array de bytes.

Llamada HTTP GET con índice[editar]

Exactamente igual sería el código para obtener un único usuario por su índice (posicion en la lista). El código sería

imort requests
...
def do_get_idx():
    print("--GET IDX--")
    result = requests.get("http://127.0.0.1:5000/users/1")
    if 200 == result.status_code:
        user = result.json()
        print(user)
    else:
        print("Ha habido algun error en la llamada")
        print(result.status_code)

La única diferencia es que la URL termina con el número de indice del usuario (el /1). Y que ahora user contendrá un usuario, "Pedro" y no una lista de usuarios. Podemos llamar a user['name'] para obtgener el nombre.

Llamada HTTP POST[editar]

El código para hacer una llamada HTTP POST sería el siguiente

imort requests
...
def do_post():
    print("-- POST --")
    new_user = '{"name": "Ana", "age": 44}'
    headers = {"Content-Type": "application/json"}
    result = requests.post("http://127.0.0.1:5000/users", data=new_user, headers=headers)

    if 201 == result.status_code:
        user = result.json()
        print(user)
        do_get()
    else:
        print("Ha habido algun error en la llamada")
        print(result.status_code)

En el servidor de web service que estamos usando de ejemplo, la petición HTTP POST sirve para crear un usuario nuevo. En la variable new_user ponemos un string JSON que es el que pasaremos como datos de ese usuario y en el formato que lo espera el servidor. En headers guardamos la cabecera HTTP adecuada para indicar que el contenido de la petición es JSON00. La llamada se hace con requests.post() pasando como primer parámetro la URL donde hacemos la petición, como segundo parámetro el string JSON que hemos cuardado en new_user y como tercer parámetro la cabecera HTTP. Esta llamada devuelve un resultado que guardamos en result.

Dentro de result, tenemos el código de retorno de la petición HTTP en result.status_code. Este código nos sirve para saber si la petición ha ido bien. En este caso, si todo va bien, el servidor devuelve un código HTTP 201. Así que ponemos un if. Si todo ha ido bien, en result.content tenemos los datos que devuelve la petición que, en este ejemplo, es el usuario recién creado en formato JSON. Así que lo decodificamos con result.json(). Luego sacamos el usuario por pantalla y hacemos una peticón do_get() simplemente para verificar en la salida la lista de usuarios con el nuveo usuario añadido. Si la cosa no ha ido bien, simplemente sacamos por pantalla un mensaje.

Petición HTTP PUT[editar]

El código para una petición HTTP PUT es el siguiente

imort requests
...
def do_put():
    print("-- PUT --")
    new_user = '{"name": "Pedro", "age": 23}'
    headers = {"Content-Type": "application/json"}
    result = requests.put("http://127.0.0.1:5000/users/1", data=new_user, headers=headers)

    if 200 == result.status_code:
        user = result.json()
        print(user)
        do_get()
    else:
        print("Ha habido algun error en la llamada")
        print(result.status_code)

Nada nuevo, muy similar al código HTTP POST. Solo cambia la llamada que es <codre>reques.put() y la URL a la que llamar. Esta URL contiene en su path el identificador del usuario a cambiar.

Llamada HTTP DELETE[editar]

El código para una llamada HTTP DELETE

imort requests, json
...
def do_delete():
    print("-- DELETE --")
    result = requests.delete("http://127.0.0.1:5000/users/3")
    if 204 == result.status_code:
        print("Usuario borrado con exito")
        do_get()
    else:
        print("Ha habido algun error en la llamada")
        print(result.status_code)

En este caso la llamada es requests.delete(). El código devuelto si no hay error es 204.