深入理解Ajax结合JSONP实现跨域请求

一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你请求的是什么东西,静态html,动态web资源,只要是跨域请求,一律不准;

写一个跨域无权限访问的DEMO:

本地js客户端代码:

//生成XMLHttpRequest
var xhr = new XMLHttpRequest();
//注册监听
          xhr.onreadystatechange=function(){
             if(xhr.readyState==4){
                 if(xhr.status==200||xhr.status==304){
                //获取服务器的响应
                      var data = xhr.responseText;
                      alert(data);
                 }
             }
          };
                 //客户端与服务端建立连接
     xhr.open("post","http://localhost:8080/HadoopClass/mainServlet",true);
            //发生post设置请求头
    xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
          //发送请求参数
    xhr.send("username=虛竹&age=21");

远程服务短代码:

@WebServlet("/mainServlet")
public class MainServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public MainServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    String username = request.getParameter("username");
        String age = request.getParameter("age");
   username = new String(username.getBytes("ISO-8859-1"),"utf8");
        System.out.println(username);
        System.out.println(age);
        response.setContentType("text/html;charset=utf8");
        response.setCharacterEncoding("utf8");
        //向客户端生成一个响应
        PrintWriter pw = response.getWriter();
        pw.println("请求成功");
    }

}

如上就是一个再简单不过的Ajax请求了,如果服务端的代码和Ajax客户端在同一工程下则Ajax客户端接受到服务器的响应数据没问题。但当两者不在同一域名下,则Ajax客户端不能接受到服务端的响应。xhr.status永远是0,不会出现HTTP协议中的状态码,如200,304等。

现在我们来看什么是JSONP

JSON(JavaScript Object Notation)和JSONP(JSON with Padding)虽然只有一个字母的差别,但其实他们根本不是一回事儿:JSON是一种数据交换格式,而JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议。

我们发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>);

于是可以判断,当前阶段如果想通过纯web端跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;

这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。

客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。

为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

如上的执行过程听起来很模糊:我们写一个如上的实例的DEMO来完成。

远程服务端的代码:

@WebServlet("/mainServlet")
public class MainServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public MainServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 响应类型  
        response.setHeader("Access-Control-Allow-Methods","GET,POST");  
        // 响应头设置  
        response.setHeader("Access-Control-Allow-Headers","x-requested-with,content-type");*/
        PrintWriter pw = response.getWriter();
        pw.println("cb({result:6666666})");
    }

}

本地客户端的代码:

  <script type="text/javascript">
    var cb = function(data){
        alert('我是本地函数,可以被跨域的文件调用,远程js带来的数据是:' + data.result);
    };
    </script>
    <script type="text/javascript" src="http://localhost:8080/HadoopClass/mainServlet"></script>
</head>

运行之后发现,我们成功的获取到了远程服务器的响应。

跨域远程获取数据的目的基本实现了,但是又一个问题出现了,我怎么让远程服务器知道它应该调用的本地函数叫什么名字呢?毕竟是jsonp的服务者都要面对很多服务对象,而这些服务对象各自的本地函数都不相同啊?解决方法是我们要本地函数的名字发送给远程服务器,然后让远程服务器根据名字动态的构建JSON。

本地客户端代码:

<script type="text/javascript">
    var showMessage = function(data){
        alert('我是本地函数,可以被跨域的文件调用,远程js带来的数据是:' + data.result);
    };
</script>
    <script type="text/javascript" src="http://localhost:8080/HadoopClass/mainServlet?callback=showMessage">
    </script>

远程服务端:

@WebServlet("/mainServlet")
public class MainServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public MainServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String callback = request.getParameter("callback");
        PrintWriter pw = response.getWriter();
        pw.println(callback+"({result:abc})");
    }

}

此时我们也不难看出,在使用JSONP来完成跨域请求时,GET比POST更方便
如何你的请求是在监听某个操作后发生的,此时动态拼接一个<script>即可:

 // 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
    var url = "http://localhost:8080/HadoopClass/mainServlet?callback=showMessage";
    // 创建script标签,设置其属性
    var script = document.createElement('script');
    script.setAttribute('src', url);
    // 把script标签加入head,此时调用开始
    document.getElementsByTagName('head')[0].appendChild(script);

到这里为止的话,相信你已经能够理解jsonp的客户端实现原理了吧?剩下的就是如何把代码封装一下,以便于与用户界面交互,从而实现多次和重复调用。

_由于在开发中我们经常使用jQuery,而jQuery对JSONP进行了一步封装.使用起来更加简单

<html xmlns="http://www.w3.org/1999/xhtml" >
 <head>
     <title>Untitled Page</title>
  <script type="text/javascript" src=jquery.min.js"></script>
      <script type="text/javascript">
  $.ajax({
  type:"get",
  url:"http://localhost:8080/HadoopClass/mainServlet",/*url写异域的请求地址*/
  dataType:"jsonp",/*加上datatype*/
  jsonpCallback:"showMessage",/*设置一个回调函数,名字随便取,和下面的函数里的名字相同就行*/
  success:function(data,textStatus){
          alert(data.result);
         alert(textStatus);
  }
 });
 </script>
   </head>
  <body>
  </body>
 </html>

