Cómo obtener y analizar el cuerpo HTTP POST en Flask - JSON y datos de formulario

En esta guía, aprenda cómo obtener, analizar y manejar datos JSON y formularios POSTED entrantes en Flask con Python.

Introducción

Flask es un excelente micro marco para el desarrollo web en Python y le permite ser extremadamente mínimo. Se puede servir una API REST en funcionamiento en segundos a través de unas pocas líneas de código:

1
2
3
4
5
6
7
8
9
from flask import Flask, request
app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello!'

if __name__ == "__main__":
    app.run()

La columna vertebral de la web moderna es el protocolo HTTP, que envía solicitudes y devuelve respuestas. Para diferenciar las intenciones detrás de estas solicitudes, se han asociado varios "verbos" con las tareas que estás realizando. Los verbos GET se usan para anotar solicitudes en las que desea recuperar recursos, los verbos POST se usan para solicitar la creación de recursos, dada la carga útil (cuerpo), los verbos DELETE se usan para solicitar la eliminación de recursos, etc.

Si desea crear un recurso en un servidor, enviará una Solicitud POST con un cuerpo que contiene los datos que envía al servidor.

En esta guía, veremos cómo obtener un cuerpo HTTP POST en Flask.

En general, lo más probable es que publique datos JSON en una API REST que consuma esos datos, o que publique datos de formularios, haciendo que un usuario complete un formulario web y luego envíe esos datos a otra API. para procesar.

Al enviar datos de formulario, generalmente se codifica como multipart/form-data, mientras que al enviar datos JSON, generalmente se codifica como application/json. Esta información está incrustada en el Encabezado de solicitud POST que también puede consultar. Por si acaso, verificaremos los encabezados de la solicitud antes de analizar los datos.

Cuando se trata de solicitudes, el módulo request de flask ​​le permite representar las solicitudes HTTP entrantes. El cuerpo de una solicitud POST se puede extraer directamente de la solicitud misma y, según la codificación, accederá al campo correspondiente:

  • solicitud.json o solicitud.get_json()
  • solicitud.formulario
  • solicitud.datos

request.json representa JSON enviado como una solicitud con el tipo de contenido application/json. Alternativamente, puede usar el método request.get_json(). Tanto el acceso al campo en sí como al método devuelven un dict, con pares clave-valor presentes en el JSON entrante.

{.icon aria-hidden=“true”}

Nota: El campo json y los métodos get_json() solo funcionarán si el Content-Type de la solicitud POST se establece en application/json. Si es una cadena con formato JSON, este enfoque fallará y dará como resultado un valor Ninguno. Si no puede hacer que el cliente envíe datos codificados correctamente, puede convertir la cadena entrante en JSON. Cubierto más adelante en la guía.

request.form representa los datos multipart/form-data que se adquieren a través de formularios web.

request.data es una representación de cadena de los datos entrantes. En general, utilizará esta representación para convertir a JSON si no puede obligar al cliente a enviar el tipo de contenido que espera.

Obtener POST JSON

Comencemos con JSON, ya que este es el formato más utilizado para transferir datos entre API. Crearemos un controlador de ruta simple que reciba una solicitud POST, en el extremo /post_json. Recuerde, el campo json solo contendrá un valor si los encabezados de la solicitud anotan correctamente el cuerpo como una carga útil application/json.

Aquí, también obtendremos el 'Content-Type' de los headers y comprobaremos si el cuerpo tiene el formato application/json. De lo contrario, ni siquiera intentaremos extraer JSON de la solicitud (fallará silenciosamente si lo hacemos) y se devolverá un mensaje de error:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from flask import Flask, request
# ...
@app.route('/post_json', methods=['POST'])
def process_json():
    content_type = request.headers.get('Content-Type')
    if (content_type == 'application/json'):
        json = request.json
        return json
    else:
        return 'Content-Type not supported!'

Si envía una solicitud POST a su punto final ahora, será recibido con el json devuelto:

1
$ curl -X POST -H "Content-type: application/json" -d "{\"firstName\" : \"John\", \"lastName\" : \"Smith\"}" "localhost:5000/post_json"

{.icon aria-hidden=“true”}

Nota: Según el sistema operativo y el shell que esté usando, puede usar ' en lugar de " o incluso omitir los caracteres de escape como \ por completo.

Esto resulta en:

1
{"firstName":"John","lastName":"Smith"}

¡Impresionante! Intentemos establecer el argumento -H en otro tipo, para verificar si el paso de validación funciona bien:

