tstr
tstr

Reputation: 1276

Django rest framework custom 404 error page

How to customize Django rest framework error page. Can't find about it. In my rest action code:

from django.http import Http404

class SomeAction(APIView):
    def get(self, *args, **kwargs):
        raise Http404()

I need to display custom error page in prod.

Upvotes: 4

Views: 3858

Answers (2)

Carson
Carson

Reputation: 8028

TL;DR

class YourAPIView(APIView):
    @staticmethod
    def my_exception_handler(exc, context):
        response = exception_handler(exc, context)
        if if response.status_code >= 400:
            return HttpResponse(loader.get_template('your.html').render(context), content_type='text/html')
        return response

    def get_exception_handler(self):
        return self.my_exception_handler

Long story

This link (custom-exception-handling) tells you to set EXCEPTION_HANDLER to yours, and you will notice that the default settings is rest_framework.views.exception_handler

Of course, you can follow the tutorial, but if you don't want to use global settings to control, then see the following.

I suggest you set breakpoints on the function (rest_framework.views.exception_handler) to see what is going on, and then you will know all proceeding.

If you don't want to debug, I list the key points below.

# rest_framework\views.py

class APIView(View):
    ...

    def dispatch(self, request, *args, **kwargs):
        ...
        try:
            ...
        except Exception as exc:
            response = self.handle_exception(exc)  # <-- focus here
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
    
     def handle_exception(self, exc):
        ...
        exception_handler = self.get_exception_handler()  # <-- focus here
    
        context = self.get_exception_handler_context()
        response = exception_handler(exc, context)
        ...
        return response
    
    def get_exception_handler(self):
        return self.settings.EXCEPTION_HANDLER

From the above code, you know you can change get_exception_handler then all down!

here is an example:

# views.py

from rest_framework import generics
from django.http import HttpResponse
from rest_framework import status
from django.template import loader
from rest_framework.views import exception_handler


class YourAPIView(generics.ListAPIView):
    ...
    def get(self, request, *args, **kwargs):
        return HttpResponse('ok', content_type='text/html')

    @staticmethod
    def my_exception_handler(exc, context):
        response = exception_handler(exc, context)  # <-- this is the default exception_handler
        if response.status_code in (status.HTTP_400_BAD_REQUEST, status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN):
            my_400_and_403_template = loader.get_template('error.html')  #  settings.py -> TEMPLATES = [{'DIRS': [ str(Path(BASE_DIR)/Path('templates/your_custom_dir')).replace('\\', '/') ]}]  # Where your_custom_dir should contain error.html
            return HttpResponse(my_400_and_403_template.render(context), content_type='text/html')
        return response

    def get_exception_handler(self):
        return self.my_exception_handler

    # def handle_exception(self, exc):
    #    return super().handle_exception(exc)

Upvotes: 2

tstr
tstr

Reputation: 1276

from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.template import loader, RequestContext, Context
from apps.settings import DEBUG
from libs.requestprovider.middleware import get_current_request

from rest_framework.views import exception_handler



def custom_exception_handler(exc):
    # Call REST framework's default exception handler first,
    # to get the standard error response.
    response = exception_handler(exc)

    # Now add the HTTP status code to the response.
    if response is not None:
        response.data['status_code'] = response.status_code

    if not DEBUG:
        #errors can be more generic
        if response.data['status_code'] == 404:
            t = loader.get_template('404.html')
            c = RequestContext(get_current_request(), {})
            return HttpResponse(t.render(c), content_type="text/html")
    return response

Upvotes: 3

Related Questions