在进行AJAX的时候会经常产生这样一个报错:

  看红字,这是浏览器的同源策略,使跨域进行的AJAX无效。注意,不是不发送AJAX请求(其实就是HTTP请求),而是请求了,也返回了,但浏览器‘咔擦’一声,下面没有了。对比下fiddler和浏览器抓的包的异同:
  fiddler:

  

  chrome:

  简而言之,浏览器这边就是头(response header)给看,身体(response body)不给看。

  什么是同源策略?为什么会有同源策略?这一点在吴翰清老师著的《白帽子讲Web安全》一书中由阐述,这里就不赘述了。下面要做的,就是使用JSONP让上面的报错消失,按正确的流程进行下去。

  首先介绍下我这里的环境,两个Web服务器,Tomcat监听8081,Node监听3000,Tomcat这边实现一个处理AJAX的JSP文件,很简单,返回一个JSON
  

<%@ page contentType="application/json; charset=utf-8" %>
{"status": true}

  Tomcat的页面对这个URL发出AJAX请求,并打印出了返回值

  但Node的页面发出AJAX请求,则像上面那样报错了,因为AJAX有同源策略保护。怎么绕过这个保护呢?平时我们页面引入的CSS、JS可能是从其他的服务器比如静态服务器、CDN获取内容,都在不同的域,可知页面内的标签引入JS是没有同源策略一说的,而且也是进行request和处理response,于是我们把这个AJAX请求改为如下代码:

var script = document.createElement('script');
script.src = 'http://localhost:8081/test/true.jsp';
document.body.insertBefore(script, document.body.lastChild);

  但还是残忍的报错了

  因为返回的JSON({"status": true})成为了一个独立的js片段,而这个片段明显是不符合语法的,如果返回的是符合语法规范的处理JSON的js片段而不仅仅是JSON就好了。比如我们将服务器端的代码改成这样:

<%@ page contentType="application/javascript; charset=utf-8" %>
console.log({"status": true});

  再在Node的页面进行AJAX

  目的是达到了,但问题是,这个AJAX的servlet不仅返回了数据,还返回了行为,难道我要把处理DOM的js写在这里面吗?页面重构了又跑到这里来修改?问题太美不敢想,所以请求成功的方法必须写在页面的js里面,比如这样

function callback(data) {
console.log(data);
}
var script = document.createElement('script');
script.src = 'http://localhost:8081/test/true.jsp';
document.body.insertBefore(script, document.body.lastChild);

  而服务器返回的js片段直接调用这个function就行了,这个就叫回调函数了

<%@ page contentType="application/javascript; charset=utf-8" %>
callback({"status": true});

  可以看到,这个方案比之前好多了,servlet和请求页面的耦合度低了很多,但没完全解决,比如callback这个回调函数的名字,如果把这个名字放在请求的parameter中,比如这样

function callback(data) {
console.log(data);
}
var script = document.createElement('script');
script.src = 'http://localhost:8081/test/true.jsp?cb=callback';
document.body.insertBefore(script, document.body.lastChild);

  服务器对这个parameter进行处理

<%@ page contentType="application/javascript; charset=utf-8" %>
<%= request.getParameter("cb") %>({"status": true});

  优化一下,对没有cb参数的请求仅返回JSON

<%
String callback = request.getParameter("cb");
if(null == callback) {
response.setContentType("application/json; charset=utf-8");
%>
{"status": true}
<%
}else {
response.setContentType("application/javascript; charset=utf-8");
%>
<%= callback %>({"status": true})
<%
}
%>

  那么整个JSONP的功能就实现了。但还有一点瑕疵,代码执行完html中留下了一个script标签,强迫症能忍?处女座能忍?

  解决方法:可以使用jQuery的方法,jQuery会清除掉留下的script标签。

$.ajax({
url: 'http://localhost:8081/test/true.jsp',
dataType: "jsonp",
jsonp: "cb",
success: function (data) {
console.log(data)
}
});

  也可以自己实现一个,我抛个砖,在js加载完成后删除节点。

function callback(data) {
console.log(data);
}
var script = document.createElement('script');
script.src = 'http://localhost:8081/test/true.jsp?cb=callback';
document.body.insertBefore(script, document.body.lastChild);
script.onload = function(){
this.parentNode.removeChild(this);
};

  至此,不知道有人发现没有,JSONP这种方式有一个致命的缺陷:就是由于它是通过引入script节点实现的,所以只支持GET方法。如果你任性,你无理取闹,你一定要用post跨域,那么只能考虑使用CORS了。

  JSONP的东西就到此结束了,其实做完才发现,实际上这是个很简单的概念,取了个比较唬人的名字而已。

