大略的来说,出于安全方面的考虑,页面中的JavaScript无法访问其他做事器上的数据,即“同源策略”。
而跨域便是通过某些手段来绕过同源策略限定,实现不同做事器之间通信的效果。

什么是JSONP?

JSON(JavaScript Object Notation) 是一种轻量级的数据交流格式,JSONP 是 JSON with padding(添补式 JSON 或参数式 JSON)的简写。
JSONP(JSON with Padding)则是JSON 的一种“利用模式”,通过这种模式可以实现数据的跨域获取。
JSONP 由两部分组成:回调函数和数据。
回调函数是当相应到来时该当在页面中调用的函数。
回调函数的名字一样平常是在要求中指定的。
而数据便是传入回调函数中的 JSON 数据。

jspjsonp跨域教你若何应用JSONP实现跨域 Webpack

JSONP跨域的基本事理

在同源策略下,在某个做事器下的页面是无法获取到该做事器以外的数据的,但img、iframe、script等标签是个例外,这些标签可以通过src属性要求到其他做事器上的数据。
利用script标签的开放策略,我们可以实现跨域要求数据,当然,也须要做事真个合营。
当我们正常地要求一个JSON数据的时候,做事端返回的是一串JSON类型的数据,而我们利用JSONP模式来要求数据的时候,做事端返回的是一段可实行的JavaScript代码

举个例子,如果须要从做事器(http://www.a.com/user?id=123)获取的数据如下:

{\"大众id\公众: 123, \公众name\"大众 : 张三, \公众age\"大众: 17}

那么,利用JSONP办法要求(http://www.a.com/user?id=123?callback=foo)的数据将会是如下:

foo({\"大众id\公众: 123, \"大众name\公众 : 张三, \公众age\"大众: 17});

当然,如果做事端考虑得更加充分,返回的数据可能如下:

try{foo({\"大众id\"大众: 123, \"大众name\"大众 : 张三, \"大众age\"大众: 17});}catch(e){}

这时候我们只要定义一个foo()函数,并动态地创建一个script标签,使其的src属性为http://www.a.com/user?id=123?callback=foo:

便可以利用foo函数来调用返回的数据了。

JSONP跨域事理探秘

我们知道,利用 XMLHTTPRequest 工具发送HTTP要求时,会碰着 同源策略 问题,域不同要求会被浏览器拦截。

那么是否有方法能绕过 XMLHTTPRequest 工具进行HTTP跨域要求呢?

换句话说,不该用 XMLHTTPRequest 工具是否可以发送跨域HTTP要求呢?

细心的你可能会创造,像诸如:

<script type=\公众text/javascript\"大众 src=\"大众http://www.a.com/scripts/1.js\公众></script>

<img src=\"大众http://www.b.com/images/1.jpg\"大众 />

<link rel=\"大众stylesheet\公众 href=\"大众http://www.c.com/assets/css/1.css\"大众 />

这种标签是不会碰着\"大众跨域\"大众问题的,严格意义上讲,这不是跨域,跨域是指在脚本代码中向非同源域发送HTTP要求,这只是跨站资源要求。

那么,我们是否可以利用跨站资源要求这一办法来实现跨域HTTP要求呢?

以<script></script>标签为例进行探索,先看一段代码:

<!DOCTYPE html>

<html>

<head>

<meta http-equiv=\公众Content-Type\"大众 content=\公众text/html; charset=utf-8\"大众 />

<title>jsonp demo</title>

<!-- JavaScript片断1 -->

<script type=\"大众text/javascript\"大众>

function handler(data) {

alert(data);

// our code here...

}

</script>

<!-- JavaScript片断2 -->

<script type=\公众text/javascript\公众>

handler('success');

</script>

</head>

<body>

A JSONP demo.

</body>

</html>

这段代码中,有2个JavaScript片断,第1个片断中定义了一个处理函数handler(),这个处理函数比较大略,没有对数据做任何处理,只是把它alert出来;第2个片断调用了它,运行这个页面浏览器会弹出\"大众success\公众。

我们假设第2个JavaScript片断存储在别的地方,然后我们利用<script src=\"大众\公众 />的办法把它引入进来,像这样:

<!DOCTYPE html>

<html>

<head>

<meta http-equiv=\公众Content-Type\"大众 content=\"大众text/html; charset=utf-8\公众 />

<title>jsonp demo</title>

<!-- JavaScript片断1 -->

<script type=\公众text/javascript\公众>

function handler(data) {

alert(data);

// our code here...

}

</script>

<!-- JavaScript片断2 -->

<script type=\"大众text/javascript\公众 src=\"大众http://service.a.com/script/1.js\公众></script>

</head>

<body>

A JSONP demo.

</body>

</html>

service.a.com/script/1.js:

handler('success');

这种方法和把JavaScript代码直接写在页面是等效的,但是,我们由此可以遐想到什么?

我们是否可以事先在本页面定义处理程序,做事端返回JS脚本,脚本的内容便是对处理程序的回调,做事返回的数据通过参数的形式传回:

handler('做事返回的数据');

然后通过动态向当前页面head节点添加<script src=\"大众做事地址\公众></script>节点的办法来“假造”HTTP要求?

于是,可以编写这样一个大略的测试用例:

先写做事端,非常大略的一个做事,返回字符串\"大众Hello World\公众,一样平常处理程序Service.ashx:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

namespace JSONPDemo.Service

{

/// <summary>

/// Service2 的择要解释

/// </summary>

public class Service2 : IHttpHandler

{

public void ProcessRequest(HttpContext context)

{

context.Response.ContentType = \"大众text/plain\"大众;

context.Response.Write(\"大众handler('Hello World');\公众);

}

public bool IsReusable

{

get

{

return false;

}

}

}

}

再写客户端,一个大略的静态Web页,index.html:

<!DOCTYPE html>

<html>

<head>

<meta http-equiv=\公众Content-Type\公众 content=\"大众text/html; charset=utf-8\"大众/>

<title></title>

<script type=\"大众text/javascript\"大众>

// 跨域发送HTTP要求,从做事端获取字符串\"大众Hello World\公众

function getHello() {

var script = document.createElement('script');

script.setAttribute('src', 'http://localhost:8546/Service.ashx');

document.querySelector(\"大众head\公众).appendChild(script);

}

// 处理函数

function handler(data) {

alert(data);

// our code here...

}

</script>

</head>

<body>

<input type=\"大众button\"大众 value=\公众发送跨域HTTP要求,获取Hello World\公众 onclick=\"大众getHello()\公众 />

</body>

</html>

测试成功!

在这个测试例子中,我们利用一样平常处理程序编写了一个大略的返回Hello World的做事,然后利用动态创建<script></script>节点的办法实现了跨域HTTP要求。

由于<script>、<img>标签资源要求是异步的,以是我们就实现了一个跨域的异步HTTP要求。

但是这么做是不足的,一个页面可能会有多个HTTP要求,而上面这个示例的处理程序只有一个——handler。

不同的要求该当由不同的处理程序来处理,对上面的代码稍做修正,只须要给<script>标签的src属性中的URL添加一个参数来指定回调函数的名称就可以了:

做事端:

public void ProcessRequest(HttpContext context)

{

context.Response.ContentType = \"大众text/plain\公众;

// 前端指定的回调函数名称

var callbackFuncName = context.Request.QueryString[\"大众callback\"大众];

var responseData = \公众Hello World\公众;

// 回调脚本,形如:handler('responseData');

var scriptContent = string.Format(\"大众{0}('{1}');\"大众, callbackFuncName, responseData);

context.Response.Write(scriptContent);

}

Web客户端:

<!DOCTYPE html>

<html>

<head>

<meta http-equiv=\公众Content-Type\"大众 content=\"大众text/html; charset=utf-8\"大众/>

<title>jsonp demo</title>

<script type=\公众text/javascript\"大众>

// 跨域发送HTTP要求,从做事端获取字符串\"大众Hello World\"大众

function getHello() {

var script = document.createElement('script');

script.setAttribute('src', 'http://localhost:8546/Service.ashx?callback=handler');//callback指定回调函数名称

document.querySelector(\"大众head\"大众).appendChild(script);

}

// 处理函数

function handler(data) {

alert(data);

// our code here...

}

</script>

</head>

<body>

<input type=\"大众button\公众 value=\"大众发送跨域HTTP要求,获取Hello World\公众 onclick=\公众getHello()\"大众 />

</body>

</html>

利用jQuery的JSONP跨域

jQuery的ajax方法对JSONP式跨域进行了封装,如果利用jQuery进行JSONP事理式的跨域HTTP要求,将会变得非常大略:

<!DOCTYPE html>

<html>

<head>

<meta http-equiv=\"大众Content-Type\"大众 content=\"大众text/html; charset=utf-8\"大众/>

<title>jQuery jsonp demo</title>

<script src=\公众jquery-1.11.0.min.js\公众></script>

<script type=\"大众text/javascript\公众>

$.ajax({

type: \"大众get\公众,

async: false,

url: \公众http://localhost:8546/Service.ashx\"大众,

dataType: \"大众jsonp\公众,

success: function (data) {

alert(data);

},

error: function () {

alert('fail');

}

});

</script>

</head>

<body>

利用jQuery统统将会变得非常大略。

</body>

</html>

只须要将dataType设置为\"大众jsonp\公众就可以进行跨域要求了,统统就像发送非跨域要求那样大略。

jQuery为我们封装好了回调函数,一样平常情形下不须要我们单独去写,如果你不想在success中处理,想单独写处理函数,那么可以通过设置这2个参数来实现:

jsonp: \"大众callback\公众,//通报给做事真个回调函数名称参数,如果不设置此项,则默认是\"大众callback\"大众jsonpCallback: \公众handler\公众,//通报给做事真个回调函数名称,如果不设置此项,则默认是形如\"大众jQuery111007837897759742043_1460657212499\"大众的由jQuery自动天生的函数名称

必须要强调的是:

1.JSONP虽然看起来很像一样平常的ajax要求,但其事理不同,JSONP是对文章第一小节事理的封装,是通过<script>标签的动态加载来实现的跨域要求,而一样平常的ajax要求是通过XMLHttpRequest工具进行;

2.JSONP不是一种标准协议,其安全性和稳定性都不如 W3C 推举的 CORS;

3.JSONP不支持POST要求,纵然把要求类型设置为post,实在质上仍旧是一个get要求。