Python Flask, comment définir le type de contenu

176

J'utilise Flask et je renvoie un fichier XML à partir d'une demande get. Comment définir le type de contenu sur xml?

par exemple

@app.route('/ajax_ddl')
def ajax_ddl():
    xml = 'foo'
    header("Content-type: text/xml")
    return xml
Tampa
la source

Réponses:

255

Essayez comme ceci:

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

Le Content-Type réel est basé sur le paramètre mimetype et le charset (par défaut UTF-8).

Les objets de réponse (et de requête) sont documentés ici: http://werkzeug.pocoo.org/docs/wrappers/

Simon Sapin
la source
1
Est-il possible de définir ces options et d'autres au niveau global (par exemple: par défaut)?
earthmeLon
10
@earthmeLon, créez une sous-classe de flask.Response, remplacez l' default_mimetypeattribut de classe et définissez-la comme app.response_class werkzeug.pocoo.org/docs/wrappers/… flask.pocoo.org/docs/api/#flask.Flask.response_class
Simon Sapin
@earthmeLon: Si vous définissez app.response_classcomme le souligne Simon, n'oubliez pas d'utiliser app.make_responsepour obtenir votre instance de réponse comme indiqué dans la réponse ci-dessous .
Martin Geisler
Les requêtes avec les navigateurs ou le facteur fonctionnent correctement avec cette approche, mais curl ne fonctionne pas correctement avec l'objet Response renvoyé. Curl affichera simplement "Trouvé". Avec curl "return content, status_code, header" semble mieux fonctionner.
fuma
144

Aussi simple que ça

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

J'espère que ça aide

Mise à jour: utilisez cette méthode car elle fonctionnera avec python 2.x et python 3.x

et deuxièmement, il élimine également le problème d'en-tête multiple.

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
Daftary dur
la source
15
La solution la plus simple. La réponse devrait certainement être acceptée
Omer Dagan
Il y a un inconvénient: il vous permet uniquement d'ajouter des en-têtes. Quand je l'ai fait, je me suis retrouvé avec deux en-têtes Content-Type en réponse - un par défaut et en ai ajouté un.
omikron
1
@omikron J'ai mis à jour la réponse, essayez la nouvelle méthode qui devrait fonctionner.
Harsh Daftary
48

J'aime et j'ai voté pour la réponse de @Simon Sapin. J'ai fini par adopter une approche légèrement différente, cependant, et j'ai créé mon propre décorateur:

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

et utilisez-le ainsi:

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

Je pense que c'est un peu plus confortable.

Michael Wolf
la source
3
Lorsque vous retournez à la fois une réponse et un code d'état comme return 'msg', 200, cela conduira à ValueError: Expected bytes. Au lieu de cela, changez le décorateur en return Response(*r, content_type='whatever'). Il décompressera le tuple en arguments. Merci cependant pour une solution élégante!
Felix
24

Utilisez la méthode make_response pour obtenir une réponse avec vos données. Puis définissez l' attribut mimetype . Enfin renvoyez cette réponse:

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

Si vous utilisez Responsedirectement, vous perdez la possibilité de personnaliser les réponses en définissant app.response_class. La make_responseméthode utilise app.responses_classpour créer l'objet de réponse. En cela, vous pouvez créer votre propre classe, ajouter pour que votre application l'utilise globalement:

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  
Marianna Vassallo
la source
C'est essentiellement la réponse acceptée de @ SimonSapin reconditionnée.
J0e3gan
@ J0e3gan merci. J'ai élargi ma réponse pour mieux expliquer pourquoi il make_responsevaut mieux utiliser que d'utiliserResponse
Marianna Vassallo
14
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
la source
2
Je pense que cette réponse est importante car elle explique clairement comment changer les en-têtes sur quelque chose à partir d'un render_template.
A Hettinger le
5

Habituellement, vous n'êtes pas obligé de créer l' Responseobjet vous-même, car vous make_response()en prendrez soin à votre place.

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

Encore une chose, il semble que personne n'ait mentionné le after_this_request, je veux dire quelque chose:

after_this_request

Exécute une fonction après cette demande. Ceci est utile pour modifier les objets de réponse. La fonction reçoit l'objet de réponse et doit renvoyer le même ou un nouveau.

pour que nous puissions le faire avec after_this_request, le code devrait ressembler à ceci:

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
la source
4

Vous pouvez essayer la méthode suivante (python3.6.2) :

cas un :

@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

cas deux :

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

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

J'utilise Flask .Et si vous voulez retourner json, vous pouvez écrire ceci:

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
la source