struts2之json请求的异常处理方案

大家都知道,使用struts2的异常处理机制,只要几行配置,就可以在发生异常时,跳转到我们指定的页面,并显示出相应的异常信息,具体的使用操作过程如下:

1)struts.xml

<struts>
<include file="struts-default.xml"></include>
    <constant name="struts.devMode" value="true" /><!-- 实现国际化资源文件和struts配置文件自动重新加载,不需要重启服务器 -->
    <constant name="struts.ui.theme" value="simple" />
    <constant name="struts.action.extension" value="," /><!-- 不要后缀 -->
    <constant name="struts.enable.SlashesInActionNames" value="true" /><!-- 使用"/"的方式 -->
    <package name="hatch" namespace="" extends="json-default"><!-- json支持  -->
...

<!-- 定义全局视图 -->
    <global-results>
	<result name="login" type="redirectAction">login</result>
	<result name="404">/WEB-INF/view/404.jsp</result>
	<result name="500">/WEB-INF/view/500.jsp</result>
    </global-results>
    <!-- 定义全局异常-->
    <global-exception-mappings>
	<exception-mapping result="500" exception="java.lang.Exception"/><!-- 出现异常时,将页面转到500这个错误页面 -->
    </global-exception-mappings>
...

</struts>




2)500.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    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>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>程序异常</title>
<%@include file="/WEB-INF/view/common/common.jspf" %>
<style type="text/css">
*{margin:0;padding:0;}
.errTitle{min-width:300px;width:90%;height:50px;line-height:50px;border:1px solid #999999;border-radius:5px;background:#0169B2;text-align:center;word-spacing:10px;font-family:'黑体';font-size:24px;margin:10px 70px;padding: 4px 4px 4px 6px;position:relative;}
.showErrWrap{font-size:10px;position:absolute;right:10px;bottom:-5px;}
.showErrWrap a:link,.showErrWrap a:visited{text-decoration:none;color:black;}
.showErrWrap a:hover{text-decoration:underline;color:yellow;}
.showErrWrap span{margin:0 4px;color:black;}
.errStack{min-width:300px;width:90%;font-family:"Courier New", Courier, monospace;border:0 none;margin:10px 70px;overflow:auto;padding:4px;}
</style>
<script type="text/javascript">
$(function(){
	$('#showErrBtn').toggle(function(){
		$('#showErrBtn').text('关闭详情');
		$('.errStack').slideDown('normal');
	},function(){
		$('.errStack').slideUp('normal');
		$('#showErrBtn').text('查看详情');
	});
});
if (window.parent != window) {
	window.parent.location.href = window.location.href;
}
</script>

</head>
<body>
<div class="errTitle">程 序 出 现 异 常!<span class="showErrWrap"><a href="javascript:void(0);" id="showErrBtn">查看详情</a><span>|</span><a href="javascript:void(0);" onclick="javascript:history.go(-1);">返回上一页</a></span></div>
<div class="errStack" style="display:none;"><pre>
<s:property value="exceptionStack"/><!-- 异常信息 -->
</pre>
</div>
</body>
</html>

假设在UserAction.java中有一个跳转到列表页面的方法,如下:

/* 用户列表视图 */
public String list() {
	int i = 10/0;
	return "list";
}

这样,在出现异常时,就能到这个页面了,效果大约是下面的样子:


但现在我很多请求处理,使用的都是ajax提交请求并获取数据的方式,这种情况下,默认的异常处理就力不从心了。跑个题儿先,稍微介绍下struts2中ajax请求的配置方式,以下以用户列表为例:

1)添加jar包支持:struts2-json-plugin-2.3.4.jar

2)在自己的struts.xml中,package继承json-default

3)返回类型为json

4)Action中添加设置返回的数据的值

如下是配置:

