agosto 5, 2022 · python

RIchiesta HTTP multipart con Python

Giusto un breve promemoria su come effettuare una richiesta HTTP multipart con Python, visto che ci ho messo mezz'ora a trovare il modo :-)

Per prima cosa importiamo un pò di librerie (l'ultima ci servirà solo per costruire l'authentication se vogliamo chiamare una API protetta da basic auth):

from urllib3 import encode_multipart_formdata
from requests import post
from base64 import b64encode

Poi dobbiamo costruire le parti della richiesta. Le parti possono essere qualsiasi cosa, ad esempio testo o il contenuto di un file:

register_document_request = f'''
<register_document_request>
</register_document_request>
'''

document = open(filename, "rb").read()

metadata = "<metadata></metadata>"

Adesso possiamo costruire il multipart, usando il metodo encode_multipart_formdata. Il metodo accetta un oggetto chiave-valore (i valori sono quelli che abbiamo costruito prima) e restituisce due stringhe: il body che contiene gli oggetti e la stringa da utilizzare come header "content-type". Il content type, infatti, oltre a specificare che si tratta di un multipart, deve specificare il "boundary", cioè la stringa random utilizzata nel body per separare le diverse parti.

fields = {
    "register_document_request": register_document_request,
    "document": document,
    "metadata": metadata,
}

body, header = encode_multipart_formdata(fields)

Giusto per curiosità, questo è un possibile aspetto del campo "content-type":

multipart/form-data; boundary=22f8f7d3ad9ba2736a7d6c89afce1ca2

A questo punto possiamo effettuare la richiesta. In questo esempio riporto anche come viene costruito l'header "Authorization", chiaramente come questo è costruito dipende dallo specifico servizio che si va a chiamare e potrebbe non essere necessario.

authAsString = f"{user}:{pwd}"
authAsBytes = authAsString.encode("utf-8")
authAsB64 = b64encode(authAsBytes).decode("ascii")

headers = {
    "content-type": header,
    "Authorization": f"Basic {authAsB64}"
}

A questo punto possiamo effettuare la chiamata vera e propria. Il parametro "verify" a False specifica che debbono essere ignorati i problemi relativi a certificati SSL nella comunicazione HTTPS. Non si dovrebbe usare, ma può essere necessario se si lavora su sistemi di test con certificati errati o autofirmati.

response = post(
    "https://url:porta/servizio", 
    headers=headers, 
    data=body,
    verify=False # ignora problemi SSL
)

Per verificare che tutto abbia funzionato, possiamo stampare lo stato e il testo della risposta:

print(f"Response status: {response.status_code}")
print(f"Response reason: {response.reason}")
print("")
print(response.text)

Fatto!