日期:2014-05-16  浏览次数:20753 次

Ajax跨域请求的解决办法

由于项目需要,对项目进行优化,将项目中的一部分动态页面生成静态页面来提高访问的速度,随后这些静态页面与web服务器分开,存于其他服务器,并为其指定了二级域名。问题来了,导致以前写的ajax全部over。

? 于是埋头苦找问题,照旧,在ajax的后提方法中设置断点,不料,断点尽然跳不进,后来,将ajax方法中与后台交互的url改为全路径(不用相对路径),结果,断点顺利进入,欣喜若狂。于是将所有url改为全路径,以为问题得到解决。后来发现ajax后台能够正常执行,但是页面上的ajax回调方法却没被执行。

? 由于之前没有类似经验,于是google百度一番,发现这是一个ajax跨域问题。网上给出的不外乎以下几种方法:

1.web端代理的方式,即用户访问A网站时所产生的对B网站的跨域访问请求均提交到A网站的指定页面,由该页面代替用户页面完成交互,从而返回合适的结果。

2.iframe,解决方案就是用window.location对象的hash属性,利用JS改变hash值网页不会刷新,可以这样实现通过JS访问hash值来做到通信,大体就是AB网站各嵌入一个对方网站的iframe,然后通过连续不断的监听hash值的变化来进行通信。比如A网站通过改变B网站iframe的hash后,B网站监听到hash的变化后就进行处理,这种方式需要开发者可以控制两个网站的代码。

3.通过script标签来请求,原理就是在本域内的A内生成一个JS标签,它的SRC指向请求的另外一个域的某个页面B,这个src里面通常会加一个A页面定义好的回调函数,B返回数据即可,可以直接返回调用这个回调函数,这种跨域的通信方式被称为JSONP,此方案存在的缺陷是, script的src属性完成该调用时采取的方式时get方式,如果请求时传递的字符串过大时,可能会无法正常运行。

4.window.name,window.name是一种解决跨域数据传输的新技术,通过在iframe中加载一个跨域的HTML文件,并且在HTML文件中设置window.name的值为需要传给接受者的数据,接收者就可以取得到window.name的值并且返回,比较关键的是同源策略的影响对location的控制不受限制,所以需要加载一个代理的页面来让发送页面读取window.name.

5.使用flash,原理是JavaScript将数据提交给本域下的 Flash,通过 Flash 中转去访问其他域的接口,只需要其他域的根目录下有一个crossdomain.xml文件,文件中设置允许所有域名或允许本域访问即可。

?

?

先说说自己项目的ajax架构:采用jquery1.6.2和struts2注解的形式

后台struts2示例代码:

@SuppressWarnings("serial")
@ParentPackage("json-default")
@Results( { @Result( type = "json })

public class BaserAjaxAction extends BaseAction {

????

Map<String, String> map = new HashMap<String, String>();

/**
? * 是否登录
? *
? * @return
? * @throws Exception
? */
?public String isAreadyLogin() throws Exception {
??try {
???Object mid = this.getReq().getSession().getAttribute(
?????SessionKey.BASER_ID);
???Object mname = this.getReq().getSession().getAttribute(
?????SessionKey.USER_NAME);
???Object pic = this.getReq().getSession().getAttribute(
?????SessionKey.BASER_PIC);
???if (null != mid && null != mname) {
????map.put("result", "true");
????map.put("userId", mid.toString());
????map.put("userName", mname.toString());
????if (pic != null && !"".equals(pic)) {
?????map.put("pic", Constant.UPLOAD_USER_IMAGE_DIR + pic);
????}
???} else {
????map.put("result", "false");
???}
??} catch (Exception e) {
???logger.error("isLogin", e);
??}
??return SUCCESS;
?}

public Map<String, String> getMap() {
??return map;
?}

?public void setMap(Map<String, String> map) {
??this.map = map;
?}

}

?代码很简单,这里采用注解的方式,这是利用struts2-json-plugin这个插件,采用json的数据传递格式与前台js交互。注意:这里map当然可以根据需要定义其他的存数数据的集合和数据类型。但是一定要有get方法,否则前台将不能正常得到数据信息(好好研究下struts2-json-plugin这个插件就知道了)。很方便?

?

前台代码:

function isLogin() {
?$.ajax( {
??type : 'post',
??url : basePath+'/front/baser_ajax!isAreadyLogin.action',
??dataType : 'json',
??success : isLoginCallback
?});
}

function isLoginCallback(data) {
?var htmlCode = "";
?if (data.map.result == "true") {
??htmlCode += "<a class='login_act' target='_blank' href=\'" + basePath
????+ "/user/visterHome-" + data.map.userId + ".html'>"
????+ data.map.userName + "</a>";
??htmlCode += "<a class='login_act' href='javascript:loginOut();'>退出</a>";
??htmlCode += "<a class='file_upload' href='javascript:checkLoginOn();'>上传视频</a>";
??document.getElementById('isLogin').value = data.map.userId;
??$('#afterLoginId_').html(htmlCode);
?}
}

?

这里也很简单,就是通过url : basePath+'/front/baser_ajax!isAreadyLogin.action',跟后台的名为baser_ajax这个action中的isAreadyLogin方法交互,得到一些数据,再填充到页面,

但是对我来说,显然这以上所说的几种解决方案对我来说都不太实际,要改动很多代码,后来看到一篇关于jquery跨域的文章(忘记链接地址了),虽然说的不是很完全,但是对我帮助很大,里面说到采用第三方的一个协议jsonp. 于是就尝试,把??dataType : 'json',换成??dataType : 'jsonp',? 后台struts2注解加上params = {
??"callbackParameter", "callback" }),name = "success" 结果搞定。