你应该知道如何跨域

开发 开发工具
转眼就是秋招季啦。经历了几场笔试面试,屡次被问到关于如何实现跨域。老实说,之前都是纸上谈兵,也没有项目需要跨域,甚至觉得这个东西没什么意义。直到今天项目中遇到了跨域问题,看了不少资料才理解跨域的普遍性和意义。特写此篇文章整理自己所得。

[[171703]]

前言

转眼就是秋招季啦。经历了几场笔试面试,屡次被问到关于如何实现跨域。老实说,之前都是纸上谈兵,也没有项目需要跨域,甚至觉得这个东西没什么意义。直到今天项目中遇到了跨域问题,看了不少资料才理解跨域的普遍性和意义。特写此篇文章整理自己所得。

转自个人博客: 关于跨域

什么是跨域

一般来说,如果你在开发中需要进行跨域操作(从一个非同源网站发送请求获取数据),一般而言,你在浏览器控制台看到的结果为:

XMLHttpRequest cannot load http://external-domain/service. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://my-domain’ is therefore not allowed access.

同源策略

说到跨域就不得不提“同源策略”。

同源策略是Web浏览器针对恶意的代码所进行的措施,为了防止世界被破坏,为了保护世界的和平,Web浏览器,采取了同源策略,只允许脚本读取和所属文档来源相同的窗口和文档的属性。

那么,怎么判断文档来源是否相同呢?很简单,看三个部分: 协议、主机、端口号。只要其中一个部分不同,则不同源。

跨域的应用场景

  1. 来自 home.example.com 的文档里的脚本读取 developer.example.com载入的文档的属性。
  2. 来自 home.example.com 的文档里的脚本读取 text.segmentfault.com载入的文档的属性。

如何跨域

设置domain属性

针对上述应用场景的***种情况,可以设置Document对象的domain属性。但是设置时使用的字符串必须具有有效的域前缀或者它本身。

PS: domain值中必须有一个点号。

PS: domain不能由松散的变为紧绷的。

  1. //初始值 "home.example.com"  
  2. document.domain = "example.com"; //OK 
  3. document.domain = "home.example.com"; //NO,不能由松散变紧绷 
  4. document.domain = "example"; //NO,必须有一个点号 
  5. document.domain = "another.com"; //NO, 必须是有效域前缀或其本身  

JSONP

JSONP由两部分组成: 回调函数和数据。

