如何在Flask中设置响应头?

157

这是我的代码:

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

然而,当我从浏览器向我的服务器发出请求时,我会收到这个错误:

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

我也尝试过这种方法,即在请求之后设置响应头:

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

不行。我得到了同样的错误。有没有办法只在路由函数中设置响应头呢?像这样的做法会更理想:

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

但我找不到任何方法来做到这一点。请帮忙。

编辑

如果我使用POST请求来curl URL,像这样:

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

我收到了这个回复:

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>

有任何想法吗?


我建议使用中间件来解决这个问题,或者只是使用 Flask-CORS 扩展。 - Night King
6个回答

151

你可以很容易地做到这一点:

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

看看 flask.Responseflask.make_response()

但是有些事情告诉我你还有另一个问题,因为after_request也应该正确处理它。

编辑
我刚刚注意到你已经在使用make_response,这是其中一种方法。就像我之前所说的,after_request也应该可以工作。尝试使用curl访问端点并查看标头内容:

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

你应该看到

> 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

注意Access-Control-Allow-Origin头部。

编辑2
正如我猜测的那样,你收到了500错误,所以你没有像你想的那样设置头部。尝试在启动应用程序之前添加app.debug = True并再次尝试。你应该会得到一些输出,显示问题的根本原因。

例如:

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

提供一个漂亮格式化的HTML错误页面,底部包含此内容(对于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

我已经从响应头中将 Server = '' 删除,但仍然可以看到两个键值,Server: Werkzeug/0.8.3 Python/2.7.5 和 Server: 。 有什么建议吗? - rohan_tare

39

使用Flask的make_response可以做如下操作:

@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

来自 Flask 文档,

flask.make_response(*args)

有时需要在视图中设置其他标头。由于视图不必返回响应对象,但可以返回由 Flask 自身转换为响应对象的值,因此向其添加标头变得棘手。您可以调用此函数而不是使用 return,这样您就会获得一个响应对象,您可以使用它来附加标题。


你可以在args中发送请求:http://flask.pocoo.org/docs/0.10/api/#flask.Flask.make_response - tokland

21

这就是我在Flask应用程序中添加标题的方法,它完美地工作了。

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

18

根据文档,你可以在视图函数中返回响应和头部信息。

如果返回一个元组,则元组中的项可以提供额外的信息。这样的元组必须是以下形式之一:(response, status)(response, headers) 或者 (response, status, headers)。状态值将覆盖状态码,而headers可以是附加标头值的列表或字典。

例如:

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

或者:

@app.route('/hello', methods=["POST"])
def hello():
   return {"foo": "bar"}, 200, {"Access-Control-Allow-Origin": "*"}

12

这对我有效

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()

4
我认为这个标记 return Response(headers={'Access-Control-Allow-Origin':'*'}) 看起来更简洁。 - Hermann

1
我们可以使用Flask应用程序上下文中的flask.g设置Python Flask应用程序的响应头。
使用flask.g在Flask应用程序上下文中设置响应头的方式是线程安全的,可以用于从应用程序的任何文件设置自定义和动态属性。如果我们从任何助手类设置自定义/动态响应头,这尤其有帮助,也可以从任何其他文件(例如中间件等)访问该类,这个flask.g是全局且仅对该请求线程有效。
比如说,如果我想从此应用程序调用的另一个API / HTTP调用中读取响应头,然后将其提取并设置为此应用程序的响应头。
示例代码:文件: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)

文件: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)

从主类调用中间件

文件: 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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接