<struts>
	<constant name="struts.devMode" value="true" /><!-- 实现国际化资源文件和struts配置文件自动重新加载,不需要重启服务器 -->
	<constant name="struts.ui.theme" value="simple" />
	<constant name="struts.action.extension" value="," /><!-- 不要后缀 -->
	<constant name="struts.enable.SlashesInActionNames" value="true" /><!-- 使用"/"的方式 -->
	<package name="hatch" namespace="" extends="json-default"> <!-- json支持 -->
...
<action name="user/*" class="userAction" method="{1}">
            <result name="list">/WEB-INF/view/sys/user/list.jsp</result>
            <result name="saveOrUpdate">/WEB-INF/view/sys/user/saveOrUpdate.jsp</result>
            <result name="assginRole">/WEB-INF/view/sys/user/assginRole.jsp</result>
            <result type="json">
                <param name="root">dataMap</param>
                <param name="excludeProperties">rows\[\d+\]\.department.parent,rows\[\d+\]\.department.users,rows\[\d+\]\.department.children,rows\[\d+\]\.roles</param>
            </result><!-- ajax请求的返回视图 -->
        </action>

如下是页面:

<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<%@include file="/WEB-INF/view/common/common.jspf"%>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
		<title>用户列表</title>
		<script type="text/javascript">
		$(function(){
			//用户列表初始化
			$('#dg').datagrid({
				striped : true,
				rownumbers : true,
				singleSelect : false,
				pagination : true,
				pageSize : 10,
				fitColumn : true,
				dataType : 'json',
				animate : true,
				loadMsg : '请稍候...',
		        url:'${ctx }/sys/user/doList', 
		        columns:[[
						  {field:'ck',checkbox:true,align:'center'},
		                  {title:'序号',field:'id',align:'center'},
		                  {title:'登录名',field:'username',align:'center'},
		                  {title:'名称',field:'name',align:'center'},
		                  {title:'性别',field:'gender',align:'center',formatter:function(v){ return v==1?'男':'女' }},
		                  {title:'手机',field:'phoneNumber',align:'center'},
		                  {title:'email',field:'email',align:'center'},
		                  {title:'所属部门',field:'department',align:'center',formatter:function(v){if(v)return v['name'];}},
		                  {title:'操作',field:'Operation',align:'center',formatter: operationFormate}
		                 ]], 
		    });//easyui的列表请求,返回数据为json
			//添加操作列
			function operationFormate(value,node){
				  var str='<a style="color:green;text-decoration:none;margin-right:4px;" href="javascript:void(0);" onclick="update('+node.id+')">修改</a>'
				  	+'<a style="color:red;text-decoration:none;margin-right:4px;" href="javascript:void(0);" onclick="del('+node.id+')">删除</a>'
				  	+'<a style="color:green;text-decoration:none;margin-right:4px;" href="javascript:void(0);" onclick="assignrole('+node.id+')">角色分配</a>'
				  	+'<a style="color:red;text-decoration:none;" href="javascript:void(0);" onclick="resetPwd('+node.id+')">密码重置</a>';
		          return str;  
		    }
			//查询用户
			$('#queryBtn').click(function(){
				var queryParams = $('#dg').datagrid('options').queryParams;
				var name = $.trim($('#queryForm').find('input[name=name]').val());
				var gender = $.trim($('#queryForm').find('select[name=gender]').val());
		        queryParams.name = name;  
		        queryParams.gender = gender;  
		        $('#dg').datagrid('options').queryParams=queryParams;    
				$('#dg').datagrid('reload');
			});
			
			//添加用户
			$('#addBtn').click(function(){
				window.location.href="${ctx}/sys/user/save";
			});
			
			//删除用户
			$('#delBtn').click(function(){
				var ss = new Array();
				var rows = $('#dg').datagrid('getSelections');
				for(var i=0; i<rows.length; i++){
					var row = rows[i];
					ss.push(row.id);
				}
				del(ss.join(','));
			});
			
		});
		//修改用户
		function update(id){
			window.location.href="${ctx}/sys/user/update?id="+id;
		}
		//删除用户
		function del(ids){
			$.messager.confirm('确认框', '确认要删除吗?此操作是不可恢复的', function(r){
				if (r){ 
					var url = '${ctx }/sys/user/del';
					$.post(url, {
						ids:ids
					}, function(data){
						if(data.result==0){
							$('#dg').datagrid('reload'); 	
						}else{
							$.messager.alert('error','失败');
						}
						alert(data.result);
					},'json');
				}
			});
		}
		 //分配角色
		function assignrole(id){
			window.location.href="${ctx}/sys/user/assginRole?id="+id;	
		}
		 //密码重置
		function resetPwd(id){
			var url = '${ctx }/sys/user/doResetPwd1';
			$.post(url, {
				id:id
			}, function(data){
				if(data.result==0){
					$.messager.alert('信息提示','修改成功,密码重置为111111!','info');
				}else{
					var msg = data.msg; 
					$.messager.alert('错误提示','操作失败!','error');
					window.location = "${ctx}/jsonHandlerAction";
				}
			},'json');
		}
	</script>
	</head>
	<body>
		<div class="ptitle">系统管理>>用户列表</div>
		<div class="content-wrap">
		<div class="pcontent">
		<div class="easyui-panel toolbar">
			<a href="javascript:void(0);" class="easyui-linkbutton" iconCls="icon-add" plain="true" id="addBtn">新建</a>
			<a href="javascript:void(0);" class="easyui-linkbutton" iconCls="icon-remove" plain="true" id="delBtn">删除</a>
			<s:form action="/user/doList" namespace="/"  method="post" id="queryForm">
			名称:<s:textfield name="name"/>
			性别:<s:select list="#{null:'全部',1:'男',2:'女'}" name="gender"/>
			<a href="javascript:void(0);" class="easyui-linkbutton" iconCls="icon-search" id="queryBtn">Search</a>
			</s:form>
		</div>
		<!-- 列表 -->
		<table id="dg" title=""></table>
		</div>
		</div>
	</body>
