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

cors在服务器还是接口_Cors最佳实践

基础知识CORS(Cross-OriginResourceSharing),跨域资源共享,是浏览器跨域的官方解决方案。相比其他常见的跨域解决方案(jso

基础知识

CORS(Cross-Origin Resource Sharing),跨域资源共享,是浏览器跨域的官方解决方案。相比其他常见的跨域解决方案(jsonp、iframe、postMessage),CORS具有以下优点:

  1. 前端代码优雅。CORS由浏览器和后台交互完成,前端开发者感受不到和同源通信的差别,代码完全一样;

  2. 规范标准,兼容性好,IE10以上都支持,浏览器之间几乎没有差异;

  3. 支持所有类型的HTTP请求,功能完善。(相比之下,jsonp只支持get,对RESTful风格接口很不友好);

  4. 跨平台统一。同一个接口可以同时供WEB和APP使用,不需额外处理;

  5. 错误信息可以被XMLHttpRequest的onerror捕获,便于调试。

原理

CORS不需要前端代码做任何处理,一切交互都由浏览器和服务端完成。请求分为两种:简单请求(simple request)和非简单请求(not-so-simple request)。符合以下3个条件即可使用简单请求:

  1. 请求方法是HEAD、GET或POST;

  2. Http Header只包含Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type,没有其他字段;

  3. Content-Type的值是application/x-www-form-urlencoded、multipart/form-data或text/plain。

不符合上述条件的任何一项,即作为非简单请求处理。非简单请求只是多加了一次OPTIONS请求,其余操作与简单请求一致。

简单请求

c2b8bdd23d45610d8941701131b24da8.png

浏览器发起简单请求的时候,会自动在header中增加Origin字段,用来告知服务器本次请求来自哪个地址。字段的值完成包含“协议+域名+端口”,因为这三者任意一个跟接口地址不一致,都会造成跨域。这个字段是浏览器添加的,前端代码里请求既不需要、也没办法修改。

服务器接收到请求之后,需要判断header的Origin字段。如果不在许可范围内,后台需要返回一个正常的、不带额外header的响应。浏览器发现缺少Access-Control-Allow-Origin字段,则抛出错误。注意,不管请求是否成功,http响应的状态码都可以是200,所以不能通过状态码去识别错误,只能通过XMLHttpRequest的onerror回调函数捕获错误。

如果Origin指定的域名在许可范围内,后台需要返回一个带有以下额外header的响应:

  • Access-Control-Allow-Origin:必填。可以填请求时Origin字段的值,表示允许本次跨域请求,也可以填“*”,表示允许任意地址的请求。

  • Access-Control-Allow-Credentials:选填。表示是否允许发送COOKIE,默认为false。

  • Access-Control-Expose-Headers:选填。在跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的字段(Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma),如果要获取header中的其他字段,需要把字段名配置在这里。

非简单请求

d68641d60a89f70378aca626b14d6e3c.png

浏览器发起非简单请求的时候,会先发起一次"preflight"(预检)请求,请求的方法为OPTIONS。预检请求的header中包含以下两个字段:

  • Access-Control-Request-Method: 必填。本次请求用到的方法。

  • Access-Control-Request-Headers: 选填。本次请求会在header中额外附带的字段。

由于这次请求是浏览器自动发起的,前端代码量既不需求、也没有办法控制,仅需要服务端去接收并做响应即可。只有服务端返回允许请求的响应,浏览器才会正式发送XMLHttpRequest请求,否则就报错,同样通过onerror回调函数捕获。

服务端对预检请求的响应,header中应带有以下字段:

  • Access-Control-Allow-Methods: 必填。本接口允许的请求方法。

  • Access-Control-Allow-Headers: 选填。本接口允许的header字段。

  • Access-Control-Allow-Credentials: 选填。表示是否允许发送COOKIE,默认为false。

  • Access-Control-Max-Age: 选填,本次预检请求有效期,单位秒。有效期内重复发起请求时,不需要再发送预检请求。

预检请求完成以后,就可以正常发送真正的请求了,这个跟简单请求是完全一致的。

实践

根据上述原理,服务端想要支持CORS,须实现两个功能:

  1. 对OPTIONS预检请求正确响应;

  2. 对其他类型的所有请求附带正确的header。

这两个功能需要拦截http请求并修改响应,可以在Nginx或者框架路由中完成,而不用修改业务代码。以下提供三类常见的解决方案。

Nginx

通过Nginx即可实现CORS,不需要修改程序代码。Nginx配置可能会用到以下功能:

  • $request_method:获取请求的方法,判断到'OPTIONS'则返回204;

  • $http_origin:获取请求的地址(即http请求header里的origin字段);

  • add_header:给响应增加头部。

举例:

server { ... location / { #处理预检请求 if ($request_method = 'OPTIONS') { add_header Access-Control-Allow-Origin https://blog.oonne.com; add_header Access-Control-Max-Age 600; add_header Access-Control-Allow-Methods GET, POST, PUT, DELETE, OPTIONS; add_header Access-Control-Allow-Headers 'Content-Type, x-auth-token'; add_header Content-Length 0 ; return 204; } #其他请求添加头部 add_header Access-Control-Allow-Origin $http_origin; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Expose-Headers Content-Length; proxy_pass http://127.0.0.1:8080/; }}

备注:http响应码204表示成功但没数据,200表示成功,预检请求没有数据,正确的状态码应该是204。(虽然返回200前端也能正常处理)

Node.js

以Express为例,我们可以实现一个中间件:

app.use(function (req, res, next) { if (req.method == 'OPTIONS') { res.header('Access-Control-Allow-Origin', 'https://blog.oonne.com'); res.header('Access-Control-Max-Age', 1728000); res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type, x-auth-token'); res.send(204); } else { res.header('Access-Control-Allow-Origin', 'https://blog.oonne.com'); res.header('Access-Control-Allow-Credentials', true); res.header('Access-Control-Allow-Headers', 'Content-Type'); next(); }});

也可以使用独立的CORS库,使用方法参考官方文档。

PHP

Larave有CORS中间件,参考文档使用即可,原理跟Express差不多,这里不用多做介绍。

Yii2的路由模式比较特殊,需要修改Controller里的behaviors。我们先可以实现一个基础的Controller,其他Controller都继承他。然后在behaviors中定义一个corsFilter:

Response::FORMAT_JSON ]; $behaviors['corsFilter'] = [ 'class' => CorsFilter::className(), 'cors' => [ 'Access-Control-Allow-Credentials' => true, 'Access-Control-Max-Age' => 3600, 'Access-Control-Request-Method' => ['POST', 'GET'], 'Access-Control-Request-Headers' => ['Content-Type', 'X-Auth-Token'], 'Origin' => Yii::$app->params['apiOrigin'], ] ]; $behaviors['verbFilter'] = [ 'class' => OptionsFilter::className(), 'actions' => $this->verbs(), ]; return $behaviors; }}

其中,CorsFilter需要对OPTIONS预检请求做特殊处理,如下:

request = $this->request ?: Yii::$app->getRequest(); $this->response = $this->response ?: Yii::$app->getResponse(); $this->overrideDefaultSettings($action); $requestCorsHeaders = $this->extractHeaders(); $responseCorsHeaders = $this->prepareHeaders($requestCorsHeaders); $this->addCorsHeaders($this->response, $responseCorsHeaders); // clear all options method $verb = Yii::$app->getRequest()->getMethod(); if ($verb=='OPTIONS'){ $this->response->statusCode = 204; $this->response->send(); return false; } return true; }}

同时,Controller里其他的behaviors配置,都需要保证对OPTIONS请求的正常返回。比如上面用到的OptionsFilter,用于过滤请求的方法的,我们需要保证所有的请求都允许OPTIONS方法,如下:

action->id; if (isset($this->actions[$action])) { $verbs = $this->actions[$action]; } elseif (isset($this->actions['*'])) { $verbs = $this->actions['*']; } else { return $event->isValid; } //对OPTIONS请求做特殊处理 $verb = Yii::$app->getRequest()->getMethod(); if ($verb=='OPTIONS'){ return $event->isValid; } $allowed = array_map('strtoupper', $verbs); if (!in_array($verb, $allowed)) { $event->isValid = false; Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $allowed)); throw new MethodNotAllowedHttpException('Method Not Allowed. This URL can only handle the following request methods: ' . implode(', ', $allowed) . '.'); } return $event->isValid; }}

如果你在继承的Controller里用到了其他behaviors,也需要考虑预检请求的返回问题。

总结

本文介绍了CORS规范的原理,并给出了Nginx、Node.js、PHP的最佳实践。

c85e197d042379b81ab52683a968a484.png




推荐阅读
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • LVS实现负载均衡的原理LVS负载均衡负载均衡集群是LoadBalance集群。是一种将网络上的访问流量分布于各个节点,以降低服务器压力,更好的向客户端 ... [详细]
  • 目录浏览漏洞与目录遍历漏洞的危害及修复方法
    本文讨论了目录浏览漏洞与目录遍历漏洞的危害,包括网站结构暴露、隐秘文件访问等。同时介绍了检测方法,如使用漏洞扫描器和搜索关键词。最后提供了针对常见中间件的修复方式,包括关闭目录浏览功能。对于保护网站安全具有一定的参考价值。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 解决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,以便查看详细日志信息。 ... [详细]
  • 31.项目部署
    目录1一些概念1.1项目部署1.2WSGI1.3uWSGI1.4Nginx2安装环境与迁移项目2.1项目内容2.2项目配置2.2.1DEBUG2.2.2STAT ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
author-avatar
晴儿19082
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有