热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

修改Flask的默认响应头实现跨域(CORS)支持

本站文章除注明转载外,均为本站原创或者翻译。本站文章欢迎各种形式的转载,但请18岁以上的转载者注明文章出处,尊重我的劳动,也尊重你的智商;本站部分原创和翻译文章提供markdown




  • 本站文章除注明转载外,均为本站原创或者翻译。

  • 本站文章欢迎各种形式的转载,但请18岁以上的转载者注明文章出处,尊重我的劳动,也尊重你的智商;


  • 本站部分原创和翻译文章提供markdown格式源码,欢迎使用 文章源码
    进行转载;


  • 本博客采用WPCMD 维护;

  • 本文标题:修改 Flask 的默认响应头实现跨域(CORS)支持


  • 本文链接: http://zengrong.net/post/2615.htm



要提供一个 RESTful API ,就必须考虑 跨域请求(CORS)
问题。在 Flask 中,我们可以简单得进行这样的处理:


@main.route('/', methods=['GET'])
def index():
resp = jsonify({'error':False})
# 跨域设置
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp

当路由较多的时候,这样写未免太不优雅,我们可以封装一个方法 responseto 用来代替 jsonify:


def responseto(message=None, error=None, data=None, **kwargs):
""" 封装 json 响应
"""
# 如果提供了 data,那么不理任何其他参数,直接响应 data
if not data:
data = kwargs
data['error'] = error
if message:
# 除非显示提供 error 的值,否则默认为 True
# 意思是提供了 message 就代表有 error
data['message'] = message
if error is None:
data['error'] = True
else:
# 除非显示提供 error 的值,否则默认为 False
# 意思是没有提供 message 就代表没有 error
if error is None:
data['error'] = False
if not isinstance(data, dict):
data = {'error':True, 'message':'data 必须是一个 dict!'}
resp = jsonify(data)
# 跨域设置
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp

这样,上面的代码可以简化为:


@main.route('/', methods=['GET'])
def index():
return responseto()

要响应一个错误消息,可以简化为:


responseto('发生了一个错误!')

但是,当我是因为 PUT 方法请求时,出现了这样的错误:


XMLHttpRequest cannot load http://127.0.0.1:5000/account/status/. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:5001' is therefore not allowed access.


从当时请求的内容(见下方)可以看出,请求变成了 OPTION 而不是 PUT 。这是由于根据 CORS 规范
( via
) ,浏览器会做一次 preflight 请求,这次请求询问服务器支持哪些方法。


