尽管AJAX是Asynchronous Javascript and XML,但事实证明,在异步请求中传输XML格式的数据总让人觉得很麻烦。尽管在服务端XML的报文解析有广泛的工具支持,如JDOM, DOM4J等。但在客户端,用户需使用DOM来解析XML报文,才能获取需要的数据,并且由于XML报文在数据之外存在许多节点信息,因此传输的数据量相对较大。如果仅使用普通文本的格式传输数据,又缺乏必要的数据结构。
JSON是Javascript Object Notation, 它是一种轻量级的数据结构。由于具有易于阅读,易于机器解析的特点,它非常适合于作为数据传输的格式,特别是对于客户端使用Javascript来解析的Web应用。
AJAX应用实现大体上分为服务端和客户端两部门,客户端若使用prototype的js库,能非常简单地实现AJAX请求和响应的获取。prototype中的Form.serialize方法更能将表单中的所有字段序列化,作为发送给服务端的数据异步发送。在服务端,需要有一定的机制来返回稳定格式的报文给客户端,并且这个响应的格式必须是易于客户端来解析的,JSON格式的报文是不错的选择。
在这里,我实现了一个基于JSON的服务端响应类,以满足上述的需求,服务端代码如下:
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
/** */ /**
*
* @author ih
*
*/
public class JsonResponseUtils ... {
private static final String NODE_RESULT="result";
private static final String NODE_MESSAGE="message";
private static final String NODE_REASON="reason";
private static final String NODE_EXTRA_CONTENT="extraContent";
public static final String REASON_UNLOGON="unlogon";
public static final String REASON_ACCESS_DENY="accessDeny";
public static final String REASON_BIZ_CONSTRAINT="bizConstraint";
public static final String REASON_EXCEPTION="error";
/** *//**
* 通过response返回到客户端
* @param response
* @param responseContent
* @throws Exception
*/
public static void outputResponse(HttpServletResponse response,String responseContent)throws Exception...{
response.getWriter().write(responseContent);
}
/** *//**
* 返回json格式的报文
* @param result
* @param message
* @param reason
* @param customerizedContent
* @return
*/
@SuppressWarnings("unchecked")
public static String generateResponse(boolean result,String message,String reason,Map customerizedContent)...{
Map map=new LinkedHashMap();
map.put(NODE_RESULT,result);
if(StringUtils.isNotEmpty(message))
map.put(NODE_MESSAGE, message);
if(StringUtils.isNotEmpty(reason))
map.put(NODE_REASON,reason);
if(customerizedContent!=null)
map.put(NODE_EXTRA_CONTENT, customerizedContent);
return generateJsonObjectString(map);
}
/** *//**
* 返回处理成功的报文
* @return
*/
public static String generateSuccessResponse()...{
return generateResponse(true, null, null, null);
}
/** *//**
* 返回处理成功的报文,含成功提示信息和用户自定义内容
* @param message
* @param customerizedContent
* @return
*/
@SuppressWarnings("unchecked")
public static String generateSuccessResponse(String message,Map customerizedContent)...{
return generateResponse(true, message, null, customerizedContent);
}
/** *//**
* 返回处理成功的报文,含成功提示信息
* @param message
* @return
*/
public static String generateSuccessResponse(String message)...{
return generateResponse(true, message, null, null);
}
/** *//**
* 返回处理失败的报文, 原因为未登陆
* @param message
* @return
*/
public static String generateUnlogonResponse(String message)...{
return generateResponse(false, message, REASON_UNLOGON, null);
}
/** *//**
* 返回处理失败的报文, 原因为无权限
* @param message
* @return
*/
public static String generateAccessDenyResponse(String message)...{
return generateResponse(false, message, REASON_ACCESS_DENY, null);
}
/** *//**
* 返回处理失败的报文, 原因为业务限制
* @param message
* @return
*/
public static String generateBizConstraintResponse(String message)...{
return generateResponse(false, message, REASON_BIZ_CONSTRAINT, null);
}
/** *//**
* 返回处理失败的报文, 原因为系统异常
* @param message
* @return
*/
public static String generateExceptionResponse(String message)...{
return generateResponse(false, message, REASON_EXCEPTION, null);
}
private static String generateJsonObjectString(Map map)...{
JSONObject jObj=new JSONObject();
jObj.putAll(map);
return jObj.toString();
}
public static void main(String[] args) ...{
System.out.println(generateSuccessResponse());
System.out.println(generateSuccessResponse("保存成功"));
Map customerizedContent=new HashMap();
customerizedContent.put("newid", "101");
System.out.println(generateSuccessResponse("保存成功", customerizedContent));
System.out.println(generateUnlogonResponse("您尚未登陆"));
System.out.println(generateAccessDenyResponse("您没有权限访问"));
System.out.println(generateBizConstraintResponse("用户名不可重复"));
System.out.println(generateExceptionResponse("数据库异常,请稍后再试"));
}
}
运行main后,将打印如下几种相同格式的报文,代表响应的不同种情况:
{"result":true}
{"message":"保存成功","result":true}
{"extraContent":{"newid":"101"},"message":"保存成功","result":true}
{"reason":"unlogon","message":"您尚未登陆","result":false}
{"reason":"accessDeny","message":"您没有权限访问","result":false}
{"reason":"bizConstraint","message":"用户名不可重复","result":false}
{"reason":"error","message":"数据库异常,请稍后再试","result":false}
Result属性代表这次处理的结果,只有true代表成功,false代表失败。
Message属性代表输出到客户端的打印信息。
Reason熟悉是对处理结果的解释,一般的业务系统有的情况基本上是unlogon未登陆,accessDeny访问拒绝,bizConstraint业务限制, error系统错误或异常。
extraContent用于输出额外的信息,比如在处理成功后客户端需要新生成纪录的主键,这时可以通过这个节点输出。
至于客户端的解析,由于是json格式的数据,非常简单地就能获取到报文中的数据。客户端代码如下:
var elemStr=Form.serialize(form);
alert(elemStr);
var url = "<c:url value="/multiActController.spr?action=jsonResponseSuccess"/>";
var mailAjax = new Ajax.Request(url,...{method: 'post',parameters:elemStr,
onComplete: function(response)...{
var responseText=response.responseText;
alert(responseText);
var jobj=responseText.parseJSON();
alert(jobj.result+","+jobj.message+","+jobj.extraContent.name+","+jobj.extraContent.address+","+jobj.extraContent.sex);
}
}
);
return false;
}
附:服务端需要引入jsonlib;客户端需引入json.js和prototype1.5。
苏屿峰于厦门