如下是Action

/* 用户 列表数据 */
	public String doList(){
		HttpServletRequest re = ServletActionContext.getRequest();
		dataMap = new HashMap<String, Object>();
		Map<String, Object> cond = MapBeanUtil.transBean2Map(user);
		Page<User> p = new Page<User>();
		cond.put("page", Integer.parseInt(re.getParameter("page")));
		cond.put("rows", Integer.parseInt(re.getParameter("rows")));
		p = userService.findUserForPage(cond);
		dataMap.put("total", p.getTotal());
		dataMap.put("rows", p.getRows());
		return SUCCESS;
	}
效果如下:

struts2之json请求的异常处理方案_第1张图片



以上是json开发的简单介绍,更详细的可以下载相关文档。

返回正题,假如正在我在Action中出现异常,可怕的事情:框架将错误页面的html代码以html的形式返回来了,以下是用firefox看到的结果:


分析思考:

struts2的异常处理,是基于拦截器的,出现目前的错误,就是因为拦截器的异常处理,没有考虑到ajax异步请求,那么是哪个拦截器处理异常呢?打开struts核心包struts2-core-2.3.4.jar中的struts-default.xml中,查看下面的信息:

<interceptors>
            <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
            <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
            <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
            <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
            <interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/>
            <interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" />
            <interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" />
            <interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" />
            <interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
            <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
。。。
发现异常是这个类com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor来处理的,因此,一个最简单的办法,就是将该类覆写,加上json请求的支持。

