Creating Custom Error Pages and Handling Exceptions for Flask

Hello, welcome back to the 2nd post of my Flask series, this post would be a continuation of my previous post, please click here to read my first post if you missed it.

 

The code for this post can be found on my GitHub account under the branch feat/error-handlers.

OUTLINE OF FLASK SERIES

 

In this new post, you will learn the following

  • Creating custom error pages to the user when an error occurs and registering errors with the register_error_handler() method.
  • How to use a .env file to manage your environment variables.

 

We would install new dependencies for our new post

  • Webargs: is a Python library for parsing and validating HTTP request objects.
  • Httpie : a command-line HTTP client written in Python that makes it easy to send HTTP requests and uses a syntax that is easier than curl.
  • python-dotenv:  helps you add .env support to your Django/flask apps in development and deployments

Remember to activate your virtual env and run pip install -r requirements.txt

 

You might want to show custom error pages to the user when an error occurs. This can be done by registering error handlers with register_error_handler() method.

WHAT IS AN ERROR HANDLER

An error handler is a normal view function that returns a response, but instead of being registered for a route, it is registered for an exception or HTTP status code that would be raised while trying to handle a request.

Let’s get into action

Create a new folder and name it utils, add 2 new files errors.py and __init__.py, now copy the code below to the errors.py

#utils/errors.py

"""Define custom error handlers for application."""

from flask import jsonify, make_response
from werkzeug.http import HTTP_STATUS_CODES


def gen_error(error, status_code, status=None, msg=None):
    """Generate error message format for error handlers."""
   
    message = str(error) or msg

    res = jsonify(status=status,
                  data={"msg": f"{HTTP_STATUS_CODES[status_code]}. {message}"})
    return make_response(res, status_code)


def handle_server_errors(error):
    """Handle all 500 server errors in code."""
    return gen_error(error, 500, "Server error: We are working to"
                     " resolve this issue.")


def handle_404_errors(error):
    """Handle wrong url requests with custom message."""
    status = "error" if isinstance(error, KeyError) else "fail"
    return gen_error(error, 404, status=status)


def handle_400_errors(error):
    """Handle 400 errors in resources."""
    return gen_error(error, 400, status="error")


Now, we have written our custom error, the next step is to import it into the app module and register it, open the api/app.py file, add the code below

from flasgger import Swagger
from flask import Flask
from flask_restful import Api
from webargs.flaskparser import abort, parser

from api.config import env_config
from utils import errors
from werkzeug import exceptions


api = Api()




def create_app(config_name):

    import resources

    app = Flask(__name__)

    app.config.from_object(env_config[config_name])
    
   
    
    #register api 
    api.init_app(app)
    Swagger(app)
    
   
    #error handling
    app.register_error_handler(exceptions.NotFound,
                               errors.handle_404_errors)

    app.register_error_handler(exceptions.InternalServerError,
                               errors.handle_server_errors)

    app.register_error_handler(exceptions.BadRequest,
                               errors.handle_400_errors)

    app.register_error_handler(FileNotFoundError,
                               errors.handle_400_errors)

    app.register_error_handler(TypeError, errors.handle_400_errors)

    app.register_error_handler(KeyError, errors.handle_404_errors)

    app.register_error_handler(AttributeError,
                               errors.handle_400_errors)

    app.register_error_handler(ValueError, errors.handle_400_errors)

    app.register_error_handler(AssertionError,
                               errors.handle_400_errors)
    
    
    #new code
    @parser.error_handler
    def handle_request_parsing_error(err, req, schema, *, error_status_code, 
                                      error_headers):
        """webargs error handler that uses Flask-RESTful's abort function to                 
        return a JSON error response to the client.
        """
        abort(error_status_code, errors=err.messages)
        
    return app

 

Non-standard HTTP codes cannot be registered by code because they are not known by Werkzeug such as (AttributeError, FileNotFoundError). So we registered it with register_error_handler() and raise that exception class if it occurs.

Since we would be using Webargs to handle our client request, we added a webargs error handler that uses Flask-RESTful’s abort function to return a JSON error response to the client.  Webargs uses marshmallow under the hood, so we decorate a function that receives an error, the request, the marshmallow.Schema instance, status code, and headers with @parser.error_handler decorator.

 

Using .env files for managing environment variables

Create a new file .env  in your root file directory

What is .env file?

This is a plain text file containing key-value entries like this:  FLASK_APP=main.py

Important: because environment variables are used to store secrets, you have to make sure it is in your .gitignore .

#.env
FLASK_APP=main.py 
FLASK_DEBUG=1 
FLASK_ENV=development

Update your main.py file (flask app) and add the code below

import os

from dotenv import load_dotenv
from api.app import create_app

load_dotenv(verbose=True)

app = create_app(os.getenv("FLASK_ENV"))

Now start your flask app, run this on your terminal flask run

Let’s test and see everything is working.

 * Serving Flask app "main.py" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 270-181-260

We are done with our 2nd post on my Flask series, next post I would be covering how to implement authentication with JWT and persist data in the database.

The link to Github tutorial is here

Until then happy coding 😊

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *