浏览器插件,远程加载服务器js 并实现jsonp跨域调用.

    需求, 用户浏览某些特定web应用时,给用户正在浏览服务器A页面时, 通过分析用户正在访问的页面内容发送消息至服务器B应用, 分析用户当前浏览内容,并返回服务器B的检查结果. 

    IE主要使用了注册表生成浏览器插件, 火狐及google使用了GreaseMonkey, 涉及到跨域访问,使用了jsonp请求. 详细请自行百度.  

 GreaseMonkey代码截图示意(完整代码在末尾)

    以下主要是记下过程中的一些要点和注意点: 

    因为服务器A的应用是第三方的,页面内容及排版的改变我方无法控制, 于是选择在浏览器插件中动态加载服务器B的js脚本. 包括jquery脚本、页面分析脚本、页面回调函数等三个主要脚本。 

//把js脚本加载到客户浏览器的head末尾中。并加入defer(不影响dom组织)标签。

function loadScriptToHead(url) {
      var head = document.head || document.getElementsByTagName('head')[0];
      var script = document.createElement('script');
      script.setAttribute("src", url);
      script.setAttribute("defer", "defer");
      head.appendChild(script);
}

然后就可以加载自己服务器上的脚本了。

//加载jquery

var jqueryjsurl = host+'/static/appjs/modules/jquery/jquery.min.js';
 loadScriptToHead(jqueryjsurl);

//加载jsonp callback的文件

    var jsonpCallback= host+'/static/demo/jsonpCallback.js';
    loadScriptToHead(jsonpCallback);

//加载页面解析函数(jsonp)

   函数核心内容:

    document.onreadystatechange = loadingChange;//当页面加载状态改变的时候执行这个方法.
    function loadingChange() 
            { 
                if(document.readyState == "complete"){ //当页面加载状态为完全结束时进入 
                  var ajaxurl = host+'/web/home/ajaxRequestForJsonP?callback=callbackAction&param=111';
                 loadScriptToHead(ajaxurl);
                } 
            } 

按照正常的json调用,callback是需要客户定义的,我们这里也选择远程加载,如果按照正常的js加载方式,会出现callback函数无法找到的错误,所以jsonp调用必须在callback函数已经加载完成后javascript才能顺利的进行编译解析。

加载了页面解析函数还是需要根据客户服务页面具体进行分析。

需要注意的细节,

一、response的设置:response.setContentType("application/javascript");

       若没有该设置,同时安全配置又设置了 X-Content-Type-Options: nosniff

       浏览器执行callback函数时会报:

       MIME type ('text/plain') is not executable, and strict MIME type checking is enabled.

二、jsonp请求的url需要进行uriencoder。起码需要对中文参数进行uri编码:param = encodeURI(param);


最后是完整的脚本:

//插件代码开始
var host = "http://localhost:8082/gdsqcx";

/***
*加载js脚本文件到header中.
*/
function loadScriptToHead(url) {
	var head = document.head || document.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    script.setAttribute("src", url);
  	script.setAttribute("defer", "defer");
    head.appendChild(script);
}

  //加载jquery脚本.
  var jqueryjsurl = host+'/static/appjs/gdsqcx/modules/jquery/jquery.min.js';
  loadScriptToHead(jqueryjsurl);
  //加载跨域调用回调函数
	var jsonpCallback = host+'/static/demo/jsonpCallBack.js';
	loadScriptToHead(jsonpCallback);  

  //加载页面解析脚本.该脚本需要在页面加载完成时载入.
	document.onreadystatechange = loadingChange;//当页面加载状态改变的时候执行这个方法.
  function loadingChange(){
    	//当页面加载状态为完全结束时进入 
      if(document.readyState == "complete"){ 
        var ajaxurl = host+'/static/demo/htmlExplain.js';
        loadScriptToHead(ajaxurl);
      } 
    }
//插件代码结束

jsonpCallback.js

function callbackAction(data){
	alert(data.param+'-企业检测结果-'+data.result)
}

htmlExplain.js

var host = "http://localhost:8082/gdsqcx";

function _x(STR_XPATH) {
  var xresult = document.evaluate(STR_XPATH, document, null, XPathResult.ANY_TYPE, null);
  var xnodes = [];
  var xres;
  while (xres = xresult.iterateNext()) {
    xnodes.push(xres);
  }
  return xnodes;
}

//页面解析脚本.
function explainUserPage(){
	
  //匹配肇庆信用网页面解析
  if(location.href.indexOf('http://credit.zhaoqing.gov.cn/companyQuery/companyQuery.jspx')==0){
	  console.log('进入目标url'+location.href);

	    var memnodes = _x('//div[@class="header_login"]/span[1]/a');
	    var membername = memnodes[0]['innerText'];
	    if(!memnodes[0]||membername=='帐户注册'){
	    	alert('登陆后才能使用解析插件!');
	    	return;
	    }
	    
	    
	    //测试,只取一个.
	    var nodes = _x('//*[@id="tableForm"]/div/table/tbody/tr[1]/td[1]/a');
	    if(nodes[0]){
	    	
	    	var subjname = nodes[0]['innerText'];
		    var paramobj = {'membername':membername,'subjname':subjname};
		    var param = JSON.stringify(paramobj);
//		    param = escape(param );
		    param = encodeURI(param)
		    console.log('参数:'+param);
			   var ajaxurl = host+'/web/home/ajaxRequestForJsonP?callback=callbackAction&param='+param;
			   console.log('jsonpurl:'+ajaxurl);
			    $.ajax({
			        url: ajaxurl,
			        type: "get",
			        dataType: "jsonp", //指定服务器返回的数据类型
			        jsonp: "callback",
			        crossDomain: true,
			        jsonpCallback:"callbackAction",
			        data:{},
			        success: function (result) {
			       	 	//alert(result.param+'-企业检测结果-'+result.result)
			        },
			        error: function(){
			             alert('fail');
			        }
			 });
	    }
	    
 }
}

//执行页面解析
explainUserPage();

 

后台java示例代码

	@ResponseBody
	@RequestMapping(value="/web/home/ajaxRequestForJsonP")
	public void ajaxRequestForJsonP(String callback,String param,HttpServletResponse response){
		Map<String,String> results = new HashMap<String,String>();
		results.put("param", param);
		results.put("result", "传入企业检测正常.");
		System.out.println("接收参数:"+param);
		String returnStr = JSONObject.fromObject(results).toString();
		String callbackAction = callback+"("+returnStr+")";
		try {
			response.reset();
			response.setContentType("application/javascript");
		    response.setHeader("Pragma", "No-cache");
		    response.setHeader("Cache-Control", "no-cache");
		    response.setCharacterEncoding("UTF-8");
		    response.setDateHeader("Expires", 0);
		    response.setHeader("Access-Control-Allow-Origin", "*");//添加跨域访问
	        
	        PrintWriter out = response.getWriter();
	        System.out.println("返回回调函数:"+callbackAction);
	        out.println(callbackAction);//返回jsonp格式数据  
	        
	        out.flush();  
	        out.close();  
	      } catch (IOException e) {  
	       e.printStackTrace();  
	      }  
		
	}

 

1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;   2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如[removed]、、<iframe>);   3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;   4、恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据;   5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。   6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。   7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

it夜猫who

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值