尽管 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 的服务端响应类,以满足上述的需求,服务端代码如下:
package
cn.ih.util.web;
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 格式的数据,非常简单地就能获取到报文中的数据。客户端代码如下:
function
submitForm(form)
...
{ 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。
苏屿峰于厦门