Search code examples
pythonjsonflask

Ensure the POST data is valid JSON


I am developping a JSON API with Python Flask.
What I want is to always return JSON, with a error message indicating any error that occured.

That API also only accept JSON data in the POST body, but Flask by default return a HTML error 400 if it can't read the data as JSON.

Preferably, I d also like to not force the user to send the Content-Type header, and if raw or text content-type, try to parse the body as JSON nonetheless.

In short, I need a way to validate that the POST body's is JSON, and handle the error myself.

I've read about adding decorator to request to do that, but no comprehensive example.


Solution

  • You have three options:

    • Register a custom error handler for 400 errors on the API views. Have this error return JSON instead of HTML.

    • Set the Request.on_json_loading_failed method to something that raises a BadRequest exception subclass with a JSON payload. See Custom Errors in the Werkzeug exceptions documentation to see how you can create one.

    • Put a try: except around the request.get_json() call, catch the BadRequest exception and raise a new exception with a JSON payload.

    Personally, I'd probably go with the second option:

    from werkzeug.exceptions import BadRequest
    from flask import json, Request, _request_ctx_stack
    
    
    class JSONBadRequest(BadRequest):
        def get_body(self, environ=None):
            """Get the JSON body."""
            return json.dumps({
                'code':         self.code,
                'name':         self.name,
                'description':  self.description,
            })
    
        def get_headers(self, environ=None):
            """Get a list of headers."""
            return [('Content-Type', 'application/json')]
    
    
    def on_json_loading_failed(self, e):
        ctx = _request_ctx_stack.top
        if ctx is not None and ctx.app.config.get('DEBUG', False):
            raise JSONBadRequest('Failed to decode JSON object: {0}'.format(e))
        raise JSONBadRequest()
    
    
    Request.on_json_loading_failed = on_json_loading_failed
    

    Now, every time request.get_json() fails, it'll call your custom on_json_loading_failed method and raise an exception with a JSON payload rather than a HTML payload.