/*
 * Copyright 2002-2006,2009 The Apache Software Foundation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.opensymphony.xwork2.interceptor;

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

import javax.servlet.http.HttpServletRequest;

import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsStatics;

import com.hatch.common.JsonHandlerException;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;

/**
 * <!-- START SNIPPET: description -->
 * 
 * This interceptor forms the core functionality of the exception handling
 * feature. Exception handling allows you to map an exception to a result code,
 * just as if the action returned a result code instead of throwing an
 * unexpected exception. When an exception is encountered, it is wrapped with an
 * {@link ExceptionHolder} and pushed on the stack, providing easy access to the
 * exception from within your result.
 * 
 * <b>Note:</b> While you can configure exception mapping in your configuration
 * file at any point, the configuration will not have any effect if this
 * interceptor is not in the interceptor stack for your actions. It is
 * recommended that you make this interceptor the first interceptor on the
 * stack, ensuring that it has full access to catch any exception, even those
 * caused by other interceptors.
 * 
 * <!-- END SNIPPET: description -->
 * 
 * <p/>
 * <u>Interceptor parameters:</u>
 * 
 * <!-- START SNIPPET: parameters -->
 * 
 * <ul>
 * 
 * <li>logEnabled (optional) - Should exceptions also be logged? (boolean
 * true|false)</li>
 * 
 * <li>logLevel (optional) - what log level should we use (
 * <code>trace, debug, info, warn, error, fatal</code>)? - defaut is
 * <code>debug</code></li>
 * 
 * <li>logCategory (optional) - If provided we would use this category (eg.
 * <code>com.mycompany.app</code>). Default is to use
 * <code>com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor</code>.
 * </li>
 * 
 * </ul>
 * 
 * The parameters above enables us to log all thrown exceptions with stacktace
 * in our own logfile, and present a friendly webpage (with no stacktrace) to
 * the end user.
 * 
 * <!-- END SNIPPET: parameters -->
 * 
 * <p/>
 * <u>Extending the interceptor:</u>
 * 
 * <p/>
 * 
 * <!-- START SNIPPET: extending -->
 * 
 * If you want to add custom handling for publishing the Exception, you may
 * override
 * {@link #publishException(com.opensymphony.xwork2.ActionInvocation, ExceptionHolder)}
 * . The default implementation pushes the given ExceptionHolder on value stack.
 * A custom implementation could add additional logging etc.
 * 
 * <!-- END SNIPPET: extending -->
 * 
 * <p/>
 * <u>Example code:</u>
 * 
 * <pre>
 * <!-- START SNIPPET: example -->
 * <xwork>
 *     <package name="default" extends="xwork-default">
 *         <global-results>
 *             <result name="error" type="freemarker">error.ftl</result>
 *         </global-results>
 * 
 *         <global-exception-mappings>
 *             <exception-mapping exception="java.lang.Exception" result="error"/>
 *         </global-exception-mappings>
 * 
 *         <action name="test">
 *             <interceptor-ref name="exception"/>
 *             <interceptor-ref name="basicStack"/>
 *             <exception-mapping exception="com.acme.CustomException" result="custom_error"/>
 *             <result name="custom_error">custom_error.ftl</result>
 *             <result name="success" type="freemarker">test.ftl</result>
 *         </action>
 *     </package>
 * </xwork>
 * <!-- END SNIPPET: example -->
 * </pre>
 * 
 * <p/>
 * This second example will also log the exceptions using our own category
 * <code>com.mycompany.app.unhandled<code> at WARN level. 
 * 
 * <pre>
 * <!-- START SNIPPET: example2 -->
 * <xwork>
 *   <package name="something" extends="xwork-default">
 *      <interceptors>
 *          <interceptor-stack name="exceptionmappingStack">
 *              <interceptor-ref name="exception">
 *                  <param name="logEnabled">true</param>
 *                  <param name="logCategory">com.mycompany.app.unhandled</param>
 *                  <param name="logLevel">WARN</param>	        		
 *              </interceptor-ref>	
 *              <interceptor-ref name="i18n"/>
 *              <interceptor-ref name="staticParams"/>
 *              <interceptor-ref name="params"/>
 *              <interceptor-ref name="validation">
 *                  <param name="excludeMethods">input,back,cancel,browse</param>
 *              </interceptor-ref>
 *          </interceptor-stack>
 *      </interceptors>
 * 
 *      <default-interceptor-ref name="exceptionmappingStack"/>
 *    
 *      <global-results>
 *           <result name="unhandledException">/unhandled-exception.jsp</result>
 *      </global-results>
 * 
 *      <global-exception-mappings>
 *           <exception-mapping exception="java.lang.Exception" result="unhandledException"/>
 *      </global-exception-mappings>
 *        
 *      <action name="exceptionDemo" class="org.apache.struts2.showcase.exceptionmapping.ExceptionMappingAction">
 *          <exception-mapping exception="org.apache.struts2.showcase.exceptionmapping.ExceptionMappingException"
 *                             result="damm"/>
 *          <result name="input">index.jsp</result>
 *          <result name="success">success.jsp</result>            
 *          <result name="damm">damm.jsp</result>
 *      </action>
 * 
 *   </package>
 * </xwork>
 * <!-- END SNIPPET: example2 -->
 * </pre>
 * 
 * @author Matthew E. Porter (matthew dot porter at metissian dot com)
 * @author Claus Ibsen
 */