原理:通过动态<script>元素来使用,可以通过src属性指定一个跨域URL。

  1. function handler(data){ 
  2.     console.log(data); 
  3.  
  4. var script = document.createElement("script"); 
  5. script.src = "https://segmentfault.com/json/?callback=handler"
  6. document.body.insertBefore(script, document.body.firstChild);  

除此之外,还可以利用jQuery来实现。 

  1. function jsonCallback(json){ 
  2.   console.log(json); 
  3.  
  4. $.ajax({ 
  5.   url: "http://run.plnkr.co/plunks/v8xyYN64V4nqCshgjKms/data-2.json"
  6.   dataType: "jsonp" 
  7. }); 

 运行结果如下:

 

某些API(例如Github API)允许你定义一个回调函数,当请求返回时执行该函数。 

  1. function logResults(json){ 
  2.   console.log(json); 
  3.  
  4. $.ajax({ 
  5.   url: "https://api.github.com/users/jeresig"
  6.   dataType: "jsonp"
  7.   jsonpCallback: "logResults" 
  8. }); 

 运行结果如下:

 

优点:

  1. 兼容性强。
  2. 简单易用,能之间访问响应文本,支持浏览器与服务器之间双向通信。

不足:

  1. 只能用GET方法,不能使用POST方法
  2. 无法判断请求是否失败,没有错误处理。

跨域资源共享CORS

需要浏览器和服务器同时支持。

原理:使用"Origin:"请求头和"Access-Control-Allow-Origin"响应头来扩展HTTP。其实就是利用新的HTTP头部来进行浏览器与服务器之间的沟通。

针对前端代码而言,变化的地方在于相对路径需改为绝对路径。 

  1. //以前的方式 
  2. var xhr = new XMLHttpRequest();  
  3. xhr.open("GET""/test"true);  
  4. xhr.send();  
  5. //CORS方式 
  6. var xhr = new XMLHttpRequest();  
  7. xhr.open("GET""http://segmentfault.com/test"true);  
  8. xhr.send();   

针对服务器代码而言,需要设置Access-Control-Allow-Origin,显式地列出源或使用通配符来匹配所有源。

优点:

  1. CORS支持所有类型的HTTP请求。
  2. 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据

不足:

  1. 不能发送和接收cookie

更新:服务端可以通过设置Access-Control-Allow-Credentials该字段来表示是否允许发送Cookie。发送ajax请求时,需配置withCredentials属性。(感谢sf小伙伴@lloyd_zhou 指正)

具体可查看 阮一峰大大的博客。

  1. 不能使用setRequestHeader()设置自定义头部
  2. 兼容IE10+

postMessage

postMessge()是HTML5新定义的通信机制。所有主流浏览器都已实现。该API定义在Window对象。

  1. otherWindow.postMessage(message, targetOrigin); 

message: 要传递的消息。

targetOrigin: 指定目标窗口的源。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;

当源匹配时,调用postMessage()方法时,目标窗口的Window对象会触发一个message事件。在进行监听事件时,应先判断origin属性,忽略来自未知源的消息。 

  1. //<http://example.com:8080>上的脚本: 
  2. var popup = window.open(...popup details...); 
  3. popup.postMessage("The user is 'bob' and the password is 'secret'"
  4.               "https://secure.example.net");   
  5. popup.postMessage("hello there!""http://example.org"); 
  6.  
  7. function receiveMessage(event) 
  8.   if (event.origin !== "http://example.org"
  9.     return
  10.   // event.source is popup 
  11.   // event.data is "hi there yourself!  the secret response is: rheeeeet!"【见下面一段代码可知】 
  12. window.addEventListener("message", receiveMessage, false);  

针对上面的脚本进行接受数据的操作: 

  1. /* 
  2.  * popup的脚本,运行在<http://example.org>: 
  3.  */ 
  4.  
  5. //当postMessage后触发的监听事件 
  6. function receiveMessage(event) 
  7.   //先判断源 
  8.   if (event.origin !== "http://example.com:8080"
  9.     return
  10.  
  11.   // event.source:window.opener 
  12.   // event.data:"hello there!" 
  13.  
  14.   event.source.postMessage("hi there yourself!  the secret response " + 
  15.                            "is: rheeeeet!"
  16.                            event.origin); 
  17.  
  18. window.addEventListener("message", receiveMessage, false);  

后续

收到了很多小伙伴的建议和指正,不胜感激,我会慢慢丰富这篇文章的内容的。请多多指教~

参考文章

责任编辑:庞桂玉 来源: segmentfault
相关推荐

2019-03-13 14:15:25

CORS跨域资源前端

2022-01-04 10:10:34

Garuda LinuArch LinuxLinux

2021-10-25 14:55:38

Linux技巧命令

2020-04-29 14:30:35

HTTPHTTPS前端

2013-01-09 13:55:43

2019-06-03 08:04:43

Apache服务器命令

2011-03-25 15:56:58

2019-02-24 23:06:00

2021-06-07 12:40:34

Python代码陷阱

2013-06-28 14:09:33

PHP库

2020-10-13 14:15:22

HTTPHTTP请求方法

2023-05-04 16:10:13

缓存前端

2015-10-26 09:19:28

PHP经验

2022-11-04 08:22:14

编译代码C语言

2021-07-05 08:43:46

Spring Beanscope作用域

2021-06-06 13:05:15

前端跨域CORS

2011-04-18 14:23:37

javascriptHTML

2023-09-05 08:23:56

SpringScope方法

2014-09-12 11:35:23

开源

2020-06-02 10:10:46

React前端组件
点赞
收藏

51CTO技术栈公众号