I have an Azure Function that receives an HTTP Request containing JSON data. The function is supposed to filter the incoming JSON type and, upon identifying the specified JSON, extract certain values from it, creating a new JSON, and sending it in a POST request to another Azure Function. However, the error 'Unexpected end of request content' sometimes appears in the Azure Logs. I've added several Try-Except blocks to try to capture this error, but so far, no success.
@app.route(route="webhooks/baas", auth_level=func.AuthLevel.ANONYMOUS)
def webhook_decoder_baas(req: func.HttpRequest) -> func.HttpResponse:
# Imports and decodes the private_key used for sending extracted information
private_key = os.environ["private_key_baas"]
private_key_baas = base64.b64decode(private_key).decode("utf-8")
# Declares the public_key for decoding the received webhook
qi_public_key = '''-----BEGIN PUBLIC KEY-----
... :)
-----END PUBLIC KEY-----'''
# Checks if the received webhook contains the correct parameters
try:
authorization = req.headers.get("AUTHORIZATION")
body = req.get_json()
# If AUTHORIZATION header is missing, returns an error.
if not authorization:
logging.error("AUTHORIZATION header not provided.")
return HttpResponse("AUTHORIZATION header not provided.")
# If no body content was sent, returns an error.
if not body:
logging.error("BODY content not provided.")
return HttpResponse("BODY content not provided.")
# Decodes the request using the PUBLIC_KEY.
decoded_header = jwt.decode(token=authorization, key=qi_public_key)
# Validates received data, returns error if any doesn't match.
try:
assert decoded_header.get("method") == "POST"
except AssertionError as assertion_error:
logging.error(f"Error in the sending method, a different method than POST was used.")
return HttpResponse(f"Error in the sending method, a different method than POST was used.")
try:
assert decoded_header.get("payload_md5") == md5(json.dumps(body).encode()).hexdigest()
except AssertionError as assertion_error:
logging.error(f"Payload hash does not match the expected one.")
return HttpResponse(f"Payload hash does not match the expected one.")
try:
assert (datetime.utcnow() - timedelta(minutes=5)) < datetime.strptime(decoded_header.get("timestamp"), "%Y-%m-%dT%H:%M:%S.%fZ") < (datetime.utcnow() + timedelta(minutes=5))
except AssertionError as assertion_error:
logging.error(f"Error in sending timestamp, outside the 10-minute limit.")
return HttpResponse(f"Error in sending timestamp, outside the 10-minute limit.")
# Handles standard errors from the JWT library for errors in the received webhook token
except jwt.JWTError as jwt_error:
logging.error(f"Token error: {str(jwt_error)}")
return HttpResponse(f"Token error: {str(jwt_error)}")
# Handles standard Python errors
except Exception as e:
logging.error(f"Internal error while processing the webhook: {str(e)}")
return HttpResponse(f"Internal error while processing the webhook: {str(e)}")
# Converts received data into a PYTHON object
payload_string = json.dumps(body)
objeto_payload = json.loads(payload_string)
# Verifies if the received data meets the desired requirements
if (
objeto_payload.get("webhook_type") == "bank_slip.status_change" and
(objeto_payload.get("status") == "payment_notice" or objeto_payload.get("status") == "notary_office_payment_notice")
):
# Extracts desired data from the received data
bank_slip_key = objeto_payload.get("data", {}).get("bank_slip_key")
paid_amount = objeto_payload.get("data", {}).get("paid_amount")
# Creates a new PYTHON object with the extracted data
payload_output = {
key: objeto_payload[key] for key in ["status"]
}
payload_output['Paid amount'] = paid_amount
payload_output['Query key of the title'] = bank_slip_key
# URL to send the extracted data
url = "https://nerowebhooks.azurewebsites.net/api/information/send"
# Encrypts the data to be sent
token = jwt.encode(payload_output, private_key_baas, algorithm='ES512')
# Creates the sending header
headers = {
'SIGNATURE': token
}
# Sends the extracted information
response = requests.post(url, headers=headers, json=payload_output)
logging.info(f"Received webhook: {payload_output}")
return HttpResponse("Webhook received successfully!", status_code=200)
else:
logging.info("Webhook received successfully, but it won't be handled at the moment!")
return HttpResponse("Webhook received successfully, but it won't be handled at the moment!")
Here is the type of JSON that I expect to receive:
{ "key": "03c38d18-d12f-4b5f-841c-afab52fe33c5", "data": { "our_number": 142, "paid_amount": 6676.38, "payment_bank": 104, "bank_slip_key": "03c38d18-d12f-4b5f-841c-afab52fe33c5", "payment_method": 2, "payment_origin": 3, "paid_in": { "name": "QI TECH", "code_number": "329", "ispb": "32402502" }, "occurrence_type": "payment_notice", "occurrence_feedback": "confirmed", "occurrence_sequence": 0, "requester_profile_code": "329-09-0001-0082162", "registration_institution": "qi_scd", "cnab_file_occurrence_order": 1, "registration_institution_occurrence_date": "2021-04-19" }, "status": "payment_notice", "webhook_type": "bank_slip.status_change", "event_datetime": "2021-04-19 20:04:06" }
But I can also receive various types of JSON, some larger, some smaller. During the testing phase, I sent all kinds of JSON, and when it should capture errors, the code did so effectively.
Instead of directly using
req.get_json(), try usingreq.get_body()and then parsing it as JSON. This way, you can explicitly handle any exceptions that might occur during parsing.IncompleteReadexception and improve the handling of the request body.It will catch the
IncompleteReadexception that might occur while reading the request content and handle it gracefully by returning a 400 Bad Request response.Result:
Reference: