Python Flask, jak ustawić typ zawartości

176

Używam Flask i zwracam plik XML z żądania get. Jak ustawić typ zawartości na xml?

na przykład

@app.route('/ajax_ddl')
def ajax_ddl():
    xml = 'foo'
    header("Content-type: text/xml")
    return xml
Tampa
źródło

Odpowiedzi:

255

Spróbuj tak:

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

Rzeczywisty typ zawartości jest oparty na parametrze mimetype i zestawie znaków (domyślnie UTF-8).

Obiekty odpowiedzi (i żądania) są udokumentowane tutaj: http://werkzeug.pocoo.org/docs/wrappers/

Simon Sapin
źródło
1
Czy można ustawić te i inne opcje na poziomie globalnym (tj. Domyślne)?
earthmeLon
10
@earthmeLon, zrób podklasę flask.Response, nadpisać default_mimetypeatrybut class i ustaw, które jako app.response_class werkzeug.pocoo.org/docs/wrappers/... flask.pocoo.org/docs/api/#flask.Flask.response_class
Simon Sapin
@earthmeLon: Jeśli ustawisz app.response_classtak, jak wskazuje Simon, pamiętaj, aby użyć app.make_responseinstancji odpowiedzi, jak wskazano w odpowiedzi poniżej .
Martin Geisler
Żądania z przeglądarkami lub listonoszem działają dobrze w tym podejściu, jednak curl nie działa dobrze z zwróconym obiektem Response. Curl po prostu wypisze „Znaleziono”. Wydaje się, że z curl „zwracana zawartość, kod_stanu, nagłówek” działa lepiej.
fuma
144

Tak proste

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

Mam nadzieję, że to pomoże

Aktualizacja: użyj tej metody, ponieważ będzie działać zarówno z pythonem 2.x, jak i pythonem 3.x

a po drugie eliminuje również problem z wieloma nagłówkami.

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
Harsh Daftary
źródło
15
Najprostsze rozwiązanie. Zdecydowanie powinna być akceptowana odpowiedź
Omer Dagan
Jest wada: pozwala tylko na DODAWANIE nagłówków. Kiedy to zrobiłem, w odpowiedzi otrzymałem dwa nagłówki Content-Type - domyślny i dodany.
omikron
1
@omikron Zaktualizowałem odpowiedź, wypróbuj nową metodę, która powinna działać.
Harsh Daftary
48

Podoba mi się odpowiedź @Simon Sapin. Skończyło się jednak na nieco innej takcie i stworzyłem własnego dekoratora:

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

i użyj go w ten sposób:

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

Myślę, że jest to nieco wygodniejsze.

Michael Wolf
źródło
3
W przypadku zwrócenia zarówno odpowiedzi, jak i kodu stanu, takiego jak return 'msg', 200, spowoduje to ValueError: Expected bytes. Zamiast tego zmień dekorator na return Response(*r, content_type='whatever'). Rozpakuje krotkę do argumentów. Dziękuję jednak za eleganckie rozwiązanie!
Felix
24

Użyj metody make_response, aby uzyskać odpowiedź ze swoimi danymi. Następnie ustaw atrybut MIME . Na koniec zwróć tę odpowiedź:

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

Jeśli używasz Responsebezpośrednio, tracisz szansę na dostosowanie odpowiedzi przez ustawienie app.response_class. make_responseMetoda wykorzystuje app.responses_class, aby obiekt odpowiedzi. W tym możesz stworzyć własną klasę, dodać, aby aplikacja używała jej globalnie:

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
źródło
Jest to zasadniczo zaakceptowana odpowiedź @ SimonSapin, przepakowana.
J0e3gan
@ J0e3gan dzięki. Rozszerzyłem moją odpowiedź, aby lepiej wyjaśnić, dlaczego używanie make_responsejest lepsze niż używanieResponse
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
źródło
2
Myślę, że ta odpowiedź jest ważna, ponieważ wyjaśnia, jak zmienić nagłówki w czymś z render_template.
A Hettinger
5

Zwykle nie musisz samodzielnie tworzyć Responseobiektu, ponieważmake_response() zajmie się tym za Ciebie.

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

Jeszcze jedno, wydaje się, że nikt nie wspomniał o after_this_request, chcę coś powiedzieć:

after_this_request

Wykonuje funkcję po tym żądaniu. Jest to przydatne do modyfikowania obiektów odpowiedzi. Funkcja otrzymuje obiekt odpowiedzi i musi zwrócić ten sam lub nowy.

więc możemy to zrobić after_this_request, kod powinien wyglądać tak:

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. jot
źródło
4

Możesz wypróbować następującą metodę (python3.6.2) :

przypadek pierwszy :

@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

przypadek drugi :

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

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

Używam Flaska. Jeśli chcesz zwrócić json, możesz napisać to:

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
źródło