论坛首页 Web前端技术论坛

关于ajax返回值处理的思考

浏览 9829 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-04-13  


在高手林立的javaeye舞台上,ajax实在是不值得提及的一个小东西,而现在旧事重提,就是想提提另一种ajax思维。

 

我们知道在ajax应用中,概括起来就是客户端一个异步请求,服务器端返回请求的数据,然后通过js和css更新页面显示的内容,在这个简单的过程中,略显麻烦的往往是返回的数据内容在页面的解析问题。如果只是简单的数字或string,我们用字符串返回就可以了,但当遇到返回的数据结构比较复杂时,使用字符串返回就会使得代码丑陋,难以维护,若使用xml或json格式返回会稍好一些,但这样的数据结构仍然在前端需要一定的解析处理,我觉得有时候我们把服务器端的数据直接构造好再写回到客户端会更加地简单和方便。

 

这种思路的具体做法就是,使用freemarker模板技术,把ajax请求的数据通过模板渲染好,然后将渲染好的模板数据写回到客户端,这样一来,不管你ajax请求的数据格式是何种数据结构,都交给freemarker来处理它,所以你ajax请求的是Map,List,Model,String等都没有问题,通过模板标签组织好数据,然后就当成字符串写回到客户端,对于客户端而言,我的每个ajax请求,你都是给我返回已经组织好数据结构的字符串,我不用做任何处理,就可以直接使用,这简化了前端的解析工作,相当于使用freemarker来屏蔽了这项麻烦的解析工作,而对于freemarker而言,它有很强的数据表现能力,可以循环迭代,逻辑判断等,所以数据的表现由freemarker来完成并不会很难。

 

我知道在很多项目中,常常会采用在java代码中拼凑html标签的方式将数据构造好,然后一并写向客户端,这种方式在一些开源项目中也是存在的,例如log4j,它在HTMLLayout中也是Java代码中嵌入html代码,只是这种方式不好,当html代码变更时,我们需要维护java代码,而通过模板技术,就只要维护模板就可以了。

 

然而这种思路其实也没有什么神奇的,会不会给你的开发带来便利,需要你酌情考虑,下面是这种方式一个具体使用的实例。

 

1.ajax请求处理的Action:

package com.javaeye.hnylj.action;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.apache.struts2.interceptor.ServletResponseAware;

import com.javaeye.hnylj.constant.Constant;
import com.javaeye.hnylj.ftl.FreemarkerProcessor;
import com.javaeye.hnylj.model.ProductInfo;
import com.javaeye.hnylj.service.ProductService;
import com.opensymphony.xwork2.ActionSupport;

/**
 * ajax返回数据处理测试
 * 
 * @since Apr 13, 2010
 */
public class AjaxNewIdeaAction extends ActionSupport implements ServletResponseAware {

	private Logger logger = Logger.getLogger(AjaxNewIdeaAction.class);
	
	private static final long serialVersionUID = 3398440661649729663L;
	
	private HttpServletResponse response;
	
	private FreemarkerProcessor freemarkerProcessor;
	
	private ProductService productService;
	
	public void setServletResponse(HttpServletResponse response) {
		this.response = response;
	}

	public ProductService getProductService() {
		return productService;
	}

	public void setProductService(ProductService productService) {
		this.productService = productService;
	}
	
	/**
	 * ajax请求处理
	 * 
	 * @return
	 * @throws Exception
	 */
	public String ajaxRequestProductList() throws Exception {
		//获取product信息,productService由spring注入
		List<ProductInfo> productInfoList = productService.queryProductByProductId();
		logger.info("product size: " + productInfoList.size());
		Map<Object, Object> productInfoMap = new HashMap<Object, Object>();
		productInfoMap.put("productInfo", productInfoList);
		freemarkerProcessor = new FreemarkerProcessor();
		//得到渲染好的模板内容
		String result = freemarkerProcessor.init(Constant.PRODUCTINFO_TEMPLATE, productInfoMap, Constant.TEMPLATE_RELATIVE_PATH);
		response.getWriter().write(result);
		productInfoMap.clear();
		logger.info("product info ajax request successfully");
		return null;
	}
}

 

2.freemarker处理类:

package com.javaeye.hnylj.ftl;

import java.io.IOException;
import java.io.StringWriter;
import java.util.Locale;
import java.util.Map;

import org.apache.struts2.ServletActionContext;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

/**
 * Freemarker处理类
 * 
 * @since Apr 13, 2010
 */
public class FreemarkerProcessor {
	
