记录一下实习期间的一个任务,原本使用的是 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 11 <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
这个就是同源策略的保护,如果浏览器对 javascript 没有同源策略的保护,那么一些重要的机密网站将会很危险~
假如自己的 url 是这个study.cn/json/jsonp/jsonp.html
,访问其他 URL 的结果如下
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 27 28 <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 的方式。
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 mainimport ( "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\"}" )) }) 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 32 <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 ) { return true ; } else { return false ; } }, error : function (XMLHttpRequest, textStatus, errorThrown ) { }, }); </script > </body > </html >
请求的结果,首先发起 OPTIONS
的请求 然后发起 GET
请求
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 mainimport ( "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) }
gin 配置 对于 gin 框架来说,如果想要限制某几个 origin 可以访问,可以使用中间件进行如下设置,创建一个名为 mycors 的包,下面创建 mycors.go
,可以通过 AllowedOrigins,AllowedHeaders 的设置来控制跨域请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mycorsimport ( "github.com/gin-gonic/gin" cors "github.com/rs/cors/wrapper/gin" ) func Cors () gin.HandlerFunc { return cors.New(cors.Options{ AllowedOrigins: []string {"http://example.com" , "http://example1.com" }, AllowedHeaders: []string {"*" }, AllowCredentials: true , }) }
然后在 router 中对 gin 使用中间件,这样就可以对 r 进行跨域处理,需要注意的是 group 需要重新设置
1 2 3 4 5 6 7 8 9 10 r := gin.New() r.Use(mycors.Cors()) test := r.Group("/test" , mycors.Cors()) { test.GET("/testapi" , func (c *gin.Context) { c.JSON(200 , gin.H{ "message" : "message" , }) }) }
NOT PASS ACCESS 如果遇到了这个报错,是首部的问题,如果按照默认的配置是不会有这个问题的,在上面的 gin 框架的讲解中也提到了对应的允许方式,AllowedHeaders
可以是多个字符串只允许某些首部的使用
如果你使用其他的封装,首先请仔细查看文档 Parameters 和对不同框架的支持 ,在默认情况下应该是可以直接使用的,还是解决不了的话可以试试这个.
1 2 3 c := cors.New(cors.Options{ AllowedHeaders: []string {"*" }, })
后记 对于现在的跨域问题,jsonp 和 cors 是两种比较常见的解决方法,当然很多地方也使用 NGINX 来进行处理 OPTIONS 的请求,对于 iframe 等等因为作者不是前端就不赘述了。
参考资料