public class ExceptionMappingInterceptor1 extends AbstractInterceptor {

	protected static final Logger LOG = LoggerFactory
			.getLogger(ExceptionMappingInterceptor.class);

	protected Logger categoryLogger;
	protected boolean logEnabled = false;
	protected String logCategory;
	protected String logLevel;

	public boolean isLogEnabled() {
		return logEnabled;
	}

	public void setLogEnabled(boolean logEnabled) {
		this.logEnabled = logEnabled;
	}

	public String getLogCategory() {
		return logCategory;
	}

	public void setLogCategory(String logCatgory) {
		this.logCategory = logCatgory;
	}

	public String getLogLevel() {
		return logLevel;
	}

	public void setLogLevel(String logLevel) {
		this.logLevel = logLevel;
	}

	@Override
	public String intercept(ActionInvocation invocation) throws Exception {
		String result;

		try {
			result = invocation.invoke();
		} catch (Exception e) {
ActionContext actionContext = invocation.getInvocationContext();
			HttpServletRequest request = (HttpServletRequest) actionContext
					.get(StrutsStatics.HTTP_REQUEST);
			if (isAjaxRequest(request)) {//如果是ajax请求方式
				ValueStack stack = invocation.getStack();
				List<ExceptionMappingConfig> exceptionMappings = invocation
						.getProxy().getConfig().getExceptionMappings();
				JsonHandlerException je = new JsonHandlerException(e);
				String mappedResult = this.findResultFromExceptions(
						exceptionMappings, je);
				result = mappedResult;
				Map<String, Object> dataMap = new HashMap<String, Object>();
				stack.set("dataMap", dataMap);
				dataMap.put("result", "500");
				StringBuffer msg = new StringBuffer(e.toString()+"\n");
	            StackTraceElement[] trace = e.getStackTrace();
	            for (int i=0; i < trace.length; i++)
	            	msg.append("\tat " + trace[i]+"\n");
				ServletActionContext.getRequest().getSession().setAttribute("errMsg", msg);
			}else{// 默认处理方式
				if (isLogEnabled()) {
					handleLogging(e);
				}
				List<ExceptionMappingConfig> exceptionMappings = invocation.getProxy().getConfig().getExceptionMappings();
				String mappedResult = this.findResultFromExceptions(exceptionMappings, e);
				if (mappedResult != null) {
					result = mappedResult;
					publishException(invocation, new ExceptionHolder(e));
				} else {
					throw e;
				}
				invocation.getStack();
				invocation.getInvocationContext().get(Action.ERROR);
//				invocation.getStack().findString("exceptionStack");
				invocation.getInvocationContext().get(Action.ERROR);
			}
		}

		return result;
	}

