在进行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. swipe和swiper的区别

    swipe.js--移动WEB页面内容触摸滑动类库 参考http://www.jiawin.com/swipe-mobile-touch-slider 1.swipe只提供简单轮播切换,底部的圆点颜色 ...

  2. centos7安装docker并安装jdk和tomcat(常用命令)

    阿里专属的镜像加速 在宿主机器编辑文件:vi /etc/docker/daemon.json 阿里专属的镜像加速地址,类似于"https://91cntlkt.mirror.aliyuncs ...

  3. HPU--1280 Divisible

    题目描述 给定一个很大的整数,我想知道它能否被9整除. 输入 有t组测试数据,每组数据给定一个整数N不存在前导0.(1 <= t <= 20,1 <= N <= 10^200) ...

  4. css 定位功能position

    Static 定位 HTML元素的默认值,即没有定位,元素出现在正常的流中.静态定位的元素不会受到top, bottom, left, right影响. 相对定位Relative相对定位元素的定位是相 ...

  5. 关于 Overtrue 的拼音库 overtrue/pinyin 为何 travis 为 error【社交系统研发日记十】

    什么是ThinkSNS ? ThinkSNS(简称TS),一款全平台综合性社交系统,为国内外大中小企业和创业者提供社会化软件研发及技术解决方案,目前最新版本为ThinkSNS+(简称TS+).社交系统 ...

  6. 【社交系统ThinkSNS+研发日记三】基于 Laravel Route 的 ThinkSNS+ Component

    [社交系统ThinkSNS+研发日记系列] 一.<ThinkSNS+ 基于 Laravel master 分支,从 1 到 0,再到 0.1> 二.<基于 Laravel 开发 Th ...

  7. Linux命令的学习

    mkdir -p 创建目录 (make directorys) p递归创建 ls -l(long)d(direcitory)显示目录或者文件 cd 切换目录  从"/"开始目录,/ ...

  8. SQL语句查询某字段不同数据的个数(DISTINCT 的使用)

    今天做了一个题,学到了一个知识点: 有一个高速收费表VF,如下: 统计收费涉及的车辆有多少: SQL语句: SELECT  COUNT(DISTINCT  VchReg)  from  VF ; 其中 ...

  9. C#操作EML邮件文件实例(含HTML格式化邮件正文和附件)

    使用QQ邮箱.163邮箱等导出的EML邮件,包含了邮件的发件人.主题.内容.附件等所有信息,该实例就如何解析这些信息,并在编辑后保存做个Demo. 如下图所示,EML文件是编码后的文本文件,可以使用正 ...

  10. 电脑重装系统按什么键进U盘PE

    电脑重装系统按什么键进U盘PE... -------------------------- 各品牌电脑重装系统快捷按键进U盘PE: -------------------------- Dell电脑重 ...