JSONP的实现流程的更多相关文章

  1. JSONP原理小记

    大家都知道JSONP(JSON with padding参数式JSON)是跨域传输数据的方法,jq等很多类库都封装了JSONP的方法,但是他的原理是怎样的呢?下面举个我认为最浅显的栗子,大家看过了都会 ...

  2. jQuery源码分析系列(36) : Ajax - 类型转化器

    什么是类型转化器? jQuery支持不同格式的数据返回形式,比如dataType为 xml, json,jsonp,script, or html 但是浏览器的XMLHttpRequest对象对数据的 ...

  3. 无需CORS,用nginx解决跨域问题,轻松实现低代码开发的前后端分离

    近年来,前后端分离已经成为中大型软件项目开发的最佳实践. 在技术层面,前后端分离指在同一个Web系统中,前端服务器和后端服务器采用不同的技术栈,利用标准的WebAPI完成协同工作.这种前后端分离的&q ...

  4. jquery 中jsonp的实现原理

    在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,即一般的 ajax是不能进行跨域请求的.但 img.iframe .script等标签是个例外,这些标签可以通过 src属性请求到其 ...

  5. jQuery源码分析系列(34) : Ajax - 预处理jsonp

    上一章大概讲了前置过滤器和请求分发器的作用,这一章主要是具体分析每种对应的处理方式 $.ajax()调用不同类型的响应,被传递到成功处理函数之前,会经过不同种类的预处理(prefilters). 预处 ...

  6. jQuery源码分析系列(35) : Ajax - jsonp的实现与原理

    ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本 json核心就是:允许用户传递一个callba ...

  7. jQuery-1.9.1源码分析系列(十六)ajax——ajax处理流程以及核心函数

    先来看一看jQuery的ajax核心处理流程($.ajax) a. ajax( [url,] options )执行流程 第一步,为传递的参数做适配.url可以包含在options中 //传递的参数只 ...

  8. 从 AJAX 到 JSONP的基础学习

    目录索引: 一.AJAX的概念二.POST && GET三.原生实现AJAX简单示例 3.1 实现代码 3.2 重点说明四.框架隐藏域 4.1 基本概念 4.2 后台写入脚本 4.3 ...

  9. jsonp跨域问题

    JSONP是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式). 同源策略限制 ...

随机推荐

  1. docke镜像上传到dockerhub仓库和阿里云docker仓库的方法

    操作指南   1.  登录阿里云docker registry: $ sudo docker login --username=linjiaxin897591495 registry.cn-hangz ...

  2. ASP.NET在母版页或内容页上获取控件ID

    原本想给一个button添加一个confirm,不同的分数提示不同的信息(大于80合格,小于80不合格,提示是否提交),最开始用了button.Atribute.Add();但是它每次获取到的是lab ...

  3. zabbix_server---微信报警

    (1)         企业应用-创建应用 1.除了对个人添加微信报警之外,还可以添加不同管理组,接受同一个应用推送的消息, 成员账号,组织部门ID,应用Agent ID,CorpID和Secret, ...

  4. eclipse导入lombok后打不开(如果你的lombok不是最新的,那就来下载最新的)

    如果你的不是最新的,去这里下载最新版的,先点击左上角的Download红方块,然后再点击下图中的位置 https://projectlombok.org/ 下载完后把eclipse关掉,双击下载的ja ...

  5. opencv 小程序170323

    1.滤波 GaussianBlur(imgThresholded, imgThresholded, Size(5, 5), 0, 0);//高斯滤波 medianBlur(imgThresholded ...

  6. AKOJ -- 1529 -- 寻找最大数

    1529: 寻找最大数 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 107  Solved: 53 上一题SubmitStatus标签打分编辑题目信 ...

  7. python_装饰器_语法糖

    什么是高阶函数? -- 把函数名当做参数传给另外一个函数,在另外一个函数中通过参数调用执行 #!/usr/bin/python3 __author__ = 'beimenchuixue' __blog ...

  8. python 集合 set

    集合 set:可变集合与不可变集合 可变集合:可以向集合中添加删除元素,非可哈希的,不能用作字典的键,也不能做其他集合的元素. 把不同的元素组成一起形成集合,集合不记录元素的位置或者插入点,也就是不能 ...

  9. 【leetcode】260. Single Number III

    Given an array of numbers nums, in which exactly two elements appear only once and all the other ele ...

  10. 使用Jetty运行Java Web项目(Maven)

    目前流行的两款IDE: Eclipse和IntelliJ IDEA 2. IntelliJ IDEA