	private boolean isAjaxRequest(HttpServletRequest request) {
		String header = request.getHeader("X-Requested-With");
		if (header != null && "XMLHttpRequest".equals(header))
			return true;
		else
			return false;
	}

	/**
	 * Handles the logging of the exception.
	 * 
	 * @param e
	 *            the exception to log.
	 */
	protected void handleLogging(Exception e) {
		if (logCategory != null) {
			if (categoryLogger == null) {
				// init category logger
				categoryLogger = LoggerFactory.getLogger(logCategory);
			}
			doLog(categoryLogger, e);
		} else {
			doLog(LOG, e);
		}
	}

	/**
	 * Performs the actual logging.
	 * 
	 * @param logger
	 *            the provided logger to use.
	 * @param e
	 *            the exception to log.
	 */
	protected void doLog(Logger logger, Exception e) {
		if (logLevel == null) {
			logger.debug(e.getMessage(), e);
			return;
		}

		if ("trace".equalsIgnoreCase(logLevel)) {
			logger.trace(e.getMessage(), e);
		} else if ("debug".equalsIgnoreCase(logLevel)) {
			logger.debug(e.getMessage(), e);
		} else if ("info".equalsIgnoreCase(logLevel)) {
			logger.info(e.getMessage(), e);
		} else if ("warn".equalsIgnoreCase(logLevel)) {
			logger.warn(e.getMessage(), e);
		} else if ("error".equalsIgnoreCase(logLevel)) {
			logger.error(e.getMessage(), e);
		} else if ("fatal".equalsIgnoreCase(logLevel)) {
			logger.fatal(e.getMessage(), e);
		} else {
			throw new IllegalArgumentException("LogLevel [" + logLevel
					+ "] is not supported");
		}
	}

	protected String findResultFromExceptions(
			List<ExceptionMappingConfig> exceptionMappings, Throwable t) {
		String result = null;

		// Check for specific exception mappings.
		if (exceptionMappings != null) {
			int deepest = Integer.MAX_VALUE;
			for (Object exceptionMapping : exceptionMappings) {
				ExceptionMappingConfig exceptionMappingConfig = (ExceptionMappingConfig) exceptionMapping;
				int depth = getDepth(exceptionMappingConfig
						.getExceptionClassName(), t);
				if (depth >= 0 && depth < deepest) {
					deepest = depth;
					result = exceptionMappingConfig.getResult();
				}
			}
		}

		return result;
	}

	/**
	 * Return the depth to the superclass matching. 0 means ex matches exactly.
	 * Returns -1 if there's no match. Otherwise, returns depth. Lowest depth
	 * wins.
	 * 
	 * @param exceptionMapping
	 *            the mapping classname
	 * @param t
	 *            the cause
	 * @return the depth, if not found -1 is returned.
	 */
	public int getDepth(String exceptionMapping, Throwable t) {
		return getDepth(exceptionMapping, t.getClass(), 0);
	}

	private int getDepth(String exceptionMapping, Class exceptionClass,
			int depth) {
		if (exceptionClass.getName().contains(exceptionMapping)) {
			// Found it!
			return depth;
		}
		// If we've gone as far as we can go and haven't found it...
		if (exceptionClass.equals(Throwable.class)) {
			return -1;
		}
		return getDepth(exceptionMapping, exceptionClass.getSuperclass(),
				depth + 1);
	}

	/**
	 * Default implementation to handle ExceptionHolder publishing. Pushes given
	 * ExceptionHolder on the stack. Subclasses may override this to customize
	 * publishing.
	 * 
	 * @param invocation
	 *            The invocation to publish Exception for.
	 * @param exceptionHolder
	 *            The exceptionHolder wrapping the Exception to publish.
	 */
	protected void publishException(ActionInvocation invocation,
			ExceptionHolder exceptionHolder) {
		invocation.getStack().push(exceptionHolder);
	}
}
这里说明下,如果是出现异常,而且是ajax请求的话,就找JsonHandlerException这个异常所对应的视图:

