0

I'm working with Flask/Flask-RESTPlus to create a JSON-based REST API and as such I want to convert any errors from the server into the JSON format used. For whatever reason, the code I'm using doesn't seem to catch JSON decoding errors and instead outputs them directly with this response:

{
    "message": "Failed to decode JSON object: Expecting value: line 1 column 1 (char 0)"
}

From what I understand of Flask this should have come through as:

{
    "errors": {
        "message": "Failed to decode JSON object: Expecting value: line 1 column 1 (char 0)"
    }
}

I added a few debugging print statements and it's fairly clear to me that the response handler I added isn't actually catching anything since nothing gets output from the exception handler when a request with invalid JSON is made.

I've got the following code that replicates the issue:

from flask import Flask, jsonify, request, Response
from flask_restplus import Api, Resource
from werkzeug.exceptions import default_exceptions, HTTPException
import logging


def err_to_json(ex):
    logging.warning("error handler")
    logging.warning(ex)
    code = 500
    if isinstance(ex, HTTPException):
        code = ex.code

    return jsonify({"errors": {"message": str(ex)}}), code


app = Flask(__name__)
app.config["HOST"] = "0.0.0.0"
app.config["PORT"] = 80
api = Api(app)


@api.route("/test")
class TestEndpoint(Resource):
    def post(self):
        data = request.get_json()
        logging.warning("data")
        logging.warning(data)


if __name__ == "__main__":
    for ex in default_exceptions:
        app.register_error_handler(ex, err_to_json)

    app.run(port=8000, host="0.0.0.0", debug=True)

This seems to be a slight adaptation of the pattern recommended by the Flask library. I've also tried directly handling the JSONDecodeError, but that made no difference either (app.register_error_handler(json.JSONDecodeError, err_to_json)).

Any idea why a request with invalid JSON causes a 400 error directly, instead of going through the error handler?

10
  • Are you sure the relevant exception is in default_exceptions? Commented Jul 8, 2019 at 19:45
  • @Barmar No, but it's not coming through as a 500 Internal Server Error if it wasn't caught, so I'm assuming that it's something that should be caught. Commented Jul 8, 2019 at 19:52
  • So the error is being caught, you're just not getting the result you expect? What you show looks like the value of the error property of the JSON result. Commented Jul 8, 2019 at 19:55
  • @Barmar That's what seems to be happening. I would agree, it seems like that is the error message but it's not being handled by my code. Commented Jul 8, 2019 at 20:02
  • Are you seeing the log messages from logging.warning("error handler")? Commented Jul 8, 2019 at 20:05

1 Answer 1

1

This is caused by Flask-RESTPlus overriding the Flask error handling code, as documented here.

In this case, I would theoretically need to set up a default error handler via the following, but it seems to be bugged, as per this un-answered issue:

@api.errorhandler
def default_error_handler(err):
    return {"errors": {"message": str(err)}}, getattr(err, "code", 500)

Note that you also must set the ERROR_INCLUDE_MESSAGE config value to False if you want to avoid Flask-RESTPlus automatically adding in the message key again, resulting a response like the following:

{
    "errors": {
        "message": "400 Bad Request: Failed to decode JSON object: Expecting value: line 1 column 1 (char 0)"
    },
    "message": "400 Bad Request: Failed to decode JSON object: Expecting value: line 1 column 1 (char 0)"
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.