Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flask: return error code 401.x

Tags:

python

flask

I'd like to notify my client that there was a login failure by returning the error code 401.1 from my Flask based REST API.

Flask is fine with something like this:

return {"message": "Invalid username or password"}, 401

How do I return a HTTP 401.1 instead of a 401?

like image 725
sisanared Avatar asked Jul 06 '26 11:07

sisanared


1 Answers

You can't out of the box. The Werkzeug response object used in the Flask stack will always treat the status code as an integer.

That's because the HTTP RFCs are very clear on what values are permitted for a status code:

The status-code element is a 3-digit integer code describing the result of the server's attempt to understand and satisfy the client's corresponding request.

Please tripple-check your requirements with your client before you go break the RFC.

The addition of a .x sub-status number is a Microsoft IIS extension that is used only internally to the server. The server sends a 401 status, but logs a detail substatus code in the logs and lets you vary the custom error message based on the sub-code. So the end user sees 401.1 Logon failed in the error page served, but the status line is still set to HTTP/1.1 401 Unauthorized. See this post by a Microsoft engineer:

Please note that there is no such thing as a 401.1 when it comes to the HTTP headers themselves. (Watch using a network sniffer to confirm for yourself).

When you see a 401.1 in IE, what's really happening is that IIS is sending down a HTTP/401 error code in that HTTP Result, but the body text provides the additional information you're seeing that claims it's really a 401.1

If you absolutely must send such a status (and break RFC compliance), you'd have to subclass the Response class used by Flask:

from flask import Response
from werkzeug.http import HTTP_STATUS_CODES

class ResponseWithSubstatus(Response):
    @property
    def status_code(self):
        """The HTTP Status code as number"""
        return self._status_code

    @status_code.setter
    def status_code(self, code):
        self._status_code = code
        try:
            main_code = code
            if isinstance(code, str) and '.' in code:
                main_code = int(code.partition('.')[0])
            self._status = '%s %s' % (code,    
                HTTP_STATUS_CODES[main_code].upper())
        except (ValueError, KeyError):
            self._status = '%s UNKNOWN' % code

    @property
    def status(self):
        """The HTTP Status code"""
        return self._status

    @status.setter
    def status(self, value):
        self._status = value
        self._status_code = self._status.split(None, 1)[0]
        try:
            self._status_code = int(self.status_code)
        except ValueError:
            pass

then use that class as the response_class attribute on your Flask() object:

from flask import Flask

app = Flask(...)
app.response_object = ResponseWithSubstatus

The above changes remove the requirement that status_code is an integer.

Note that it may still be possible for your WSGI server to choke on this! Different WSGI servers may handle a non-conformant status line better or worse.

like image 100
Martijn Pieters Avatar answered Jul 12 '26 01:07

Martijn Pieters



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!