flask: error_handler for blueprints - http-status-code-404

Can error_handler be set for a blueprint?
#blueprint.errorhandler(404)
def page_not_found(error):
return 'This page does not exist', 404
edit:
https://github.com/mitsuhiko/flask/blob/18413ed1bf08261acf6d40f8ba65a98ae586bb29/flask/blueprints.py
you can specify an app wide and a blueprint local error_handler

You can use Blueprint.app_errorhandler method like this:
bp = Blueprint('errors', __name__)
#bp.app_errorhandler(404)
def handle_404(err):
return render_template('404.html'), 404
#bp.app_errorhandler(500)
def handle_500(err):
return render_template('500.html'), 500

errorhandler is a method inherited from Flask, not Blueprint.
If you are using Blueprint, the equivalent is app_errorhandler.
The documentation suggests the following approach:
def app_errorhandler(self, code):
"""Like :meth:`Flask.errorhandler` but for a blueprint. This
handler is used for all requests, even if outside of the blueprint.
"""
Therefore, this should work:
from flask import Blueprint, render_template
USER = Blueprint('user', __name__)
#USER.app_errorhandler(404)
def page_not_found(e):
""" Return error 404 """
return render_template('404.html'), 404
On the other hand, while the approach below did not raise any error for me, it didn't work:
from flask import Blueprint, render_template
USER = Blueprint('user', __name__)
#USER.errorhandler(404)
def page_not_found(e):
""" Return error 404 """
return render_template('404.html'), 404

add error handling at application level using the request proxy object:
from flask import request,jsonify
#app.errorhandler(404)
#app.errorhandler(405)
def _handle_api_error(ex):
if request.path.startswith('/api/'):
return jsonify(ex)
else:
return ex
flask Documentation

I too couldn't get the top rated answer to work, but here's a workaround.
You can use a catch-all at the end of your Blueprint, not sure how robust/recommended it is, but it does work. You could also add different error messages for different methods too.
#blueprint.route('/<path:path>')
def page_not_found(path):
return "Custom failure message"

Surprised others didn't mention miguelgrinberg's excellent tutorial.
https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-vii-error-handling
I found the sentry framework for error handling (links below). Seems overly complex. not sure of the threshold where it becomes useful.
https://flask.palletsprojects.com/en/1.1.x/errorhandling/
https://docs.sentry.io/platforms/python/guides/flask/

I combined previous excellent answers with the official docs from Flask, section 'Returning API Errors as JSON', in order to provide a more general approach.
Here is a working PoC that you can copy and paste on your registered blueprint API route handler (e.g. app/api/routes.py):
#blueprint.app_errorhandler(HTTPException)
def handle_exception(e):
"""Return JSON instead of HTML for HTTP errors."""
# start with the correct headers and status code from the error
response = e.get_response()
# replace the body with JSON
response.data = json.dumps({
"code": e.code,
"name": e.name,
"description": e.description,
})
response.content_type = "application/json"
return response

Flask doesnt support blueprint level error handlers for 404 and 500 errors. A BluePrint is a leaky abstraction. Its better to use a new WSGI App for this, if you need separate error handlers, this makes more sense.
Also i would recommend not to use flask, it uses globals all over the places, which makes your code difficult to manage if it grows bigger.

Related

How to do a Post/Redirect/Get (PRG) in FastAPI?

I am trying to redirect from POST to GET. How to achieve this in FastAPI?
What did you try?
I have tried below with HTTP_302_FOUND, HTTP_303_SEE_OTHER as suggested from Issue#863#FastAPI: But Nothing Works!
It always shows INFO: "GET / HTTP/1.1" 405 Method Not Allowed
from fastapi import FastAPI
from starlette.responses import RedirectResponse
import os
from starlette.status import HTTP_302_FOUND,HTTP_303_SEE_OTHER
app = FastAPI()
#app.post("/")
async def login():
# HTTP_302_FOUND,HTTP_303_SEE_OTHER : None is working:(
return RedirectResponse(url="/ressource/1",status_code=HTTP_303_SEE_OTHER)
#app.get("/ressource/{r_id}")
async def get_ressource(r_id:str):
return {"r_id": r_id}
# tes is the filename(tes.py) and app is the FastAPI instance
if __name__ == '__main__':
os.system("uvicorn tes:app --host 0.0.0.0 --port 80")
You can also see this issue here at FastAPI BUGS Issues
I also ran into this and it was quite unexpected. I guess the RedirectResponse carries over the HTTP POST verb rather than becoming an HTTP GET. The issue covering this over on the FastAPI GitHub repo had a good fix:
POST endpoint
import starlette.status as status
#router.post('/account/register')
async def register_post():
# Implementation details ...
return fastapi.responses.RedirectResponse(
'/account',
status_code=status.HTTP_302_FOUND)
Basic redirect GET endpoint
#router.get('/account')
async def account():
# Implementation details ...
The important and non-obvious aspect here is setting status_code=status.HTTP_302_FOUND.
For more info on the 302 status code, check out https://httpstatuses.com/302 Specifically:
Note: For historical reasons, a user agent MAY change the request method from POST to GET for the subsequent request. If this behavior is undesired, the 307 Temporary Redirect status code can be used instead.
In this case, that verb change is exactly what we want.

Error 405 Method not allowed