	/**
	 * 初始化模板引擎
	 * 
	 * @param ftl 模板名称
	 * @param map 模板中需要的参数集合
	 * @param relativePath 模板相对于根路径的相对路径
	 * @throws IOException
	 * @throws TemplateException
	 */
	public String init(String ftl, Map<Object,Object> map, String relativePath) throws IOException, TemplateException {
		Configuration freemarkerCfg = new Configuration();
	    freemarkerCfg.setServletContextForTemplateLoading(ServletActionContext.getServletContext(), relativePath);
	    freemarkerCfg.setEncoding(Locale.getDefault(), "UTF-8");
	    Template template = freemarkerCfg.getTemplate(ftl);
	    template.setEncoding("UTF-8");
	    
	    StringWriter result = new StringWriter();
	    template.process(map, result);
	    return result.toString();
	}
}

 

3.页面使用: 

<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<title>ajax</title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<meta http-equiv="pragma" content="no-cache"/>
	<meta http-equiv="cache-control" content="no-cache"/>
	<meta http-equiv="expires" content="0"/>    
	<script language="JavaScript" src="${pageContext.request.contextPath}/js/jquery.js"></script>
	<script language="JavaScript" src="${pageContext.request.contextPath}/js/product.js"></script>
  </head>
  
  <body>
    <div id="main_content">
	  <div class="tab_1"><a href="/index.html">首页</a></div>
	  <div class="tab_2"><a href="javascript:showProduct('flag')">产品列表</a></div>
	  <div id="flag" class="item"></div>
    </div>
  </body>
</html>

当我们点击产品列表就会发起ajax请求。

 

4.product.js内容:

function showProduct(obj) {
	$.ajax({
		url:"/product/ajaxRequestProductList.action",
		type: "POST",
		cache: false,
		success: function(html) {
		   document.getElementById(obj).innerHTML=html;
		},
	    error: function(){
	        document.getElementById(obj).innerHTML="网络连接超时,无法显示数据!";
	        return;
	    }
	});
}

 

5.productInfo.ftl模板内容:

<div class="content">
	<#if productInfo?size != 0> 
	<#list productInfo as productInfo>
	<div class="preview">
	  <div class="preview_detail">
	  	${productInfo.productName}
	  </div>
	  <div class="preview_detail">
	  	${productInfo.productPrice}
	  </div>
	  <div class="preview_detail">
		<a href="/product/productDetail.action?productId=${productInfo.productId}">
		 ${productInfo.productPrice}
		</a>
	  </div>
	</div>
	</#list>
	<#else>    
	<div class="preview">
	  抱歉,没有找到相关信息!
	</div>
	</#if>
</div>

 

主要代码如上,省去了一些其他的代码。

在具体项目中,并不是每一个ajax都采用这种方式,推荐那些返回的数据结构比较复杂时采用这种方式,例如一个ajax的分页列表,一个ajax动态菜单树就可以使用它。另外,这种方式会不会带来性能的问题,我觉得不需要太担忧,因为它最多就是多使用了一点内存。 

 

 

   发表时间:2010-04-14  
不错的想法,相当于我们所说的MVC中的V再细化,细化为渲染层(由freemarker完成)和展现层(由js完成),也可以说是把之前用js直接处理action返回的json/xml代码分离成由freemarker来渲染布局,然后再用相对简单的js代码进行展现,对于复杂的数据结构json/xml返回是个不错的选择
0 请登录后投票
   发表时间:2010-04-14  
最最根本的功能就是页面的局部刷新了,
0 请登录后投票
   发表时间:2010-12-22  
可以作为返回结构的子集
0 请登录后投票
   发表时间:2010-12-23  
可以用js模板,节省带宽。
0 请登录后投票
   发表时间:2010-12-23  
嗯,不错的想法,学到了一招。以前都是用代码拼凑。。。
0 请登录后投票
   发表时间:2011-01-17  
请将Constant.PRODUCTINFO_TEMPLATE代码提交上来。谢谢
0 请登录后投票
   发表时间:2011-01-17  
这种方式在互联网应用上我觉得是不行的,浪费了很多带宽,如果做企业级的应用是可以的
0 请登录后投票
   发表时间:2011-01-17   最后修改:2011-01-17
oming 写道
请将Constant.PRODUCTINFO_TEMPLATE代码提交上来。谢谢


这个类里面就是几个常量,
其中
Constant.PRODUCTINFO_TEMPLATE 代表模板文件的名称,例如:index.ftl
Constant.TEMPLATE_RELATIVE_PATH 代表模板文件相对于根路径的相对路径,例如:/res/ftl
0 请登录后投票
   发表时间:2011-01-17  
  兄弟,我们已经这么做了2年了。效果挺好的。省去了中间的数据转换。
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics