什么是跨域?

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

什么是JSONP?

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

JSONP跨域的基本原理

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

举个例子,假如需要从服务器(http://www.a.com/user?id=123)获取的数据如下:

  1. {"id": 123, "name" : 张三, "age": 17}

那么,使用JSONP方式请求(http://www.a.com/user?id=123?callback=foo)的数据将会是如下:

  1. foo({"id": 123, "name" : 张三, "age": 17});

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

  1. 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请求。

使用JSONP实现跨域的更多相关文章

  1. 使用XHR2或Jsonp实现跨域以及实现原理

    我们直接使用XMLHttpRequset请求外部接口 会发现 报这个错误 其实浏览器成功发送请求并拿回了数据  只是浏览器的同源策略 禁止了获取  在xhr2 服务器端支持跨域 需要在响应头增加 Ac ...

  2. JSONP实现跨域

    首先提出:什么是跨域?如何解决跨域? 跨域可以简单的理解为从一个域名访问另一个域名,由于javascript的同源政策的限制,出于安全的考虑,不允许浏览器这么做.比如a.com 域名下的js无法操作b ...

  3. 跨域解决方案二:使用JSONP实现跨域

    跨域的实现方式有多种,除了 上篇文章 提到的CORS外,常见的还有JSONP.HTML5.Flash.iframe.xhr2等. 这篇文章对JSONP的跨域原理进行了探索,并将我的心得记录在这里和大家 ...

  4. 转(JSONP处理跨域事件)

     前言: 由于Sencha Touch 2这种开发模式的特性,基本决定了它原生的数据交互行为几乎只能通过AJAX来实现. 当然了,通过调用强大的PhoneGap插件然后打包,你可以实现100%的Soc ...

  5. jsonp实现跨域访问

    要实现JSONP跨域访问,首先就要了解什么是跨域?然后JSONP与JSON的关系? 1.什么是跨域? 跨域简单的说就是一个域名下的程序和另一个域名下的程序做数据交互.比如说:现有一个http://ww ...

  6. 关于使用Jsonp做跨域请求

    今天在使用Jsonp做跨域请求的练习时碰上这样一个问题 代码如下 <!DOCTYPE html> <html> <head> <meta charset=&q ...

  7. AJAX跨域问题解决方法(2)——JSONP解决跨域

    JSONP是什么?JSON全称为JSON with Padding,是JSON的一种补充的使用方式,不是官方协议. 使用JSONP服务器后台要改动吗?JSONP不同于一般的ajax请求返回json对象 ...

  8. 跨域 - 自定义 jsonp实现跨域

    问题:在现代浏览器中默认是不允许跨域. 办法:通过jsonp实现跨域   在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的.但是,在页面上引入不同域上的js脚本文件却是 ...

  9. 什么是同源策略,什么是跨域,如何跨域,Jsonp/CORS跨域

    同源策略 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响. 可以说Web是构建在同源策略基础之上 ...

随机推荐

  1. 从Vue.js源码角度再看数据绑定

    写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出.文章的原地址:https://github.com/an ...

  2. header 头各种类型文件下载

    function down_file($url,$type='application/zip'){     header("Cache-Control: public");     ...

  3. python入门学习笔记(二)

    6.6替换元素 7.tuple类型 7.1创建tuple 7.2创建单元素tuple    7.3"可变"的tuple 8.条件判断和循环 8.1,if语句   8.2,if... ...

  4. 项目构建工具Maven

  5. Spring MVC 原理

    一.什么是springmvc springMVC是spring框架的一个模块,springMVC和spring无需通过中间整合层进行开发. springMVC是一个基于mvc的web框架. Sprin ...

  6. AppCompatActivity 去掉标题栏和EditText弹出软键盘遮住输入框问题

    1. AppCompatActivity去掉标题栏 此处除掉标题栏,需要注意一点,AppCompactActivity是继承自Activity.然而,AppCompactActivity据查看网上资料 ...

  7. Java中子类能继承父类的私有属性吗?

    前段时间去听老师讲课的时候,老师告诉我子类是可以继承父类所有的属性和方法的.当时我是极其疑惑的,因为之前学校考试时这个考点我记得很清楚:子类只能继承父类的非私有属性和方法.老师给我的解释是这样的--先 ...

  8. linux web服务器及LAMP动态网站平台搭建

    (vim补:)vim另存为:x,x w 目标文件路径vim批量删除x:x,x d Web服务:基于B/S架构的web通信服务端:支持HTTP协议的网页提供程序客户端:按标记规范显示网页的浏览器程序客户 ...

  9. 关于 frame的一些基本知识

    关于 frame的一些基本知识只是摘抄了一部分,供初学者参考. b.帧速率: 帧速率是每秒显示的图像数.标准影片(NTSC) 是29.97 帧第秒 (fps),电影是每秒24 帧fps.欧洲标准是(P ...

  10. Davinci DM6446开发攻略-UBOOT-2009.03移植2 nand flash的烧写

      很长一段时间没有更新博客了,是因为要推出新开发方案和做好客户服务工作,忙得不易乐乎.有关DAVINCI U-BOOT的移植,以前写过一篇u-boot-1.3.4(2008年的),其实和这个u-bo ...