71

Instead of doing this:

res = HttpResponse("Unauthorized")
res.status_code = 401
return res

Is there a way to do it without typing it every time?

TIMEX
  • 259,804
  • 351
  • 777
  • 1,080
  • [Kindly use `Unauthenticated`, instead of `Unauthorized` for the status code of 401.](https://stackoverflow.com/a/6937030/6282576) – Amir Shabani Dec 15 '22 at 20:32

5 Answers5

168

I know this is an old one, but it's the top Google result for "django 401", so I thought I'd point this out...

Assuming you've already imported django.http.HttpResponse, you can do it in a single line:

return HttpResponse('Unauthorized', status=401)

The 'Unauthorized' string is optional. Easy.

Stu Cox
  • 4,486
  • 2
  • 25
  • 28
  • 7
    This is passable and I upvoted it; however - for a similar scenario I would consider using error 403 (Forbidden) instead - for that there is a ready mechanism: https://docs.djangoproject.com/en/dev/topics/http/views/#the-403-http-forbidden-view - just define `handler403` in root `urls.py`, and then `raise django.core.exceptions.PermissionDenied`. – Tomasz Gandor Mar 21 '14 at 15:43
  • 2
    @TomaszGandor Yep, or `return HttpResponseForbidden` works for 403s too (raising an exception as you’ve suggested it often more convenient). 401 and 403 aren’t quite the same though, although some argue over the exact difference. – Stu Cox Mar 23 '14 at 17:35
  • According to Wikipedia, the 401 response must include a WWW-Authenticate header field. So, it's simpler to use 403. https://en.wikipedia.org/wiki/List_of_HTTP_status_codes – Simon Steinberger Nov 20 '15 at 20:06
  • 9
    @SimonSteinberger Very true, but there isn’t a standard `WWW-Authenticate` header that any systems will recognise for most auth systems today, so unless you’re using Basic Auth I’d argue this is a pragmatic case to violate the spec. IMO 403 has a separate purpose (i.e. you’ve authenticated but you’re not allowed to access this [403] vs you haven’t authenticated yet [401]) – Stu Cox Jan 07 '16 at 08:23
  • gz for the gold medal @Stu Cox – another Jun 07 '18 at 13:41
  • 1
    I can't think of a use case of 401 without an API, so if anyone has an idea.. And API framework like drf usually include a 401: https://www.django-rest-framework.org/api-guide/exceptions/ – Jean-Xavier Raynaud Jan 03 '20 at 14:29
  • @Jean-XavierRaynaud Why? Because you'd normally redirect to a login page rather than returing a 401? I can't even remember what my use case was for returning a 401 when I wrote this answer! It might have been hand-rolling an API, or some edge case we didn't want to redirect to login for some reason. – Stu Cox Mar 16 '20 at 16:29
  • Does anyone know why django.http.response doesn't have a class for this? It has for other status codes. – java-addict301 Dec 08 '22 at 18:43
  • To answer above questions, 403 is for failed authorization, 401 is for failed authentication per w3 spec. Two very different things. – java-addict301 Dec 08 '22 at 18:47
12
class HttpResponseUnauthorized(HttpResponse):
    status_code = 401

...
return HttpResponseUnauthorized()

Normally, you should set the instance in __init__ or you end up with class variables that are shared between all instances. However, Django does this for you already:

class HttpResponse(object):
    """A basic HTTP response, with content and dictionary-accessed headers."""

    status_code = 200

    def __init__(self, content='', mimetype=None, status=None,
            content_type=None):
        # snip...
        if status:
            self.status_code = status

(see the Django code in context)

Wilfred Hughes
  • 29,846
  • 15
  • 139
  • 192
12

Inheritance solution

from django.http import HttpResponse

class Http401(HttpResponse):
    def __init__(self):
        super().__init__('401 Unauthorized', status=401)

in a util.py to replace multiple calls to:

return HttpResponse('401 Unauthorized', status=401)

with just:

return Http401()

Interestingly there are other named responses in 1.9.6 https://github.com/django/django/blob/1.9.6/django/http/response.py#L443 but not 401.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • 2
    I like this response. But the line of `__init__(self):` should change like this `super(Http401, self).__init__(message, status=401)` – Imju Jun 29 '16 at 23:00
  • @Hobaak In Python 3 we can write just `super().__init__`: http://stackoverflow.com/a/33191175/895245 . A `message` could be added, but if you're not doing APIs, just HTTP browser endpoints, it is not very necessary. – Ciro Santilli OurBigBook.com Jun 30 '16 at 05:15
  • 1
    OK. The solution threw me an error in 1.8.6 Django with 2.7 Python. My suggestion helped go away the issue. Maybe it works with Python 3. I like this solution most out of these answers nevertheless. – Imju Jul 01 '16 at 00:28
  • 1
    You can add to the end of the `__init__`: `self['WWW-Authenticate'] = 'FormBased'` if you want to meet the requirements to include that header when responding with a 401. – Tim Tisdall Jul 03 '17 at 13:54
9
class HttpResponseUnauthorized(HttpResponse):
    def __init__(self):
        self.status_code = 401

...
return HttpResponseUnauthorized()
Glenn Maynard
  • 55,829
  • 10
  • 121
  • 131
  • 2
    You don't need to put it in the `__init__` method, just set the attribute in the class definition. – Matthew Schinckel Dec 05 '10 at 08:01
  • @Matthew: I generally don't do that. It makes it too easy to fall into the unintentionally-shared-object trap, and it's unclear to put some initialization in the class body and some in `__init__`. – Glenn Maynard Dec 05 '10 at 18:57
  • 1
    This returns the following error: `'HttpResponseUnauthorized' object has no attribute '_headers'` – btk Jun 22 '11 at 14:39
  • 3
    from looking at django.http.__init__.py adding it in the class definition seems to be the preferred way to do it, should call HttpRepsonses constructor via super if you wish to do it in __init__ – sjh Jan 23 '12 at 10:46
  • throws object has no attribute '_headers' – Mutant Oct 12 '12 at 04:33
3

Write a view decorator that checks the appropriate HTTP headers and returns the appropriate response (there is no built-in type for response code 401).

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358