236

I am using Flask and I return an XML file from a get request. How do I set the content type to xml ?

e.g.

@app.route('/ajax_ddl')
def ajax_ddl():
    xml = 'foo'
    header("Content-type: text/xml")
    return xml
Community
  • 1
  • 1
Tampa
  • 75,446
  • 119
  • 278
  • 425

8 Answers8

333

Try like this:

from flask import Response
@app.route('/ajax_ddl')
def ajax_ddl():
    xml = 'foo'
    return Response(xml, mimetype='text/xml')

The actual Content-Type is based on the mimetype parameter and the charset (defaults to UTF-8).

Response (and request) objects are documented here: http://werkzeug.pocoo.org/docs/wrappers/

Simon Sapin
  • 9,790
  • 3
  • 35
  • 44
  • 2
    Is it possible to set these and other options on a global level (ie: default)? – earthmeLon Jul 29 '13 at 19:02
  • 13
    @earthmeLon, make a subclass of `flask.Response`, override the `default_mimetype` class attribute, and set that as `app.response_class` http://werkzeug.pocoo.org/docs/wrappers/#werkzeug.wrappers.BaseResponse.default_mimetype http://flask.pocoo.org/docs/api/#flask.Flask.response_class – Simon Sapin Jul 30 '13 at 08:42
  • @earthmeLon: If you set `app.response_class` like Simon points out, remember to use `app.make_response` to get your reponse instance like [pointed out in the answer below](http://stackoverflow.com/a/31751634/110204). – Martin Geisler Aug 05 '15 at 15:52
  • Requests with browsers or postman work fine with this approach, however curl does not work well with the returned Response object. Curl will just print "Found". With curl "return content, status_code, header" seem to work better. – fuma Dec 21 '18 at 09:35
186

As simple as this

x = "some data you want to return"
return x, 200, {'Content-Type': 'text/css; charset=utf-8'}

Update: Use the method below because it will work with both python 2.x and python 3.x and it eliminates the "multiple header" problem (potentially emitting multiple, duplicate headers).

from flask import Response
r = Response(response="TEST OK", status=200, mimetype="application/xml")
r.headers["Content-Type"] = "text/xml; charset=utf-8"
return r
starball
  • 20,030
  • 7
  • 43
  • 238
Harsh Daftary
  • 2,575
  • 1
  • 14
  • 12
51

I like and upvoted @Simon Sapin's answer. I ended up taking a slightly different tack, however, and created my own decorator:

from flask import Response
from functools import wraps

def returns_xml(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        r = f(*args, **kwargs)
        return Response(r, content_type='text/xml; charset=utf-8')
    return decorated_function

and use it thus:

@app.route('/ajax_ddl')
@returns_xml
def ajax_ddl():
    xml = 'foo'
    return xml

I think this is slightly more comfortable.

Michael Wolf
  • 2,179
  • 2
  • 16
  • 14
  • 4
    When returning both a response and a status code like `return 'msg', 200`, this will lead to `ValueError: Expected bytes`. Instead, change the decorator to `return Response(*r, content_type='whatever')`. It will unpack the tuple to arguments. Thank you though, for an elegant solution! – Felix May 28 '19 at 06:11
31

Use the make_response method to get a response with your data. Then set the mimetype attribute. Finally return this response:

@app.route('/ajax_ddl')
def ajax_ddl():
    xml = 'foo'
    resp = app.make_response(xml)
    resp.mimetype = "text/xml"
    return resp

If you use Response directly, you lose the chance to customize the responses by setting app.response_class. The make_response method uses the app.responses_class to make the response object. In this you can create your own class, add make your application uses it globally:

class MyResponse(app.response_class):
    def __init__(self, *args, **kwargs):
        super(MyResponse, self).__init__(*args, **kwargs)
        self.set_cookie("last-visit", time.ctime())

app.response_class = MyResponse  
ti7
  • 16,375
  • 6
  • 40
  • 68
20
from flask import Flask, render_template, make_response
app = Flask(__name__)

@app.route('/user/xml')
def user_xml():
    resp = make_response(render_template('xml/user.html', username='Ryan'))
    resp.headers['Content-type'] = 'text/xml; charset=utf-8'
    return resp
Ryan Liu
  • 499
  • 4
  • 5
  • 2
    I think this answer is important because it makes clear how to change the headers on something from a render_template. – A Hettinger Apr 21 '17 at 20:36
10

You can try the following method(python3.6.2):

case one:

@app.route('/hello')
def hello():

    headers={ 'content-type':'text/plain' ,'location':'http://www.stackoverflow'}
    response = make_response('<h1>hello world</h1>',301)
    response.headers = headers
    return response

case two:

@app.route('/hello')
def hello():

    headers={ 'content-type':'text/plain' ,'location':'http://www.stackoverflow.com'}
    return '<h1>hello world</h1>',301,headers

I am using Flask .And if you want to return json,you can write this:

import json # 
@app.route('/search/<keyword>')
def search(keyword):

    result = Book.search_by_keyword(keyword)
    return json.dumps(result),200,{'content-type':'application/json'}


from flask import jsonify
@app.route('/search/<keyword>')
def search(keyword):

    result = Book.search_by_keyword(keyword)
    return jsonify(result)
zhengGuo
  • 116
  • 1
  • 3
5

Usually you don’t have to create the Response object yourself because make_response() will take care of that for you.

from flask import Flask, make_response                                      
app = Flask(__name__)                                                       

@app.route('/')                                                             
def index():                                                                
    bar = '<body>foo</body>'                                                
    response = make_response(bar)                                           
    response.headers['Content-Type'] = 'text/xml; charset=utf-8'            
    return response

One more thing, it seems that no one mentioned the after_this_request, I want to say something:

after_this_request

Executes a function after this request. This is useful to modify response objects. The function is passed the response object and has to return the same or a new one.

so we can do it with after_this_request, the code should look like this:

from flask import Flask, after_this_request
app = Flask(__name__)

@app.route('/')
def index():
    @after_this_request
    def add_header(response):
        response.headers['Content-Type'] = 'text/xml; charset=utf-8'
        return response
    return '<body>foobar</body>'
lord63. j
  • 4,500
  • 2
  • 22
  • 30
2

When sending files

from flask import send_file

@app.route("/graph", methods = ['GET'])
def grafh():
    return send_file('graph.png', mimetype='image/png', as_attachment=False)

Change as_attachment if you want it previewed or as download

Punnerud
  • 7,195
  • 2
  • 54
  • 44