How to return a relative URI Location header with Flask?

Guillaume Vincent picture Guillaume Vincent · Mar 26, 2014 · Viewed 10.3k times · Source

Flask replaces the content of my Location header when building my HTTP response. It change my actual relative URI Location header with an absolute one.

@app.route('/votes', methods=['POST'])
def votes():
    return jsonify(), 201, {'location': '/votes/1'}

my test :

def test_vote_creation(self):
    response = self.app.post('/votes',
                             data=json.dumps({
                                 'name': 'Test vote'
                             }), content_type='application/json')
    print(response.headers['location'])

return http://localhost/votes/1 instead of /votes/1

How to return a relative URI Location header with Flask jsonify ?

Edit: According to the current version of the HTTP/1.1 standard, RFC 2616, the value of the Location header must be an absolute URI. But the RCF is going to change to allow relative URIs as well. So I want to change the default behaviour of my API to answer with a relative URI in my location header.

more detail on this post

Answer

Martijn Pieters picture Martijn Pieters · Mar 28, 2014

The HTTP RFC specifies that the Location header must be an absolute URI:

14.30 Location

The Location response-header field is used to redirect the recipient to a location other than the Request-URI for completion of the request or identification of a new resource. For 201 (Created) responses, the Location is that of the new resource which was created by the request. For 3xx responses, the location SHOULD indicate the server's preferred URI for automatic redirection to the resource. The field value consists of a single absolute URI.

Location       = "Location" ":" absoluteURI

As such the Flask / Werkzeug response object converts any relative URL Location header to an an absolute URL.

You can override this behaviour, although I would not recommend you do so. To override it, set the autocorrect_location_header attribute of a Response object to False. jsonify() returns a response object, alter that:

@app.route('/votes', methods=['POST'])
def votes():
    response = jsonify()
    response.status_code = 201
    response.headers['location'] = '/votes/1'
    response.autocorrect_location_header = False
    return response

but note that your WSGI server can still enforce an absolute URL even if Flask doesn't.