JSONP跨域访问实现登录验证

最近在做一个手机Web项目,硬着头皮上了。现在比较流行的就是使用Phonegap+HTML5+CSS+JS/JQuery做一个看起来
native的mobile web
app。但是由于时间急,而且这些东西都不是很熟悉,再加上这只是对已有web网站的mobile化,因此采用了Phonegap+几乎纯server端
页面的方式,做起来省事多了,但是缺点还是有一大堆的,这里就不细说了。

浅析Ajax跨域原理及JQuery中的实现分析

AJAX
的出现使得网页可以通过在后台与服务器进行少量数据交换,实现网页的局部刷新。但是出于安全的考虑,ajax不允许跨域通信。如果尝试从不同的域请求数据,就会出现错误。如果能控制数据驻留的远程服务器并且每个请求都前往同一域,就可以避免这些安全错误。但是,如果仅停留在自己的服务器上,Web
应用程序还有什么用处呢?如果需要从多个第三方服务器收集数据时,又该怎么办?

 

 

 

 一、关于ajax跨域的思考

  1、Ajax为什么不能跨域?到底是卡在哪个环节了?(下面项目中具体说,这里先说下结论)。
Ajax其实就是向服务器发送一个GET或POST请求,然后取得服务器响应结果,返回客户端。跨域出现错误的地方正是请求到了数据,拿不回去,所以我们ajax跨域的问题就可以转化为数据怎么拿回去的问题。

 

  2、既然不能直接访问第三方站点,我们可以在服务器上面做代理,通过ajax向代理发送请求,代理获得数据后在返回给客户端,当然这是一种解决办法,但是一次请求要从客户端经过代理到第三方站点,然后再原路返回,响应速度是个问题。

 

  3、我们发现我们可以将一些js、css等文件放在第三方的服务器上面,如CDN等来加快网页的打开速度,这样是没有任何问题的,也就是说web页面可以加载放在任意站点的js、css、图片等资源,不会受到”跨域”的影响。这个时候,我们会想到:既然我们可以调用第三方站点的js,那么如果我们将数据放到第三方站点的js中不就可以将数据带到客户端了吗?

 

下面我们来做一个实验,来验证一下我们的猜想成不成立:

 

  打开Visual
Studio,新建一个Web项目,这里用WebForm,然后我们在项目中添加一个名为remoteJs的js文件,写入如下代码:

 

function GetRemoteData() {

    return “remote data”;

}

很简单,就一个方法,返回一个字符串,下面我们来写一个客户端调用,既然是跨域,那就写个html静态页面来测试吧,新建local.html,输入以下代码:

 

复制代码

<!DOCTYPE html>

<html>

<head>

    <title>本地站点</title>

    <meta charset=”UTF-8″>

    <script type=”text/javascript”
src=”;

</head>

<script type=”text/javascript”>

    var data = GetRemoteData();

    alert(data);

</script>

<body>

 

</body>

</html>

复制代码

