Custom Exception Handler In Django Rest Framework
Parimal P
Exception handling is a crucial aspect of any web application, as it helps ensure that errors and unexpected situations are handled precisely.
In Django Rest Framework (DRF), a powerful and flexible toolkit for building Web APIs, you can create a custom exception handler to tailor error responses to your application’s needs.
In the current scenario, DRF is the de facto tool for creating REST APIs leveraing Django. It is a useful framework with effective functionalities.
In this blog post, we will explicitly create a custom exception handler that covers various exceptions and provides meaningful error messages to the clients.
We will even demonstrate the implementation of a custom exception handler with necessary code examples.
Understanding Django Rest Framework’s Exception Handling
Django Rest Framework (DRF) offers in-built exception handling that can be utilized to manage errors, mistakes, and exceptions in a RESTful API. Furthermore, exception handling in DRF allows you to intercept and customize how errors are presented to clients. By default, DRF provides a standard exception handler that formats exceptions into JSON responses.
However, it would help if you created a custom handler to tailor the error messages and structure to match your application’s requirements.
Read More:- Reasons to Choose Django For Web Development
Building a Custom Exception Handler
You can enable custom exception handling by building a handler function that transforms exceptions raised in the API views into response objects. This facilitates you to regulate the style of error responses utilized by the API. Let us build a custom exception handler with below explained steps.
Defining the Custom Exception Handler Function
Are you required to deal with and handle the exceptions yourselves? Here is how to do it better and precisely. To create a custom exception handler, first, you must define a function that takes two arguments: the exception (exc) and the context (context) in which the exception occurred.
The function will return an applicable error message. In our case, we will also include logic to handle different exceptions.
# exception_handler.py from rest_framework.views import exception_handler from .make_response import response import logging logger = logging.getLogger('django') def custom_exception_handler(exc, context): """ The function `custom_exception_handler` handles custom exceptions by mapping them to specific handlers and returning a response with the appropriate status code and message. :param exc: The `exc` parameter is the exception object that was raised. It contains information about the exception, such as its type, message, and traceback :param context: The `context` parameter in the `custom_exception_handler` function is a dictionary that contains information about the current request and view that raised the exception. It typically includes the following keys: :return: a response object. """ try: exception_class = exc.__class__.__name__ handlers = { 'NotAuthenticated': _handler_authentication_error, 'InvalidToken': _handler_invalid_token_error, 'ValidationError': _handler_validation_error, # Add more handlers as needed } res = exception_handler(exc, context) if exception_class in handlers: # calling hanlder based on the custom message = handlers[exception_class](exc, context, res) else: # if there is no hanlder is presnet message = str(exc) return response(data={}, status_code=res.status_code, message=message) except Exception as e: logger.error(str(e)) # ... (other handler functions)
Handling Different Types of Exceptions
This section will showcase you how to handle three types of exceptions: NotAuthenticated, InvalidToken, and ValidationError. You can extend this pattern to handle added exceptions as required.
# exception_handler.py # ... (import statements) # Existing handler functions def _handler_authentication_error(exc, context, response): """ The function returns a message indicating that an authorization token is not provided. :param exc: The `exc` parameter is the exception object that was raised during the authentication process :param context: The `context` parameter is a dictionary that contains additional information about the error that occurred. It can include details such as the request that caused the error, the user who made the request, or any other relevant information :param response: The `response` parameter is the HTTP response object that will be returned to the client. It contains information such as the status code, headers, and body of the response :return: the string "An authorization token is not provided." """ return "An authorization token is not provided." def _handler_invalid_token_error(exc, context, response): """ The function handles an invalid token error by returning a specific error message. :param exc: The `exc` parameter represents the exception that was raised. In this case, it would be an invalid token error :param context: The `context` parameter is a dictionary that contains additional information about the error that occurred. It can include details such as the request that caused the error, the user who made the request, or any other relevant information :param response: The `response` parameter is the HTTP response object that will be returned to the client. It contains information such as the status code, headers, and body of the response :return: the string "An authorization token is not valid." """ return "An authorization token is not valid." def _handler_validation_error(exc, context, response): """ The function `_handler_validation_error` handles validation errors by extracting the error code and value, and returning a custom error message based on the code. :param exc: The `exc` parameter is an exception object that is raised when a validation error occurs. It contains information about the error, such as the field that failed validation and the error code :param context: The `context` parameter is a dictionary that contains additional information about the exception that occurred. It can include details such as the request, view, and args that were being processed when the exception occurred :param response: The `response` parameter is the response object that will be returned by the handler. It is used to modify the response if needed :return: a message based on the validation error code. """ key = list(list(exc.__dict__.values())[0].keys())[0] try: code = list(list(exc.__dict__.values())[0].values())[0][0].__dict__['code'] value = list(list(exc.__dict__.values())[0].values())[0][0] except: code = list(list(exc.__dict__.values())[0].values())[0][0][0].__dict__['code'] value = list(list(exc.__dict__.values())[0].values())[0][0][0] custom_msg_code = ["required", "null", "blank"] if code in custom_msg_code: message = f"{key} field is required" elif code: message = f"{value}" return message # ... (other handler functions)
By default, NotAuthenticated exception leads to a direct response through the HTTP status code “401 Unauthenticated.” However, it may even lead to a “403 Forbidden” response, as per the authentication scheme in practice.
The ValidationError class is fundamentally utilized for field-based validation, and by using validator classes. It is raised while you call for serializer.is_valid with explicit raise_exception keyword argument.
Read More:- Why Should You Choose Python for Web Development?
Implementing the Custom Exception Handler in Django
To integrate the custom exception handler into your Django project, you must update the DRF settings in your settings.py file.
# settings.py # ... (other settings) # Configure the custom exception handler REST_FRAMEWORK = { "EXCEPTION_HANDLER": "path.to.your.exception_handler.custom_exception_handler" } # ... (other settings)
It is significant to note that you should only raise custom exceptions when the error is somewhat that the customer can do something about, for instance, when the customer has used invalid input or other unauthorized requests.
If the error is a server-side problem, like a database connection error, utilizing the built-in exception handling offered by DRF or Django is better.
Read More:- Reasons Why Choose Python As Your Next Web Project
There are several and diverse properties accessible for inspecting the status of an API exception. You can leverage these properties to create custom exception handling for your in-hand and futuristic technology projects.
Conclusion
Custom exception handling can be a commanding tool when leveraged rightly in a Django Rest Framework project. With it, you can offer more comprehensive and useful error messages to your API clients and enhance the user experience of your API.
This blog post explored creating a custom exception handler for Django Rest Framework. We learned how to define a custom exception handler function, handle different types of exceptions with meaningful error messages, and integrate the custom handler into a Django project.
Customizing your exception-handling logic allows you to provide more user-friendly and informative error messages to clients, enhancing the overall user experience of your API. By tailoring error responses to your application’s needs, you can effectively communicate issues and promote a smoother interaction between your application and its users.
Moving Forward with Django App Development
Technostacks is a leading python development company and our teams have a proven experience in working with Django applications. Contact us for your next Django app development project.