OPTIONS /account/status/ HTTP/1.1
Host: 127.0.0.1:5000
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Access-Control-Request-Method: PUT
Origin: http://localhost:5001
User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Mobile Safari/537.36
Access-Control-Request-Headers:
Accept: */*
Referer: http://localhost:5001/account/
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6


因此,上面提到的使用 responseto
的方法就没有作用了。因为任何一个路由都有可能包含 preflight 请求。



我们可以通过自定义的 Response 类(作为 Flask Response 的子类)来实现这个需求,让 flask 回复的任何响应都带有 Access-Control-Allow-*
的 HEAD。通过设置 Flask app 的 response_class
属性可以做到这点。


from flask import Flask, Response
class MyResponse(Response):
pass
def create_app():
app = Flask(__name__)
app.response_class = MyResponse

我们需要在 MyResponse 类中对 headers 进行一些操作。为此我们需要了解 Flask Response 的源码实现。



Flask 的 Response 是对 werkzeug.wrappers.Response
的一个简单继承。下面是 flask 中 Response 的源码实现(位于 wrappers.py 中):


from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase
class Response(ResponseBase):
"""The response object that is used by default in Flask. Works like the
response object from Werkzeug but is set to have an HTML mimetype by
default. Quite often you don't have to create this object yourself because
:meth:`~flask.Flask.make_response` will take care of that for you.
If you want to replace the response object used you can subclass this and
set :attr:`~flask.Flask.response_class` to your subclass.
"""
default_mimetype = 'text/html'


没错,就是仅此而已。因此我们还需要去看 werkzeug.wrappers.Response
的源码。下面是一点点节选:


def __init__(self, respOnse=None, status=None, headers=None,
mimetype=None, content_type=None, direct_passthrough=False):
if isinstance(headers, Headers):
self.headers = headers
elif not headers:
self.headers = Headers()
else:
self.headers = Headers(headers)


了解了 __init__
的结构,我们可以这样做继承。由于参数太多,我们仅保留一个 response 参数,其余的使用 **kwargs
代替:


from werkzeug.datastructures import Headers
class MyResponse(Response):
def __init__(self, respOnse=None, **kwargs):
kwargs['headers'] = ''
headers = kwargs.get('headers')
# 跨域控制
origin = ('Access-Control-Allow-Origin', '*')
methods = ('Access-Control-Allow-Methods', 'HEAD, OPTIONS, GET, POST, DELETE, PUT')
if headers:
headers.add(*origin)
headers.add(*methods)
else:
headers = Headers([origin, methods])
kwargs['headers'] = headers
return super().__init__(response, **kwargs)

使用上面的代码可以方便地实现跨域支持,根据需要调整 methods 和 origin 的值即可。



关于自定义响应类,Miguel 的这篇文章写得更详细: Customizing the Flask Response Class


(全文完)




推荐阅读
  • 深入解析 Vue 中的 Axios 请求库
    本文深入探讨了 Vue 中的 Axios 请求库,详细解析了其核心功能与使用方法。Axios 是一个基于 Promise 的 HTTP 客户端,支持浏览器和 Node.js 环境。文章首先介绍了 Axios 的基本概念,随后通过具体示例展示了如何在 Vue 项目中集成和使用 Axios 进行数据请求。无论你是初学者还是有经验的开发者,本文都能为你解决 Vue.js 相关问题提供有价值的参考。 ... [详细]
  • 本文介绍了如何使用JavaScript的Fetch API与Express服务器进行交互,涵盖了GET、POST、PUT和DELETE请求的实现,并展示了如何处理JSON响应。 ... [详细]
  • 在高并发需求的C++项目中,我们最初选择了JsonCpp进行JSON解析和序列化。然而,在处理大数据量时,JsonCpp频繁抛出异常,尤其是在多线程环境下问题更为突出。通过分析发现,旧版本的JsonCpp存在多线程安全性和性能瓶颈。经过评估,我们最终选择了RapidJSON作为替代方案,并实现了显著的性能提升。 ... [详细]
  • 深入解析 Android IPC 中的 Messenger 机制
    本文详细介绍了 Android 中基于消息传递的进程间通信(IPC)机制——Messenger。通过实例和源码分析,帮助开发者更好地理解和使用这一高效的通信工具。 ... [详细]
  • 本文详细探讨了Java中的ClassLoader类加载器的工作原理,包括其如何将class文件加载至JVM中,以及JVM启动时的动态加载策略。文章还介绍了JVM内置的三种类加载器及其工作方式,并解释了类加载器的继承关系和双亲委托机制。 ... [详细]
  • 本文深入探讨了UNIX/Linux系统中的进程间通信(IPC)机制,包括消息传递、同步和共享内存等。详细介绍了管道(Pipe)、有名管道(FIFO)、Posix和System V消息队列、互斥锁与条件变量、读写锁、信号量以及共享内存的使用方法和应用场景。 ... [详细]
  • 本文详细介绍了如何正确安装Java EE SDK,并解决在安装过程中可能遇到的问题,特别是关于servlet代码在Apache Tomcat 10中无法运行的情况。 ... [详细]
  • 本文探讨了为何采用RESTful架构及其优势,特别是在现代Web应用开发中的重要性。通过前后端分离和统一接口设计,RESTful API能够提高开发效率,支持多种客户端,并简化维护。 ... [详细]
  • 教程:如何打造令人印象深刻的GitHub个人主页Readme
    本文将指导您如何创建一个既专业又个性化的GitHub个人主页Readme,通过添加统计数据、常用语言和最近活动等元素,让您的主页更加吸引人。 ... [详细]
  • 技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统
    技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • REST与RPC:选择哪种API架构风格?
    在探讨REST与RPC这两种API架构风格的选择时,本文首先介绍了RPC(远程过程调用)的概念。RPC允许客户端通过网络调用远程服务器上的函数或方法,从而实现分布式系统的功能调用。相比之下,REST(Representational State Transfer)则基于资源的交互模型,通过HTTP协议进行数据传输和操作。本文将详细分析两种架构风格的特点、适用场景及其优缺点,帮助开发者根据具体需求做出合适的选择。 ... [详细]
  • Java 中重写与重载的区别
    本文详细解析了 Java 编程语言中重写(Override)和重载(Overload)的概念及其主要区别,帮助开发者更好地理解和应用这两种多态性机制。 ... [详细]
  • 本文详细介绍了如何正确配置Java环境变量PATH,以确保JDK安装完成后能够正常运行。文章不仅涵盖了基本的环境变量设置步骤,还提供了针对不同操作系统下的具体操作指南。 ... [详细]
  • 本文详细探讨了Java命令行参数的概念、使用方法及在实际编程中的应用,包括如何通过命令行传递参数给Java程序,以及如何在Java程序中解析这些参数。 ... [详细]
author-avatar
hy11011_847
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有