生命不止,继续 go go go !!!
继续,echo web框架,今天就聊一聊JSONP。
JSONP
1、什么是JSONP?
JSONP (JSON with padding) is used to request data from a server residing in a different domain than the client. It was proposed by Bob Ippolito in 2005.
JSONP enables sharing of data bypassing same-origin policy. The policy disallows running JavaScript to read media DOM elements or XHR data fetched from outside the page’s origin. The aggregation of the site’s scheme,port number and host name identifies as its origin. Due to inherent insecurities,JSONP is being replaced by CORS.
关于golang中cors可以参考:Go实战–golang中使用echo框架中的cors(labstack/echo、rs/cors)
要了解JSONP,不得不提一下JSON,那么什么是JSON ?
JSON is a subset of the object literal notation of JavaScript. Since JSON is a subset of JavaScript,it can be used in the language with no muss or fuss.
关于golang中使用json可以参考:
Go语言学习之encoding/json包(The way to go)
Go实战–net/http中JSON的使用(The way to go)
JSONP(JSON with Padding)是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。
JSONP有什么用
由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源,为了实现跨域请求,可以通过script标签实现跨域请求,然后在服务端输出JSON数据并执行回调函数,从而解决了跨域的数据请求。
如何使用JSONP
下边这一DEMO实际上是JSONP的简单表现形式,在客户端声明回调函数之后,客户端通过script标签向服务器跨域请求数据,然后服务端返回相应的数据并动态执行回调函数。
Jsonp原理
首先在客户端注册一个callback,然后把callback的名字传给服务器。
此时,服务器先生成 json 数据。
然后以 javascript 语法的方式,生成一个function,function 名字就是传递上来的参数 jsonp.
最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。
客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动态执行回调函数)
JS中使用JSONP
html
<button id="btn">Click to get HTTP header via JSONP</button>
<pre id="result"></pre>
JavaScript
有人提供的线上的服务,通过jsonp返回http请求:
https://ajaxhttpheaders.appspot.com/?callback={{YOUR_CALLBACK_NAME}}
'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 = "https://ajaxhttpheaders.appspot.com/?callback=myCallback";
document.querySelector("head").appendChild(tag);
}
btn.addEventListener("click",jsonp);
golang中实现JSONP
参考地址:
https://siongui.github.io/2017/03/18/go-jsonp-server-implementation-code/
要点:
Get the name of front-end callback function from the query string in HTTP request.
Encode the data to be returned to the client in JSON format. In this case,it’s the HTTP headers encoded in JSON.
Set the Content-Type as application/javascript in HTTP response.
Return the string callbackName(JSONString); to the client,where callbackName is the name of callback in query string,and JSONString is the JSON-encoded data in step 2.
main.go
package main
import (
"encoding/json"
"fmt"
"net/http"
)
const indexHtml = `<!DOCTYPE html> <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 = "/jsonp?callback=myCallback"; document.querySelector("head").appendChild(tag); } btn.addEventListener("click",jsonp); </script> </body> </html>`
func handler(w http.ResponseWriter,r *http.Request) {
fmt.Fprintf(w,indexHtml)
}
func jsonpHandler(w http.ResponseWriter,r *http.Request) {
callbackName := r.URL.Query().Get("callback")
if callbackName == "" {
fmt.Fprintf(w,"Please give callback name in query string")
return
}
b,err := json.Marshal(r.Header)
if err != nil {
fmt.Fprintf(w,"json encode error")
return
}
w.Header().Set("Content-Type","application/javascript")
fmt.Fprintf(w,"%s(%s);",callbackName,b)
}
func main() {
http.HandleFunc("/jsonp",jsonpHandler)
http.HandleFunc("/",handler)
http.ListenAndServe(":8000",nil)
}
浏览器输入:
http://localhost:8000/
点击按钮,结果:
{
"Accept": [ "*/*" ],"Accept-Encoding": [ "gzip,deflate,br" ],"Accept-Language": [ "zh-CN,zh;q=0.9,en;q=0.8" ],"Alexatoolbar-Alx_ns_ph": [ "AlexaToolbar/alx-4.0" ],"Connection": [ "keep-alive" ],"Cookie": [ "_ga=GA1.1.981224509.1509938615" ],"Referer": [ "http://localhost:8000/" ],"User-Agent": [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/62.0.3202.94 Safari/537.36" ] }
echo中使用JSONP
参考地址:
https://echo.labstack.com/cookbook/jsonp
html
<!DOCTYPE html>
<html>
<head>
<Meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<title>JSONP</title>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript"> var host_prefix = 'http://localhost:1323'; $(document).ready(function() { // JSONP version - add 'callback=?' to the URL - fetch the JSONP response to the request $("#jsonp-button").click(function(e) { e.preventDefault(); // The only difference on the client end is the addition of 'callback=?' to the URL var url = host_prefix + '/jsonp?callback=?'; $.getJSON(url,function(jsonp) { console.log(jsonp); $("#jsonp-response").html(JSON.stringify(jsonp,2)); }); }); }); </script>
</head>
<body>
<div class="container" style="margin-top: 50px;">
<input type="button" class="btn btn-primary btn-lg" id="jsonp-button" value="Get JSONP response">
<p>
<pre id="jsonp-response"></pre>
</p>
</div>
</body>
</html>
main.go
package main
import (
"math/rand"
"net/http"
"time"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)
func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Static("/","public")
// JSONP
e.GET("/jsonp",func(c echo.Context) error {
callback := c.QueryParam("callback")
var content struct {
Response string `json:"response"`
Timestamp time.Time `json:"timestamp"`
Random int `json:"random"`
}
content.Response = "Sent via JSONP"
content.Timestamp = time.Now().UTC()
content.Random = rand.Intn(1000)
return c.JSONP(http.StatusOK,callback,&content)
})
// Start server
e.Logger.Fatal(e.Start(":1323"))
}