jsonp和cors处理跨域-Golang

记录一下实习期间的一个任务,原本使用的是jsonp来解决跨域的问题
但是前端拿不到HTTP的状态码,所以改用了cors
如果错误,请大佬指正!!!

跨域这个东西一直感觉并不是一个很清楚的东西,这次顺便整理一下。
网页中进行测试请求,Server是用的GO语言写的,戳我跳转Golang-Server

跨域

跨域:浏览器对于javascript的同源策略的限制,例如a.cn下面的js不能调用b.cn中的js,对象或数据(因为a.cn和b.cn是不同域),所以跨域就出现了.

同源策略

请求的url地址,必须与浏览器上的url地址处于同域上,也就是域名,端口,协议相同.
比如:我在本地上的域名是localhost,请求另外一个域名一段数据。这个时候在浏览器上会报错。

1
2
3
4
5
6
7
8
9
10
<html>
<head><title>CrossDomain</title></head>
<body>
<script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
$.get('http://localhost:8080/normalHttp',function(res){
})
</script>
</body>
</html>

浏览器路径:
localhost/CrossDomain.html
CrossDomainError.png

这个就是同源策略的保护,如果浏览器对javascript没有同源策略的保护,那么一些重要的机密网站将会很危险~

假如自己的url是这个study.cn/json/jsonp/jsonp.html,访问其他URL的结果如下

请求地址 形式 结果
http://study.cn/test/a.html 同一域名,不同文件夹 成功
http://study.cn/json/jsonp/jsonp.html 同一域名,统一文件夹 成功
http://a.study.cn/json/jsonp/jsonp.html 不同域名,文件路径相同 失败
http://study.cn:8080/json/jsonp/jsonp.html 同一域名,不同端口 失败
https://study.cn/json/jsonp/jsonp.html 同一域名,不同协议 失败

jsonp

jsonp 全称是JSON with Padding,是为了解决跨域请求资源而产生的解决方案,是一种依靠开发人员创造出的一种非官方跨域数据交互协议。
一个是描述信息的格式,一个是信息传递双方约定的方法。

jsonp的产生

1.AJAX直接请求普通文件存在跨域无权限访问的问题,不管是静态页面也好.
2.不过我们在调用js文件的时候又不受跨域影响,比如引入jquery框架的,或者是调用相片的时候
3.凡是拥有scr这个属性的标签都可以跨域例如<script><img><iframe>
4.如果想通过纯web端跨域访问数据只有一种可能,那就是把远程服务器上的数据装进js格式的文件里.
5.而json又是一个轻量级的数据格式,还被js原生支持
6.为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback 参数给服务端

Example

放一个jsonp的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<html>
<head><title>Go JSONP Server</title></head>
<body>
<button id="btn">Click to get HTTP header via JSONP</button>
<pre id="result"></pre>
<script>
'use strict';

var btn = document.getElementById("btn");
var result = document.getElementById("result");

function myCallback(acptlang) {
result.innerHTML = JSON.stringify(acptlang, null, 2);
}

function jsonp() {
result.innerHTML = "Loading ...";
var tag = document.createElement("script");
tag.src = "http://localhost:8080/normalJsonp?callback=myCallback";
document.querySelector("head").appendChild(tag);
}

btn.addEventListener("click", jsonp);
</script>
</body>
</html>

打开localhost/JsonP.html,然后使用click,既可以看到效果,但是前端好像拿不到表示错误的HTTP状态码,因为这个方式是使用的加载src的方式。
Jsonp.png

Cors

CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。下面有些内容没有整理,去看下阮老师写的吧–>阮一峰:跨域资源共享 CORS 详解

简介

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

rs/cors

Go net/http configurable handler to handle CORS requests.
一个GitHub的开源项目,我使用它来完成跨域请求,炒一下README的demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"net/http"

"github.com/rs/cors"
)

func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte("{\"hello\": \"world\"}"))
})

// cors.Default() setup the middleware with default options being
// all origins accepted with simple methods (GET, POST). See
// documentation below for more options.
handler := cors.Default().Handler(mux)
http.ListenAndServe(":8080", handler)
}

这个上面给出的替换整个handler的做法,我看了下rs/cors/doc,文档里面说明比较少(issue里面给的内容很多,不过都是英文,逃),还是研究了一下如何对于单个的api进行处理的方法–>戳我跳转Golang-Server

ajax请求

这里放一下ajax的请求,对应是Golang-Server的请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

<html>
<head><title>Go JSONP Server</title></head>
<body>
<script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
var url = "http://localhost:8080/hello";
$.ajax({
type: "get",
url: url,
headers: {
contentType: "application/json;charset=utf-8",
},
dataType: "json",
success: function (data) {
if ("true" == data.status) {
// alert("成功!"+XMLHttpRequest.status);
//这里也可以循环data里你要显示的数据
return true;
} else {
// alert("失败!错误信息如下:" + data.errorMsg+"status:"+XMLHttpRequest.status);
return false;
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
// alert("请求失败!"+XMLHttpRequest.status);
},
});
</script>
</body>
</html>

请求的结果,首先发起OPTIONS的请求
OPTIONS.png
然后发起GET请求
GET.png

Preflighted Request

这个就是先发送一个方法为OPTIONS的预请求(preflight request),用于试探服务端是否能接受真正的请求,如果options获得的回应是拒绝性质的,比如404\403\500等http状态,就会停止post、put等请求的发出。
那么, 什么情况下请求会变成preflighted request呢?

  • 请求方法不是GET/HEAD/POST
  • POST请求的Content-Type并非application/x-www-form-urlencoded, multipart/form-data, 或text/plain
  • 请求设置了自定义的header字段

Golang-Server

这里是样例使用Server的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package main

import (
"net/http"

"strconv"
"github.com/rs/cors"
"fmt"
)

func normalHttp(w http.ResponseWriter, req *http.Request) {
data := "{\"hello\": \"world\"}"
w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
w.Header().Set("Content-Length", strconv.Itoa(len(data)))
w.Write([]byte(data))
return
}
func norlJsonp(w http.ResponseWriter, req *http.Request) {
query := req.URL.Query()
callback := query.Get("callback")
data := "{\"hello\": \"world\"}"
re := []byte(fmt.Sprintf("%s(%s)", callback, data))
w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
w.Header().Set("Content-Length", strconv.Itoa(len(re)))
w.Write([]byte(re))
return
}

type myClass struct {
ID int `json:"id"`
}

func main() {
mux := http.NewServeMux()
c := cors.New(cors.Options{
AllowedHeaders: []string{"*"},
})
mux.Handle("/normalHttp", http.HandlerFunc(normalHttp))
mux.Handle("/normalJsonp", http.HandlerFunc(norlJsonp))
mux.Handle("/hello", c.Handler(http.HandlerFunc(normalHttp)))

http.ListenAndServe(":8080", mux)
}

NOT PASS ACCESS

如果遇到了这个问题,应该是首部的问题,如果按照默认的配置是不会有这个问题的
Acess-Origin.png
Access-Control_Allow_Origin.png

如果你使用其他的封装,首先请仔细查看文档Parameters对不同框架的支持,在默认情况下应该是可以直接使用的,还是解决不了的话可以试试这个.

1
2
3
c := cors.New(cors.Options{
AllowedHeaders: []string{"*"},
})

后记

对于现在的跨域问题,jsonp和cors是两种比较常见的解决方法,当然很多地方也使用NGINX来进行处理OPTIONS的请求,在很多地方都是用NGINX的方案,对于iframe等等因为作者不是前端就不赘述了。

参考资料

如果对您有帮助,请我喝杯咖啡?