热门标签 | 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


(全文完)




推荐阅读
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • 本文讨论了如何在codeigniter中识别来自angularjs的请求,并提供了两种方法的代码示例。作者尝试了$this->input->is_ajax_request()和自定义函数is_ajax(),但都没有成功。最后,作者展示了一个ajax请求的示例代码。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • springmvc学习笔记(十):控制器业务方法中通过注解实现封装Javabean接收表单提交的数据
    本文介绍了在springmvc学习笔记系列的第十篇中,控制器的业务方法中如何通过注解实现封装Javabean来接收表单提交的数据。同时还讨论了当有多个注册表单且字段完全相同时,如何将其交给同一个控制器处理。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
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社区 版权所有