1
$ curl -X POST -H "Content-type: multipart/form-data" -d "{\"firstName\" : \"John\", \"lastName\" : \"Smith\"}" "localhost:5000/post_json"

Esto resulta en:

1
Content-Type not supported!

Alternativamente, get_json() funciona de la misma manera:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from flask import Flask, request
# ...
@app.route('/post_json', methods=['POST'])
def process_json():
    content_type = request.headers.get('Content-Type')
    if (content_type == 'application/json'):
        json = request.get_json()
        return json
    else:
        return 'Content-Type not supported!'

Obtener POST JSON de la cadena

Fuimos nosotros los que enviamos la solicitud hasta el momento, por lo que teníamos la libertad de cambiar el tipo de contenido como mejor nos pareciera. Es posible que este no sea siempre el caso y, a veces, puede encontrarse con una solicitud con formato JSON que no tiene asignado el tipo de contenido correcto.

En ese caso, json y get_json() no analizan el cuerpo entrante como JSON en absoluto, y terminará siendo Ninguno, del cual no podrá extraer nada. En tales casos, puede usar el módulo json para cargar la cadena que ha recibido en un diccionario (pares clave-valor).

Importemos el módulo y convertamos los request.data entrantes:

1
2
3
4
5
6
from flask import Flask, request, json
# ...
@app.route('/post_json', methods=['POST'])
def process_json():
    data = json.loads(request.data)
    return data

Ahora, ya sea que envíe un cuerpo codificado con text/plain, o un cuerpo codificado con application/json, el módulo json puede manejar la entrada. Si intentamos enviar cualquiera de estas solicitudes, ambas darán como resultado la misma respuesta:

1
2
$ curl -X POST -H "Content-type: application/json" -d "{\"firstName\" : \"John\", \"lastName\" : \"Smith\"}" "localhost:5000/post_json"
$ curl -X POST -H "Content-type: text/plain" -d "{\"firstName\" : \"John\", \"lastName\" : \"Smith\"}" "localhost:5000/post_json"

Dan como resultado:

1
{"firstName":"John","lastName":"Smith"}

Obtener formulario POST

Al completar formularios, tiene una serie de entradas y sus etiquetas correspondientes. Bajo el capó, estos son solo pares de “clave-valor”:

1
2
3
username=user_input
password=user_input_2
...

Esto generalmente se deriva del front-end, generalmente una página HTML con una etiqueta <form> con varios campos <input> dentro de ella. También puede enviar datos de formulario a través de curl como:

1
$ curl -X POST -H "Content-type: multipart/form-data"  -F "username=john" -F "password=doe" "localhost:5000/post_form"

O bien, puede contraer todos los campos en un solo argumento:

1
$ curl -X POST -H "Content-type: multipart/form-data"  -F "username=john&password=doe" "localhost:5000/post_form"

Sin embargo, en general, trabajará con formularios reales, y un formulario con el que estaríamos trabajando se parece a:

1
2
3
4
<form action="/post_form" enctype="multipart/form-data" method="POST"> 
    <input type="text" name="username">
    <input type="password" name="password">
</form>

El nombre de cada <entrada> se asigna como la clave a una entrada de valor por parte del usuario. El ‘formulario’ extraído del objeto ‘solicitud’ es otro ‘dict’, y puede acceder a los campos individualmente fácilmente:

1
2
3
4
5
6
7
8
from flask import Flask, request
# ...
@app.route('/post_form', methods=['POST'])
def process_form():
    data = request.form
    print(data['username'])
    print(data['password'])    
    return data

Cuando enviamos una solicitud a través de la consola, se devuelve el diccionario que contiene los pares clave-valor (que luego se formatea nuevamente, como JSON):

1
{"password":"doe","username":"john"}

Y en el extremo del servidor: las entradas para estos dos campos se han impreso, justo debajo del registro de la solicitud entrante.

1
2
3
127.0.0.1 - - [09/Dec/2021 00:24:32] "POST /post_form HTTP/1.1" 200 -
john
doe

Naturalmente, en lugar de simplemente imprimir los valores en la consola, validaría los datos, crearía un usuario y los conservaría en la base de datos. Alternativamente, puede completar los campos de cualquier clase de esta manera, antes de usar esa clase para su propósito.

Conclusión

En esta guía, hemos analizado cómo manejar las solicitudes HTTP POST entrantes en Flask. Hemos cubierto los datos JSON entrantes, así como también cómo manejar JSON representado por cadenas que no se selecciona automáticamente.

Finalmente, hemos cubierto los datos del formulario.