让我们的Web项目跑起来,然后打开local.html,可以看到弹出一个窗口,显示信息remote
data。这里证明我们的想法是正确的。接下来的问题是,我们如何根据需要发送请求和获取请求的结果呢?下面我们来认识一下JSONP。

 

 

 

 二、JSONP

 1、什么是JSONP

 

  JSONP(JSON with
Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。其核心思想是利用JS标签里面的跨域特性进行跨域数据访问,在JS标签里面存在的是一个跨域的URL,实际执行的时候通过这个URL获得一段字符串,这段返回的字符串必须是一个合法的JS调用,通过EVAL这个字符串来完成对获得的数据的处理。

 

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

 

 

 

2、JSONP的实现

 

  下面我们通过一个例子来说明一下JSONP是如何实现ajax跨域请求的。这里我们模拟图书馆图书的查询,在刚刚我们建立的web项目里面添加一个名为SearchBook的一般处理程序,写入如下代码:

 

复制代码

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

 

namespace BookLibrary

{

    public class SearchBook : IHttpHandler

    {

 

        public void ProcessRequest(HttpContext context)

        {

            context.Response.ContentType = “text/plain”;

            string callback = context.Request[“callback”];

            context.Response.Write(callback +
“({‘BookName’:’English’,’Pages’:562})”);

        }

 

        public bool IsReusable

        {

            get

            {

                return false;

            }

        }

    }

}

复制代码

暂时先不解释,我们写完客户端看到效果后在详细说明,然后修改刚刚的local.html,代码如下:

 

复制代码

<!DOCTYPE html>

<html>

<head>

    <title>跨域请求</title>

    <meta charset=”UTF-8″>

</head>

<body>

<input type=”button” value=”发送请求” onclick=”GetAjaxData();” />

</body>

<script type=”text/javascript”>

    var GetData = function (data) {

      alert(data.BookName + ” ” + data.Pages);

   };

    function GetAjaxData(){

        var url =
“”;

        var script = document.createElement(‘script’);

        script.setAttribute(‘src’, url);

        document.getElementsByTagName(‘head’)[0].appendChild(script); 

    }

   

</script>

</html>

  

复制代码

这个html页面有一个按钮,绑定方法GetAjaxData,当我们单击发送请求时,就会向Web站点发送请求,获取查询的数据,我们让Web站点跑起来,然后打开local.html,单击按钮,看到弹出如下信息:

 

 

 

我们成功的取得了web站点的数据,实现了跨域请求。下面我们来说一下他的实现原理:

 

var url = “”;

这一行代码我们定义了请求的url,问号前面的是web站点一般处理程序SearchBook的地址,问号后面我们传入了一个参数callback,值为GetData,也就是我们上面定义的方法名,及回调函数名称。当然我们可以传入更多的参数。

 

var script = document.createElement(‘script’);

        script.setAttribute(‘src’, url);

        document.getElementsByTagName(‘head’)[0].appendChild(script); 

这三行代码就是添加script节点,url指向第三方站点,执行结果如图:

 

 

 

那么我们刚刚写的一般处理程序返回的结果就不用说了吧,他就是返回一个字符串,内容为:

 

 

 

看到这里清楚了吧,就是第三方站点生成一个对回调函数的调用,传入查询结果,然后通过<script>加载到客户端执行。看到这里是不是想到了什么?是不是和C#里面的委托有些共同点?整体流程如图:

 

 

 

可以看到,整个过程,本地站点一直处于主动的地位,主动的发送请求,主动的加载远程js.而第三方站点则处于被动的响应。

 

 

 

3、普通Ajax请求在哪个环节出错了

 

下面,我们用JQuery的ajax来说明一下ajax请求到底是卡在哪个环节了,修改GetAjaxData方法如下:

 

复制代码

           $.ajax({

                type: “get”,

                async: false,

                url: “”,

                dataType: “text”,

                success: function (data) {

                    alert(data.BookName + ” ” + data.Pages);

                },

                error: function () {

                    alert(‘fail’);

                }

            });

复制代码

在SearchBook里面context.Response.Write(callback +
“({‘BookName’:’English’,’Pages’:562})”);这行下端点,然后运行,会发现可以走到断点,然后就出错了。

 

 

 

4、用JQuery实现ajax跨域

 

其实JQuery里面也封装了跨域的ajax方法,我们来看一下上面的方法用JQuery怎么写:

 

复制代码

<script type=”text/javascript”>

    function GetAjaxData() {

        $.ajax({

            type: “get”,

            async: false,

            url: “”,

            dataType: “jsonp”,

            jsonp:
“callback”,//传递给请求处理程序或页面的,标识jsonp回调函数名(一般为:callback)

            jsonpCallback: “GetData”,//callback的function名称

            success: function (data) {

                alert(data.BookName + ” ” + data.Pages);

            },

            error: function () {

                alert(‘fail’);

            }

        });

    }

</script>

复制代码

注意,JQuery写法里面Url后面就不用再写?来传递参数了,jsonp的值相当于?后面的值及参数名称,jsonpCallback的值就是参数的value.success就是执行成功后调用的方法。

 

哎,不对啊,怎么没有GetData方法了?JQuery到底是怎么实现的呢?下面我们来调试一下JQuery,来看一下里面是怎么实现的,调试js,当然还是要用Chrome,看图:

 

 

 

这张图中,我们看到有个对象s,在做url拼接操作,看到选中那行了吧,?后面拼的是s.jsonp,最后拼接的是callbackName.继续向下走:

 

 

 

我们看到s.url的值,为拼接后的值,是不是和我们自己写的js代码里面的url一模一样,继续向下走:

 

 

 

我们看到JQuery又在刚刚的url后面添加下划线等号,然后又跟了一串数字,至于什么用,我也说不上来,继续向下走:

 

 

 

看到了什么,success方法,哈哈,这是JQuery在变量参数,继续走:

 

 

 

看到什么了?没错,这就是JQuery最终调用的方法,最后一行代码,添加了script节点,和我自己写js实现的原理一样。继续向下走,看看还有什么:

 

 

 

看到JQuery执行完后,又删除了刚刚添加的script节点,还是JQuery想的周到啊~~ 

 

 下面我们来看一下,我们自己写的js执行后的DOM结构:

 

 

 

看到了吧,script节点会随着请求的次数一路飙升,不过并不会引起错误,刷新后就消失了。而JQuery执行后,DOM结构是不变的。

 

 

 

 三、总结

  
 1、ajax和jsonp这两种技术在调用方式上“看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装;

 

  2、ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是通过HTTP来动态添加<script>标签来调用服务器提供的js脚本。

 

  3、其实ajax与jsonp的区别不在于是否跨域,ajax通过服务端代理一样可以实现跨域,jsonp本身也不排斥同域的数据的获取。

 

  4、jsonp是一种方式或者说非强制性协议,如同ajax一样,它也不一定非要用json格式来传递数据,如果你愿意,字符串都行,只不过这样不利于用jsonp提供公开服务。

 

  5、jsonp整个过程中,本地站点一直处于主动的地位,主动的发送请求,主动的加载远程js.而第三方站点则处于被动的响应。

 

 

AJAX
的出现使得网页可以通过在后台与服务器进行少量数据交换,实现网页的局部刷新。但是出于安全…

本文章来给大家详细jquery中的ajax跨域请求,
在JQuery对于Ajax的跨域请求有两类解决方案,不过都是只支持get方式。分别是JQuery的jquery.ajax
jsonp格式和jquery.getScript方式。

唠叨之前首先声明一点,本人不是教授,也没有大量研究过,因此如有说错的地方,请指出,不甚感谢。

ajax要在各种浏览器下都实现完美的跨域需要借助于jsonp技术,jsonp实质是请求一个js脚本文件,在js文件载入完毕时执行某个函数,这样就可以完美的规则跨域问题了。

好了,这篇随笔要展示的是跨域访问的一种实现,这是针对传统ajax不支持跨域访问而提出的。当然你也可以用别的技术实现跨域访问,比如说websocket,只是这种实现比较费lib。只用一个2Kb的jsonp包就能跨域访问,何乐而不为呢。

什么是jsonp格式呢?API原文:如果获取的数据文件存放在远程服务器上(域名不同,也就是跨域获取数据),则需要使用jsonp类型。使用这种类型的话,会创建一个查询字符串参数
callback=?
,这个参数会加在请求的URL后面。服务器端应当在JSON数据前加上回调函数名,以便完成一个有效的JSONP请求。意思就是远程服务端需要对返回的数据做下处理,根据客户端提交的callback的参数,返回一个callback(json)的数据,而客户端将会用script的方式处理返回数据,来对json数据做处理。JQuery.getJSON也同样支持jsonp的数据方式调用。

首先来说说啥叫跨域访问,即由于浏览器的安全,不允许访问不同域下的资源,通俗的说,A网站的资源不允许被B网站所使用。不过这也不是绝对的,偶尔借个螺丝刀也是可以的,像a标签的href,script标签的src,form表单的
action属性等都是可以“借”东西的。

jquery封装了jsonp请求的发送方式,使jsonp请求和ajax请求方式几乎相同了,如下是jquery跨域请求的写法:

对于jsonp真正的原理,目前也没有很深入的去了解过。至于百度上搜的什么原理,说的都不对。比如“使用script标签加载配置对象中的跨域
url”,我想说的是为什么ajax就不能加载跨域url呢。而且我发现一个怪事,同样使用$.getJson()进行JSONP跨域访问,如果type
为get,则可以跨域访问,但如果改成post,则不行了。

 代码如下

好了,我们还是开始这个例子吧,理论什么的还是等有空google去吧。

复制代码

首先理一下简单的需求,就是本地html可以登录到server端进行验证,而且登录后还应该是在本地,这是一个典型的跨域访问。

$.ajax({
     url:”testserver.php”,
     dataType: ‘jsonp’, // 注意:JSONP <– P (lowercase)
     success:function(json){
         // do stuff with json (in this case an array)
         alert(“Success”);
     },
     error:function(){
         alert(“Error”);
     },
});

用到的技术:

注意跨域请求的一般ajax请求的区别在于dataType设置成了jsonp了

前台采用HTML+CSS+JQuery + JQuery Mobile,即做成一个mobile web
app,可以在手机上进行登录,需要使用Phonegap进行打包。

对应的服务器端代码示例如下:

后台采用纯粹的servlet进行处理,使用tomcat做web服务器。

 代码如下

哦,忘了jsonp是主角了,那就赶紧先拉出来溜溜吧。先上登录页面,我们那可爱的jsonp就被用到submit按钮上。注意一
点,如果你不是js大神,那么使用jquery的方法$.json(…)是没法实现error方法的回调的,即如果服务器压根没开,要请求的登录验证
url根本不能访问,$.json()方法努力尝试,发现不行,于是果断不通知你就撤了。解决的方法是使用jquery-jsonp插件,一个大小只有
2K的“高科技”啊,当然大神可以自己实现。

复制代码

functionlogon(){var_url=createReqUrl();if(!_url)returnfalse;//alert(_url);showWaiting();$.jsonp({url:_url,timeout:5000,success:function(jsonData){if(jsonData[0].success==true){saveUserID(jsonData[0].retuid);//redirectturnToMenuPage();}else{$(#showError).html(jsonData[0].errorinfo);showSubmit();}},error:function(xOptions,textStatus){$(#showError).html(textStatus);showSubmit();}});}

<?php
$arr = array(“element1″,”element2”,array(“element31″,”element32”));
$arr[‘name’] = “response”;
echo $_GET[‘callback’].”(“.json_encode($arr).”);”;  // 09/01/12
corrected the statement
?>

这个方法可谓是前台登录的要害了。要做的事情是:

上面介绍的是php的,下面我来看个与asp.net的

1.获取web服务器地址配置页面中的值,再加
上表单输入框的值,最后拼接成一个url。注意这个url要带上“callback=?”,以方便后面$.jsonp的使用(当然也不是必须要这样做的,
这边不写后面可以补不足)。

前台请求代码

2.猪脚粉墨登场,具体api可以参见。由于插件作者的NB,error回调方法可以使用了。

 代码如下

我想说的是跨域访问不是那么easy的,需要双方的配合。

复制代码

首先,url中回调参数的name跟后台的获取参数名字要一致。这里,我有必要详细的说明。

$.ajax({
                type: “GET”,
                url:
“”,            
                dataType: “jsonp”,
                jsonp: “jsoncallback”,  
                success: function (result) {   
alert(result.Success);   
alert(result.Content);                               
                },

前台中,$.jsonp({

                error: function (result, status) {
                   //处理错误
                }
            });

url:callback=?
//你看到的是?,但其实使用的是默认的_jqjsp,自己参看jsonp插件的api吧

后台处理代码 ValidAccountsExists.aspx

success:function(){..},
//这个回调函数应该是众望所归,后台输出的script脚本,其实基本上回调的就是他

 

error:function(){…} //上面的猪脚挂了,就由他当替补,比如服务器根本没开

 代码如下

}

复制代码

上面斜体加粗部分都是可以由用户指定的,是可以替换的,但是他们换了,服务器端也得换。所以除非吃饱了撑着,否则。。。你懂的。

string accounts = GameRequest.GetQueryString(“accounts”);
string jsoncallback = GameRequest.GetQueryString(“jsoncallback”);
Response.ContentEncoding =System.Text.Encoding.UTF8;
Response.ContentType = “application/json”;
Response.Write(jsoncallback + “({“Success”:”True”,”Content”:”” +
accounts  + “”})”);
 Response.End();

后台,获取回调方法名使用老掉牙的request.getParameter(callback),输出的script要形如?({aa:bb,cc:dd})。

 

这么做,前后台就跟亲戚一样了,大家互相交流嘛。

上面服务器代码在输出时添加了js函数调用$_GET[‘callback’],很显然是通过对此php文件的get参数callback传递了一个js的函数名,jquery会自动设置这个callback。

奉上后台servlet的代码,让他们真正交流起来吧,哈哈。

另外jquery还提供了
$.getJSON()方法,这个方法是对上面跨域请求的封装,注意使用此方法时需要将请求的url的callback参数设置为?。

@OverrideprotectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{StringuserID=null,password=null,jsonpCallBack=null;userID=req.getParameter(userid);password=req.getParameter(password);//回调方法名要要一致jsonpCallBack=req.getParameter(callback);StringretJSONPStr=null;//如果登录过了,就不要瞎起哄了,回家待着吧。if(userID!=nullreq.getSession().getAttribute(userID)!=null){retJSONPStr=UserValidService.doLogoned();retJSONPStr=JSONStrUtil.createRetJSONP(jsonpCallBack,retJSONPStr);}else{//sbRetJSON:正确的翻译是傻逼返回jsonStringBuffersbRetJSON=newStringBuffer();UserBeanretUser=newUserBean();//流氓是不允许登陆的BooleanisSuccess=UserValidService.doValidation(userID,password,sbRetJSON,retUser);if(isSuccess){req.getSession().setAttribute(userID,retUser);}retJSONPStr=JSONStrUtil.createRetJSONP(jsonpCallBack,sbRetJSON.toString());}PrintWriterout=resp.getWriter();//System.out.println(retJSONPStr);//告诉前台,快递到了out.println(retJSONPStr);}

,
在JQuery对于Ajax的跨域请求有两类解决方案,不过都是只支持get方式。分别是JQuery的jquery.ajax
jso…

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图