Come si impostano le intestazioni di risposta in Flask?


99

Questo è il mio codice:

@app.route('/hello', methods=["POST"])
def hello():
    resp = make_response()
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

Tuttavia, quando effettuo una richiesta dal browser al mio server ottengo questo errore:

XMLHttpRequest cannot load http://localhost:5000/hello. 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

Ho anche provato questo approccio, impostando le intestazioni di risposta "dopo" la richiesta:

@app.after_request
def add_header(response):
    response.headers['Access-Control-Allow-Origin'] = '*'
    return response

Niente da fare. Ho fatto lo stesso errore. C'è un modo per impostare solo le intestazioni di risposta nella funzione route? Qualcosa di simile sarebbe l'ideale:

@app.route('/hello', methods=["POST"])
    def hello(response): # is this a thing??
        response.headers['Access-Control-Allow-Origin'] = '*'
        return response

ma non riesco a trovare comunque per farlo. Per favore aiuto.

MODIFICARE

se arriccio l'URL con una richiesta POST in questo modo:

curl -iX POST http://localhost:5000/hello

Ottengo questa risposta:

HTTP/1.0 500 INTERNAL SERVER ERROR
Content-Type: text/html
Content-Length: 291
Server: Werkzeug/0.9.6 Python/2.7.6
Date: Tue, 16 Sep 2014 03:58:42 GMT

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request.  Either the server is overloaded or there is an error in the application.</p>

Qualche idea?

Risposte:


96

Puoi farlo abbastanza facilmente:

@app.route("/")
def home():
    resp = flask.Response("Foo bar baz")
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

Guarda flask.Response e flask.make_response ()

Ma qualcosa mi dice che hai un altro problema, perché anche tu after_requestavrebbe dovuto gestirlo correttamente.

MODIFICA
Ho appena notato che stai già usando make_responseche è uno dei modi per farlo. Come ho detto prima, after_requestavrebbe dovuto funzionare anche lui. Prova a raggiungere l'endpoint tramite curl e guarda quali sono le intestazioni:

curl -i http://127.0.0.1:5000/your/endpoint

Tu dovresti vedere

> curl -i 'http://127.0.0.1:5000/'
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 11
Access-Control-Allow-Origin: *
Server: Werkzeug/0.8.3 Python/2.7.5
Date: Tue, 16 Sep 2014 03:47:13 GMT

Notando l'intestazione Access-Control-Allow-Origin.

EDIT 2
Come sospettavo, stai ottenendo un 500 quindi non stai impostando l'intestazione come pensavi. Prova ad aggiungere app.debug = Trueprima di avviare l'app e riprova. Dovresti ottenere un output che ti mostra la causa principale del problema.

Per esempio:

@app.route("/")
def home():
    resp = flask.Response("Foo bar baz")
    user.weapon = boomerang
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

Fornisce una pagina di errore html ben formattata, con questa in basso (utile per il comando curl)

Traceback (most recent call last):
...
  File "/private/tmp/min.py", line 8, in home
    user.weapon = boomerang
NameError: global name 'boomerang' is not defined

24

Uso make_responsedi Flask qualcosa di simile

@app.route("/")
def home():
    resp = make_response("hello") #here you could use make_response(render_template(...)) too
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

Da flask docs ,

flask.make_response (* args)

A volte è necessario impostare intestazioni aggiuntive in una vista. Poiché le viste non devono restituire oggetti di risposta ma possono restituire un valore che viene convertito in un oggetto di risposta da Flask stesso, diventa complicato aggiungere intestazioni ad esso. Questa funzione può essere chiamata invece di usare un ritorno e otterrai un oggetto risposta che puoi usare per allegare intestazioni.


È possibile inviare le richieste negli argomenti: flask.pocoo.org/docs/0.10/api/#flask.Flask.make_response
tokland

5

Questo lavoro per me

from flask import Flask
from flask import Response

app = Flask(__name__)

@app.route("/")
def home():
    return Response(headers={'Access-Control-Allow-Origin':'*'})

if __name__ == "__main__":
    app.run()

2
C'è anche la notazione return Response(headers={'Access-Control-Allow-Origin':'*'})che mi sembra più pulita.
Hermann

4

Questo è stato il modo in cui ho aggiunto le mie intestazioni nella mia applicazione flask e ha funzionato perfettamente

@app.after_request
def add_header(response):
    response.headers['X-Content-Type-Options'] = 'nosniff'
    return response

0

Possiamo impostare le intestazioni di risposta nell'applicazione Python Flask usando il contesto dell'applicazione Flask usando flask.g

Questo modo di impostare le intestazioni di risposta nel contesto dell'applicazione Flask utilizzando flask.gè thread-safe e può essere utilizzato per impostare attributi personalizzati e dinamici da qualsiasi file dell'applicazione, questo è particolarmente utile se stiamo impostando intestazioni di risposta personalizzate / dinamiche da qualsiasi classe helper, che può accessibile anche da qualsiasi altro file (ad esempio middleware, ecc.), questo flask.gè globale e valido solo per quel thread di richiesta.

Di 'se voglio leggere l'intestazione della risposta da un'altra chiamata api / http che viene chiamata da questa app, quindi estrarre qualsiasi e impostarla come intestazioni di risposta per questa app.

Codice di esempio: file: helper.py

import flask
from flask import request, g
from multidict import CIMultiDict
from asyncio import TimeoutError as HttpTimeout
from aiohttp import ClientSession

    def _extract_response_header(response)
      """
      extracts response headers from response object 
      and stores that required response header in flask.g app context
      """
      headers = CIMultiDict(response.headers)
      if 'my_response_header' not in g:
        g.my_response_header= {}
        g.my_response_header['x-custom-header'] = headers['x-custom-header']


    async def call_post_api(post_body):
      """
      sample method to make post api call using aiohttp clientsession
      """
      try:
        async with ClientSession() as session:
          async with session.post(uri, headers=_headers, json=post_body) as response:
            responseResult = await response.read()
            _extract_headers(response, responseResult)
            response_text = await response.text()
      except (HttpTimeout, ConnectionError) as ex:
        raise HttpTimeout(exception_message)

file: middleware.py

import flask
from flask import request, g

class SimpleMiddleWare(object):
    """
    Simple WSGI middleware
    """

    def __init__(self, app):
        self.app = app
        self._header_name = "any_request_header"

    def __call__(self, environ, start_response):
        """
        middleware to capture request header from incoming http request
        """
        request_id_header = environ.get(self._header_name)
        environ[self._header_name] = request_id_header

        def new_start_response(status, response_headers, exc_info=None):
            """
            set custom response headers
            """
            # set the request header as response header
            response_headers.append((self._header_name, request_id_header))
            # this is trying to access flask.g values set in helper class & set that as response header
            values = g.get(my_response_header, {})
            if values.get('x-custom-header'):
                response_headers.append(('x-custom-header', values.get('x-custom-header')))
            return start_response(status, response_headers, exc_info)

        return self.app(environ, new_start_response)

Chiamare il middleware dalla classe principale

file : main.py

from flask import Flask
import asyncio
from gevent.pywsgi import WSGIServer
from middleware import SimpleMiddleWare

    app = Flask(__name__)
    app.wsgi_app = SimpleMiddleWare(app.wsgi_app)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.