bottlepy / bottle

bottle.py is a fast and simple micro-framework for python web-applications.
http://bottlepy.org/
MIT License
8.38k stars 1.46k forks source link

Returned dictionary doesn't get properly serialized from error handlers #710

Open lcosmin opened 9 years ago

lcosmin commented 9 years ago

I've got the following code (using bottle.py@854fbd7f88):

from bottle import run, Bottle, abort

app = Bottle()

def message(msg, code):
    return {"message": msg, "code": code}

@app.error(400)
def eh(error):
    return message(error.body, error.status_code)

@app.route("/bad", method=["GET"])
def handler():
    abort(400)

@app.route("/good", method=["GET"])
def handler():
    return message("works ok", 200)

run(app, host='localhost', port=8080)

I get different results when calling /bad and /good, as it can be seen below:

$ for i in bad good; do echo -n "$i ==> "; curl http://localhost:8080/$i ; echo;  done
bad ==> messagecode
good ==> {"message": "works ok", "code": 200}

Any idea what's going on?

defnull commented 9 years ago

Output processing does not happen for error handlers. They must return a WSGI conform value (list of byte strings).

This is a limitation of the error handling design and might improve in future versions. The main problem is recursion: What if the output processing of an error handler fails (e.g. json serialisation error)?

I'll keep this issue open as a reminder that this is not solved yet.

eric-wieser commented 9 years ago

Maybe add a @critical_error that behaves as @error currently does, and gets called when @error(500) itself throws an exception?

jacebrowning commented 8 years ago

Output processing does not happen for error handlers. They must return a WSGI conform value (list of byte strings).

That seems to disagree with http://bottlepy.org/docs/dev/tutorial.html#error-pages, which says (emphasis mine):

You can read from request, write to response and return any supported data-type except for HTTPError instances.

jacebrowning commented 8 years ago

In case anyone else needs it, a workaround for the situation @lcosmin presented:

import json

...

@app.error(400)
def eh(error):
    data = message(error.body, error.status_code)
    return json.dumps(data)

...

noting that this might cause issues if JSON serialization fails.