Django: How to raise Http401 and Http403 exceptions like Http404, RAISE EXCEPTION not RESPONSE

Question:

I’m trying to make an api using Django, and i’m verifying the request header if it contains an api key and raise an exception according to that, like so:

def _check_driver_authorization(request):
if request.headers.get('authorization') is not None:
    token = request.headers['authorization']
    user = Driver.objects.filter(access_token=token)
    if user.exists():
        return

    raise Http401
else:
    raise Http403

I didn’t find anyone trying to make it like this, and i’ve searched many threads in here, they are all trying to return responses (render), my case i’m trying to interupt the request and raise the exception.
I inspired this from get_object_or_404.

Edit/update:
for more details and explanation, the built-in Http404 exception raises this:
Http404

but the exception i tried to make (exactly the same as Http404), raises this:
Http403

Asked By: wassim chaguetmi

||

Answers:

There are handler and exception to response converter which allows you to do a raise Http404. As you can see they also convert PermissionDenied exception to response with 403 status code. So you can raise it instead of Http403. But for 401 you have to return return HttpResponse('Unauthorized', status=401) something like that.

Answered By: Davit Tovmasyan

This is the piece of Django code handling your exceptions:

def response_for_exception(request, exc):
    if isinstance(exc, Http404):
        if settings.DEBUG:
            response = debug.technical_404_response(request, exc)
        else:
            response = get_exception_response(request, get_resolver(get_urlconf()), 404, exc)

    elif isinstance(exc, PermissionDenied):
        logger.warning(
            'Forbidden (Permission denied): %s', request.path,
            extra={'status_code': 403, 'request': request},
        )
        response = get_exception_response(request, get_resolver(get_urlconf()), 403, exc)

    elif isinstance(exc, MultiPartParserError):
        logger.warning(
            'Bad request (Unable to parse request body): %s', request.path,
            extra={'status_code': 400, 'request': request},
        )
        response = get_exception_response(request, get_resolver(get_urlconf()), 400, exc)

    elif isinstance(exc, SuspiciousOperation):
        if isinstance(exc, (RequestDataTooBig, TooManyFieldsSent)):
            # POST data can't be accessed again, otherwise the original
            # exception would be raised.
            request._mark_post_parse_error()

        # The request logger receives events for any problematic request
        # The security logger receives events for all SuspiciousOperations
        security_logger = logging.getLogger('django.security.%s' % exc.__class__.__name__)
        security_logger.error(
            force_text(exc),
            extra={'status_code': 400, 'request': request},
        )
        if settings.DEBUG:
            response = debug.technical_500_response(request, *sys.exc_info(), status_code=400)
        else:
            response = get_exception_response(request, get_resolver(get_urlconf()), 400, exc)

    elif isinstance(exc, SystemExit):
        # Allow sys.exit() to actually exit. See tickets #1023 and #4701
        raise

    else:
        signals.got_request_exception.send(sender=None, request=request)
        response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())

    # Force a TemplateResponse to be rendered.
    if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)):
        response = response.render()

    return response

As you can see here, Django will handle Http404 and PermissionDenied exceptions. It seems there’s no exception for an Http 401 response.

PermissionDenied (Http 403) exception is the following one (similar to Http404 exception):

class PermissionDenied(Exception):
    """The user did not have permission to do that"""
    pass

So it should work with a raise PermissionDenied.

Django docs: Error views

Answered By: Hagyn

If you are using django rest framework, you can inherit from APIException like so:

from rest_framework.exceptions import APIException

class UnauthorizedException(APIException):
    status_code = 401
    default_detail = "Not logged in"

and then for example in your view or wherever you can do like

    if request.user.is_anonymous:
        raise UnauthorizedException

The caller then gets a 401.

Answered By: jjjcomputers
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.