I am desgining a web app using google app engine and python and
While extracting the form data ie. parameter values using the get request the following error occurs during the runtime:
405 Method Not Allowed
The method GET is not allowed for this resource.
Following is the code:
is the get mothos creating problem or is there any other solution to get parameter values.
class PostBody(webapp2.RequestHandler):
def post(self):
stringContent=cgi.escape(self.request.get('txtLocation'))
stringurl='http://maps.googleapis.com/maps/api/place/textsearch/json?query='|stringContent|'&sensor=false&key=mykey'
result=json.load(urllib.urlopen(stringurl))
self.response.write(result)
Thanks...
Try replacing
def post(self)
by
def get(self)

Prototyping: Simplest HTTP server with URL routing (to use w/ Backbone.Router)?

We're working on a Backbone.js application and the fact that we can start a HTTP server by typing python -m SimpleHTTPServer is brilliant.
We'd like the ability to route any URL (e.g. localhost:8000/path/to/something) to our index.html so that we can test Backbone.Router with HTML5 pushState.
What is the most painless way to accomplish that? (For the purpose of quick prototyping)
Just use the built in python functionality in BaseHTTPServer
import BaseHTTPServer
class Handler( BaseHTTPServer.BaseHTTPRequestHandler ):
def do_GET( self ):
self.send_response(200)
self.send_header( 'Content-type', 'text/html' )
self.end_headers()
self.wfile.write( open('index.html').read() )
httpd = BaseHTTPServer.HTTPServer( ('127.0.0.1', 8000), Handler )
httpd.serve_forever()
Download and install CherryPy
Create the following python script (call it always_index.py or something like that) and also replace 'c:\index.html' with the path of your actual file that you want to use
import cherrypy
class Root:
def __init__(self, content):
self.content = content
def default(self, *args):
return self.content
default.exposed = True
cherrypy.quickstart(Root(open('c:\index.html', 'r').read()))
Run python <path\to\always_index.py>
Point your browser at http://localhost:8080 and no matter what url you request, you always get the same content.

Handle webapp2 404 error in requesthandler class method instead of in a function

I'm using the webapp2 framework in Google App Engine (Python). In webapp2 exception handling: exceptions in the WSGI app it's described how to handle 404 errors in a function:
import logging
import webapp2
def handle_404(request, response, exception):
logging.exception(exception)
response.write('Oops! I could swear this page was here!')
response.set_status(404)
def handle_500(request, response, exception):
logging.exception(exception)
response.write('A server error occurred!')
response.set_status(500)
app = webapp2.WSGIApplication([
webapp2.Route('/', handler='handlers.HomeHandler', name='home')
])
app.error_handlers[404] = handle_404
app.error_handlers[500] = handle_500
How can I handle the 404 error in a webapp2.RequestHandler class, in the .get() method of that class?
Edit:
The reason I want to call a RequestHandler is to access the session (request.session). Otherwise I'm not able to pass the current user to the template of the 404 error page. i.e. on the StackOverflow 404 error page you can see your username. I would like to display the username of the current user on my website's 404 error page as well. Is this possible in a function or does it has to be a RequestHandler?
Correct code based on #proppy's answer:
class Webapp2HandlerAdapter(webapp2.BaseHandlerAdapter):
def __call__(self, request, response, exception):
request.route_args = {}
request.route_args['exception'] = exception
handler = self.handler(request, response)
return handler.get()
class Handle404(MyBaseHandler):
def get(self):
self.render(filename="404.html",
page_title="404",
exception=self.request.route_args['exception']
)
app = webapp2.WSGIApplication(urls, debug=True, config=config)
app.error_handlers[404] = Webapp2HandlerAdapter(Handle404)
The calling convention of error handler and request handler callables are different:
error_handlers takes (request, response, exception)
RequestHandler takes (request, response)
You may use something similar to Webapp2HandlerAdapter to adapt a webapp2.RequestHandler to a callable.
class Webapp2HandlerAdapter(BaseHandlerAdapter):
"""An adapter to dispatch a ``webapp2.RequestHandler``.
The handler is constructed then ``dispatch()`` is called.
"""
def __call__(self, request, response):
handler = self.handler(request, response)
return handler.dispatch()
But you would have to sneak the extra exception argument in request route_args.

How can I setup a global DeadlineExceededError handler?

I'd like to catch and handle DeadlineExceededError so users don't see the standard "Server Error" page that App Engine throws by default.
I know that DeadlineExceededErrors are not caught when overriding handle_exception in your request handler (we already do this).
I have tried, unsuccessfully so far, to use the custom error_handlers app.yaml configuration like so:
error_handlers:
- error_code: timeout
file: timeout.html
...but that also doesn't seem to catch DeadlineExceededErrors, unless I'm doing something wrong.
I am aware that I can use the following pattern to catch DeadlineExceededErrors inside particular request handlers:
class MainPage(webapp.RequestHandler):
def get(self):
try:
# Do stuff...
except DeadlineExceededError:
# Many Whelps! Handle it!
...but I would like to avoid adding this to every single request handler in my application.
How can I globally catch these elusive suckers?
One possible solution is to use webapp2, which is a pretty neat framework as it is and has a lot of useful stuff over the original webapp. With webapp2, you can handle the exception in the handle_500 method, as follows:
def BaseHandler(webapp2.RequestHandler):
def handle_500(request, response, exception):
if isinstance(exception, DeadlineExceededError):
response.write('Deadline exceeded!')
else:
response.write('A server error occurred!')
logging.exception(exception)
response.set_status(500)

Resources