支持ajax的静态页面生成

前端之家收集整理的这篇文章主要介绍了支持ajax的静态页面生成前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

目前主流的页面静态技术都是基于模板生成的,但是对于一些采用ajax+js渲染的页面,这种方法是无能为力的。要解决这个问题,首先要有一个能模拟浏览器的运行环境,其他问题都比较容易解决。能模拟浏览器的技术有好多,seleninum , htmlunit等。其中htmlunit是java开发用无界面的浏览器,速度和性能非常好,对html建模并且提供API来访问页面,点击链接等等,不需要任务驱动程序,提供javascript执行环境,现在很多支持ajax网络爬虫也是在它基础上实现的。

如何基于htmlunit实现ajax页面静态化呢?下面我用一个例子阐述吧,没什么比用代码更直接清楚。这个例子有个ajax渲染的页面页面主要有两块内容,顶部是用户信息,下面是读取osc 首页的综合资讯,基本需求是综合资讯内容静态化用户信息不需要。

index.jsp页面代码

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<Meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body  whitelist="/userServlet" >
<div id="top"></div>
<h1>osc综合资讯</h1>
<div id="content"></div>
<div>
<button  onclick="generateStaticHtml(this);">生成静态页面</button>
<script type="text/javascript">
//渲染页面
(function renderPage(){
	
	var xmlhttp = new XMLHttpRequest() ;
	xmlhttp.onreadystatechange=function()
	  {
	  if (xmlhttp.readyState==4 && xmlhttp.status==200)
	    {
	    document.getElementById("top").innerText=xmlhttp.responseText;
	    }
	  }
	xmlhttp.open("GET","${pageContext.request.contextPath }/userServlet",true);
	xmlhttp.send();
	
	var xmlhttp2 = new XMLHttpRequest() ;
	xmlhttp2.onreadystatechange=function()
	  {
	  if (xmlhttp2.readyState==4 && xmlhttp2.status==200)
	    {
	    document.getElementById("content").innerHTML=xmlhttp2.responseText;
	    }
	  }
	xmlhttp2.open("GET","${pageContext.request.contextPath }/contentServlet",true);
	xmlhttp2.send();
	
})() ;

function generateStaticHtml(btn){
	btn.innerText = "在处理中,请稍后"
	var xmlhttp = new XMLHttpRequest() ;
	xmlhttp.onreadystatechange=function()
	  {
	  if (xmlhttp.readyState==4 && xmlhttp.status==200)
	    {
		  btn.innerText ="重新生成" ;
		  window.open("${pageContext.request.contextPath }/index.html") ;
	    }
	  }
	xmlhttp.open("GET","${pageContext.request.contextPath }/generateStaticServlet",true);
	xmlhttp.send();
}

</script>
</div>
</body>
</html>




动态页面效果

注意上面图的两个ajax是加载动态内容触发,然后用javascript渲染到页面

点击"生成静态页面“按钮会触发后台调用静态组件生成静态页面(index.html)

@H_502_28@/** * 触发生成静态页面 */ protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { new StaticHtml().process("http://127.0.0.1:8080/ajax/index.jsp",request.getServletContext().getRealPath("/index.html")); }



@H_502_28@/** * 生成静态页面组件 * @author Wen * */ public class StaticHtml { //javascript 拦截 ajax 请求 private final static String ajaxInterceptJs = "(function(XHR) { " + "var open = XHR.prototype.open;" + "var send = XHR.prototype.send;" + "%s" + "XHR.prototype.open = function(method,url,async,user,pass) {" + " this._url = url;" + " open.call(this,method,pass);" + "};" + "XHR.prototype.send = function(data) {" + " if(XHR[this._url]){" + " this.abort() ;" + " delete XHR[this._url] ;" + " return ;" + " }" + " send.call(this,data);" + "}" + "})(XMLHttpRequest);"; public void process(String dynamicUrl,String staticPath) throws FailingHttpStatusCodeException,MalformedURLException,IOException { final WebClient webClient = new WebClient(); LogAjaxController logAjaxController = new LogAjaxController(); webClient.setAjaxController(logAjaxController); final HtmlPage page = webClient.getPage(dynamicUrl); //取出加载页面过程中触发的ajax url List<String> ajaxRequests = logAjaxController.getAjaxRequests(); //页面完整html(包含ajax动态获取) String htmlContent = page.asXml(); //页面还包含ajax加载代码,需要把这些jax请求拦截下来,但是有些情况是不须要拦截的就要添加到白名单 Document document = Jsoup.parse(htmlContent); String whitelistStr = document.body().attr("whitelist"); if (ajaxRequests.size() > 0 && whitelistStr != null) { String[] whitelist = whitelistStr.split(","); List<String> list = new ArrayList<String>(); for (String url : ajaxRequests) { boolean find = false; for (String wlUrl : whitelist) { if (url.indexOf(wlUrl) != -1) { find = true; break; } } if (!find) { list.add(url); } } ajaxRequests = list; } if( ajaxRequests.size() > 0 ){ Element script = new Element(Tag.valueOf("script"),""); script.attr("type","text/javascript"); StringBuilder sb = new StringBuilder() ; for(String url : ajaxRequests ){ sb.append("XHR['").append(url).append("']=true;") ; } script.text( String.format(ajaxInterceptJs,sb.toString()) ) ; document.head().prependChild(script);//注入拦截ajax js 保证拦截ajax的代码最先执行 } //写入文件 FileUtils.writeStringToFile(new File(staticPath),document.html(),"utf-8"); webClient.closeAllWindows(); } /** * 记录所有ajax请求url * * @author Wen * */ static class LogAjaxController extends NicelyResynchronizingAjaxController { private List<String> ajaxRequests = new ArrayList<String>(); @Override public boolean processSynchron(HtmlPage page,WebRequest settings,boolean async) { ajaxRequests.add(settings.getUrl().getPath()); return super.processSynchron(page,settings,async); } public List<String> getAjaxRequests() { return Collections.unmodifiableList(ajaxRequests); } } }





静态页面效果

页面效果和动态的index.jsp是一样的,但此时只有一个ajax请求刷新用户信息及访问次数,综合资讯的内容已经被静态化的。基本算是实现了我的需要。需要说明有几个地方。

如何通htmlunit取得ajax请求的url

htmlunit提供了处理ajax请求接口,我们只要简单继承NicelyResynchronizingAjaxController这个类,把ajax请求的url记录下来就可以了

静态页面也包含ajax加载综合资讯代码,这请求是处理拦截下来的

实际上静态页面会包含有跟原来页面一模一样的ajax加载动态内容代码,这些代码对于静态页面来说没有用的,因为内容都被静态化,没必要再发请求加载。我们通在生成静态页面会有页面注入以下javascript,可以把没必要的请求拦截下来(只拦截一次)

不需要被拦截ajax要怎样设置

在index.jsp 代码的body标签有个whitelist属性可设置ajax白名单,注入拦截代码时会读取这个值过虑掉,默认会拦截页面渲染触发的所有ajax请求。


四、未解决的问题

htmlunit只能调page.asXml()取页面html内容,但是这个方法不是很完美,它只是返回标准的xml代码,会把html的DOCTYPE声明删除掉,这个会导致浏览解析css会出错,临时办法把<!--?xml version="1.0" encoding="UTF-8"?-->替换回原代码页面DOCTYPE。查遍了htmlunit文档,都没有找到可以直接获取完整html源代码方法,找到的同学可以告诉我。

完成的例子代码下载http://pan.baidu.com/s/15qyPr

原文链接:https://www.f2er.com/ajax/165491.html

猜你在找的Ajax相关文章