Python Flask, como configurar el tipo de contenido

176

Estoy usando Flask y devuelvo un archivo XML de una solicitud de obtención. ¿Cómo configuro el tipo de contenido en xml?

p.ej

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

Respuestas:

255

Intenta así:

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

El tipo de contenido real se basa en el parámetro mimetype y el juego de caracteres (el valor predeterminado es UTF-8).

Los objetos de respuesta (y solicitud) se documentan aquí: http://werkzeug.pocoo.org/docs/wrappers/

Simon Sapin
fuente
1
¿Es posible establecer estas y otras opciones a nivel global (es decir, predeterminado)?
earthmeLon
10
@earthmeLon, cree una subclase de flask.Response, anule el default_mimetypeatributo de clase y app.response_class configúrelo como werkzeug.pocoo.org/docs/wrappers/… flask.pocoo.org/docs/api/#flask.Flask.response_class
Simon Sapin
@earthmeLon: Si configura app.response_classcomo Simon señala, recuerde utilizar app.make_responsepara obtener su instancia de respuesta como se indica en la respuesta a continuación .
Martin Geisler
Las solicitudes con navegadores o carteros funcionan bien con este enfoque, sin embargo, curl no funciona bien con el objeto de respuesta devuelto. Curl simplemente imprimirá "Encontrado". Con curl "devolver contenido, status_code, encabezado" parece funcionar mejor.
fuma
144

Tan simple como esto

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

Espero eso ayude

Actualización: utilice este método porque funcionará con python 2.xy python 3.x

y en segundo lugar también elimina el problema de múltiples encabezados.

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 duro
fuente
15
La solución más simple. Definitivamente debería ser la respuesta aceptada
Omer Dagan
Hay un inconveniente: solo le permite AGREGAR encabezados. Cuando lo hice, terminé con dos encabezados de tipo contenido en respuesta: uno predeterminado y agregué uno.
omikron
1
@omikron He actualizado la respuesta, pruebe el nuevo método que debería funcionar.
Harf Daftary
48

Me gusta y voté por la respuesta de @Simon Sapin. Sin embargo, terminé tomando una táctica ligeramente diferente y creé mi propio decorador:

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

y úsalo así:

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

Creo que esto es un poco más cómodo.

Michael Wolf
fuente
3
Al devolver una respuesta y un código de estado como return 'msg', 200, esto conducirá a ValueError: Expected bytes. En cambio, cambie el decorador a return Response(*r, content_type='whatever'). Descomprimirá la tupla en argumentos. ¡Sin embargo, gracias por una solución elegante!
Felix
24

Use el método make_response para obtener una respuesta con sus datos. Luego establezca el atributo mimetype . Finalmente devuelva esta respuesta:

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

Si usa Responsedirectamente, pierde la oportunidad de personalizar las respuestas mediante la configuración app.response_class. El make_responsemétodo utiliza el app.responses_classpara hacer el objeto de respuesta. En esto, puede crear su propia clase, agregar hacer que su aplicación la use globalmente:

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
fuente
Esta es esencialmente la respuesta aceptada de @SimonSapin reempaquetada.
J0e3gan
@ J0e3gan gracias. He ampliado mi respuesta para explicar mejor por qué usar make_responsees mejor que usarResponse
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
fuente
2
Creo que esta respuesta es importante porque deja en claro cómo cambiar los encabezados en algo desde una plantilla de renderizado.
Un Hettinger
5

Por lo general, no tiene que crear el Responseobjeto usted mismo porque make_response()se encargará de eso por usted.

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

Una cosa más, parece que nadie mencionó el after_this_request, quiero decir algo:

after_this_request

Ejecuta una función después de esta solicitud. Esto es útil para modificar objetos de respuesta. La función pasa el objeto de respuesta y tiene que devolver el mismo o uno nuevo.

para que podamos hacerlo after_this_request, el código debería verse así:

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>'
señor63. j
fuente
4

Puede probar el siguiente método (python3.6.2):

caso uno :

@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

caso dos:

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

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

Estoy usando Flask. Y si quieres devolver json, puedes escribir esto:

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
fuente