<!-- 定义全局视图 -->
		<global-results>
			<result name="login" type="redirectAction">login</result>
			<result name="404">/WEB-INF/view/404.jsp</result>
			<result name="500">/WEB-INF/view/500.jsp</result>
			<result name="json_500" type="json"><param name="root">dataMap</param></result>
			<result type="json"><param name="root">dataMap</param></result><!-- ajax请求的返回视图 -->
		</global-results>
		<!-- 定义全局异常-->
		<global-exception-mappings>
			<exception-mapping result="500" exception="java.lang.Exception"/>
			<exception-mapping result="json_500" exception="com.hatch.common.JsonHandlerException"/>
		</global-exception-mappings>
...

下面是自定义Json异常类

package com.hatch.common;

public class JsonHandlerException extends Exception {

	/**
	 * 
	 */
	private static final long serialVersionUID = -4788951533205831941L;

	public JsonHandlerException() {
		super();
	}

	public JsonHandlerException(String message) {
		super(message);
	}

	public JsonHandlerException(String message, Throwable cause) {
		super(message, cause);
	}

	public JsonHandlerException(Throwable cause) {
		super(cause);
	}
}
这样在页面一端,就可以正确的获取异常数据了。

struts2之json请求的异常处理方案_第2张图片

对于普通的$.post请求,这个异常数据很容易可以人性化的提示给用户,这里用的easyui,可以在数据表格加载时,验证下有没有返回数据:

$('#dg').datagrid({
				striped : true,
				rownumbers : true,
				singleSelect : false,
				pagination : true,
				pageSize : 10,
				fitColumn : true,
				dataType : 'json',
				animate : true,
				loadMsg : '请稍候...',
		        url:'${ctx }/sys/user/doList', 
				loadFilter:function(data){
					if(!data.rows||!data.total){
						if(data.msg){
							$.messager.alert('错误提示','操作失败!错误原因:<hr/>'+data.msg,'error');
						}else{
							$.messager.alert('错误提示','操作失败!','error');
						}
						return {total:0,rows:[]};
					}
				},
		        columns:[[
						  {field:'ck',checkbox:true,align:'center'},
		                  {title:'序号',field:'id',align:'center'},
		                  {title:'登录名',field:'username',align:'center'},
		                  {title:'名称',field:'name',align:'center'},
		                  {title:'性别',field:'gender',align:'center',formatter:function(v){ return v==1?'男':'女' }},
		                  {title:'手机',field:'phoneNumber',align:'center'},
		                  {title:'email',field:'email',align:'center'},
		                  {title:'所属部门',field:'department',align:'center',formatter:function(v){if(v)return v['name'];}},
		                  {title:'操作',field:'Operation',align:'center',formatter: operationFormate}
		                 ]], 
		    });

以上红色字体表示数据加载异常时,提示给用户错误信息,效果如下:

当然,如果想做一致的处理,像默认的拦截器那样定位到一个错误页面,可以在这里加跳转信息:

loadFilter:function(data){
					if(!data.rows||!data.total){
						if(data.msg){
							$.messager.alert('错误提示','操作失败!错误原因:<hr/>'+data.msg,'error');
						}else{
							$.messager.alert('错误提示','操作失败!','error');
						}
						window.location = '${ctx}/jsonHandlerAction'
						return {total:0,rows:[]};
					}
				},
这样,又能跳转到我们默认的错误页面了:

struts2之json请求的异常处理方案_第3张图片

这个错误信息,用了一个很笨的方法传递到错误页面,即在拦截器捕获到这个ajax操作的异常时 ,将这个异常信息加入到会话中,然后在js跳转后,在错误页面中取出错误信息。

至此,该方案介绍完毕,有更好方法的童鞋可以赐教!


你可能感兴趣的:(exception,json,异常处理,struts2,全局异常)