这次我们没有写showMessage这个函数呢?而且竟然也运行成功了。这就是jQuery的功劳了,jquery在处理jsonp类型的ajax时,自动帮你生成回调函数并把数据取出来供success属性方法来调用。

最后铭记:JSONP不支持POST请求仅仅执行GET请求

通过如上的联系,相信你已经很容易理解JSONP的使用方法了。但JSONP不是解决跨域请求的唯一方法,最简单粗暴的方法就是让服务器支持跨域:

// 指定允许其他域名访问  
        response.setHeader("Access-Control-Allow-Origin","*");
        // 响应类型  
        response.setHeader("Access-Control-Allow-Methods","GET,POST");  
        // 响应头设置  
        response.setHeader("Access-Control-Allow-Headers","x-requested-with,content-type");

Access-Control-Allow-Origin:* 表示允许任何域名跨域访问

如果需要指定某域名才允许跨域访问,只需把Access-Control-Allow-Origin:*改为Access-Control-Allow-Origin:允许的域名

例如:header(‘Access-Control-Allow-Origin:http://www.client.com‘);

这样服务器就支持跨域请求了,不需要利用JSONP了。
最后总结:

  • 1.ajax和jsonp这两种技术在调用方式上“看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery都把jsonp作为ajax的一种形式进行了封装;
  • 但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。
  • 所以说,其实ajax与jsonp的区别不在于是否跨域,ajax通过服务端代理一样可以实现跨域,jsonp本身也不排斥同域的数据的获取。
  • 还有就是,jsonp是一种方式或者说非强制性协议,如同ajax一样,它也不一定非要用json格式来传递数据,如果你愿意,字符串都行,只不过这样不利于用jsonp提供公开服务。

 总而言之,jsonp不是ajax的一个特例,哪怕jquery等巨头把jsonp封装进了ajax,也不能改变这一点!

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
版权声明 楚狂人Windows驱动编程基础教程 本书是免费电子书。作者保留一切权利。但在保证本书完整性(包括版权声明、前言、正文内容、后记、以及作者的信息),并不增删、改变其中任何文字内容的前提下,欢迎任何读者以任何形式(包括各种格式的文档)复制和转载本书。同时不限制利用此书赢利的行为(如收费注册下载,或者出售光盘或打印版本)。不满足此前提的任何转载、复制、赢利行为则是侵犯版权的行为。 发现本书的错漏之处,请联系作者。请不要修改本文中任何内容,不经过作者的同意发布修改后的版本。 前言 本书非常适合熟悉Windows应用编程的读者转向驱动开发。所有的内容都从最基础的编程方法入手。介绍相关的内核API,然后举出示范的例子。这本书只有不到70页,是一本非常精简的小册子。所以它并不直接指导读者开发某种特定类型的驱动程序。而是起到一个入门指导的作用。 即使都是使用C/C++语言的代码,在不同的应用环境中,常常看起来还是大相径庭。比如用TurboC++编写的DOS程序代码和用VC++编写的MFC应用程序的代码,看起来就几乎不像是同一种语言。这是由于它们所依赖的开发包不相同的缘故。 在任何情况下都以写出避免依赖的代码为最佳。这样可以避免重复劳动。但是我们在学习一种开发包的使用时,必须习惯这个环境的编码方式,以便获得充分利用这个开发包的能力。 本书的代码几乎都依赖于WDK(Windows Driver Kit)。但是不限WDK的版本。WDK还在不断的升级中。这个开发包是由微软公司免费提供的。读者可以在微软的网站上下载。 当然读者必须把WDK安装的计算机上并配置好开发环境。具体的安装和配置方法本书没有提供。因为网上已经有非常多的中文文档介绍它们。 读完这本书之后,读者一定可以更轻松的阅读其他专门的驱动程序开发的文档和相关书籍。而不至于看到大量无法理解的代码而中途放弃。如果有任何关于本书的内容的问题,读者可以随时发邮件到[email protected]或者[email protected]。能够回答的问题我一般都会答复。 写本书的时候,我和wowocock合作的一本名为《天书夜读》(在网上有一个大约20%内容的缩减电子版本)正在电子工业出版社编辑。预计还有不到一个月左右就会出版。这也是我自己所见的唯一一本中文原创的从汇编和反汇编角度来学习Windows内核编程和信息安全软件开发的书。希望读者多多支持。有想购买的读者请发邮件给我。我会在本书出版的第一时间,回复邮件告知购买的方法。 此外我正在写另一本关于Windows安全软件的驱动编程的书。但是题目还没有拟好。实际上,读者现在见到的免费版本的《Windows驱动编程基础教程》是从这本书的第一部分中节选出来的。这本书篇幅比较大,大约有600-800页。主要内容如下: 第一章驱动编程基础 第二章磁盘设备驱动 第三章磁盘还原与加密 第四章传统文件系统过滤 第五章小端口文件系统过滤 第六章文件系统保护与加密 第七章协议网络驱动 第八章物理网络驱动 第九章网络防火墙与安全连接 第十章打印机驱动与虚拟打印 第十一章视频驱动与过滤 附录A WDK的安装与驱动开发的环境配置 附录B 用WinDbg调试Windows驱动程序

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值