`
Rainbow702
  • 浏览: 1065258 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类

struts2 跳转至404 页面的解决方案

阅读更多

对于使用了struts2的工程,以下几种情况,我觉得需要跳转至 404 页面告诉用户:

① 在地址栏里,直接输入一个不存在的jsp页面

     比如, http://xxx:port/webapp/test.jsp, 其中test.jsp根本就不存在

PS:下面两种情况是以使用“convention plugin”为前提的

② 在地址栏里,直接输入一个不存在的action

     比如, http://xxx:port/webapp/test!method1,其中,TestAction根本就不存在

③ 在地址栏里,输入了的action虽然存在,但是指定的方法,在action中却不存在

     比如,http://xxx:port/webapp/test!method2,其中TestAction存在,但是method2却不存在于TestAction中

 

下面对以上各种情况进行说明解决方案。

对于第①种,可以直接在web.xml中进行如下配置: 

<error-page>
    <error-code>404</error-code>
    <location>/jsp/error/error_forward.jsp?code=400</location>
</error-page>

上面的location对应的页面有如下限制: 

(1) 必须以“/”开头,这个是API中明确说了的 

The location element contains the location of the resource in the web application relative to the root of the web application. The value of the location must have a leading `/'.

 (2) jsp页面中不能使用struts2的tag,否则会报错: 

The Struts dispatcher cannot be found.  This is usually caused by using Struts tags without the associated filter. Struts tags are only usable when the request has passed through its servlet filter, which initializes the Struts dispatcher needed for this tag.

(3)无法指定一个action作为location的值。如果你指定了一个action,那么当出现404出错时,画面将会是一片空白。

 

有许有人会问,我上面那个地址为什么后面要跟 “?code=400”。

那么先来看一下“error_forward.jsp”的实现吧:

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript">
    window.onload = function() {
        var code = '<%=request.getParameter("code")%>';
        switch(code) {
            case '404' : 
                window.top.location.replace("<%=request.getContextPath()%>/forward!notFound");
                break;
            // other case can be listed here
            default:
                window.top.location.replace("<%=request.getContextPath()%>/forward!notFound");
                break;
        }
    };
</script>
</head>

<body>
</body>
</html>

从上面的代码来看,我们可以通过code来决定跳转至哪种错误页面,404就跳转到404的错误页面,500就跳转至500的错误页面,等等等等。

 

可能还会有人会说,那为什么不直接在 web.xml 中直接配置要跳转的页面,而是还要再中间这么一个跳转过程呢。

哎,说来苦逼啊。因为错误页面要支持国际化,即英文和中文情况下,错误页面里的内容是不一样的。如果直接在 web.xml 中配置,就无法使用struts2的tag来实现国际化(原因上面说了,请参照第(2)点)。

好了,到此,第①种情况解决完了了。

 

下面说第②种

其实,使用第①种的解决方案就可以解决这第②种的问题的,但是控制台会打出异常信息: 

http-bio-7070-exec-9 2015-07-16 10:46:12,212 WARN [org.apache.struts2.dispatcher.Dispatcher : 68] - <Could not find action or result: /test-fs/forward11!notFound>
There is no Action mapped for namespace / and action name forward11. - [unknown location]
	at com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:185)
	at com.opensymphony.xwork2.DefaultActionProxyFactory.createActionProxy(DefaultActionProxyFactory.java:70)
	at org.apache.struts2.rest.RestActionProxyFactory.createActionProxy(RestActionProxyFactory.java:53)
	at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:554)
	at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81)
	at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.jasig.cas.client.util.AssertionThreadLocalFilter.doFilter(AssertionThreadLocalFilter.java:50)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.jasig.cas.client.util.HttpServletRequestWrapperFilter.doFilter(HttpServletRequestWrapperFilter.java:70)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
。。。。。。

所以,如果只想让页面跳转至404页面,而控制台不要打印上面这种异常信息的话,可以在 struts.xml 中进行如下配置: 

<package name="your-package" extends="struts-default">
    <default-action-ref name="notFound" />
    <action name="notFound" class="com.test.test.action.ForwardAction" method="notFound">
        <result name="notFound">/jsp/error/404.jsp</result>
    </action>
</package>

需要提醒的是,不要给上面的package指定namespace。另外,action的 result 一定要声明。除非在global-results中刚好有一个result 与你的“notFound()”这个方法所要跳转的result的名字 是一样的。 

上面的配置的意思就是,在package下配置一个 default action,这个当输入的action找不到的时候,就会使用此action来进行处理。

以我上面的配置来说,当一个action找不到时候,就会调用 com.test.test.action.ForwardAction 中的 notFound() 方法来对应。这个方法的实现如下: 

package com.test.test.action;

import org.apache.log4j.Logger;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;

import com.test.test.base.BaseAction;

@Results({ @Result(name = "notFound", location = "/jsp/error/404.jsp"))
public class ForwardAction extends BaseAction {
	/**
	 * forward to the 404 page
	 * 
	 * @return the logic view
	 */
	public String notFound() {
		addActionMessage(getText("page.404.tip"));
		return "notFound";
	}
}

 需要注意的是,上面的Action中,虽然声明了一个  @Result(name = "notFound", location = "/jsp/error/404.jsp")。但是这个Result仅仅服务于直接调用 ForwardAction中的 notFound()方法的情况。如果你是因为输入了一个不存在的action,通过default action来调用了ForwarAction中的这noFound()方法时,这个result的配置是不会起作用的。这也是我上面说的“另外,action的 result 一定要声明”的原因。

好了,到此,第②种情况解决完了了。

 

下面说第③种

这种情况是调查时间最长的。

首先,struts2中,执行action的类是: com.opensymphony.xwork2.DefaultActionInvocation中的invokeAction()方法。我们来看下: 

protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
    String methodName = proxy.getMethod();

    if (LOG.isDebugEnabled()) {
        LOG.debug("Executing action method = #0", methodName);
    }

    String timerKey = "invokeAction: " + proxy.getActionName();
    try {
        UtilTimerStack.push(timerKey);

        Object methodResult;
        try {
            methodResult = ognlUtil.getValue(methodName + "()", getStack().getContext(), action);
        } catch (OgnlException e) {
            // hmm -- OK, try doXxx instead
            try {
                String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1)  + "()";
                methodResult = ognlUtil.getValue(altMethodName, ActionContext.getContext().getContextMap(), action);
            } catch (OgnlException e1) {
                // well, give the unknown handler a shot
                if (unknownHandlerManager.hasUnknownHandlers()) {
                    try {
                        methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
                    } catch (NoSuchMethodException e2) {
                        // throw the original one
                        throw e;
                    }
                } else {
                    throw e;
                }
            }
        }
        return saveResult(actionConfig, methodResult);
    } catch (NoSuchPropertyException e) {
        throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
    } catch (MethodFailedException e) {
        // We try to return the source exception.
        Throwable t = e.getCause();

        if (actionEventListener != null) {
            String result = actionEventListener.handleException(t, getStack());
            if (result != null) {
                return result;
            }
        }
        if (t instanceof Exception) {
            throw (Exception) t;
        } else {
            throw e;
        }
    } finally {
        UtilTimerStack.pop(timerKey);
    }
}

从代码来看,它是通过“ognlUtil.getValue()”去执行指定action中的指定方法的,若指定的方法不存在时,它会尝试着在在方法名前加上  “do” 构成一个新的方法名,然后再去执行这个新的方法,如果这个新的方法名还是不存在,那么,它就会调用所有配置好的 UnknownHandler 去处理这种情况。 

关键就是这一点。 UnknownHandler 怎么配置的?

在struts2-convention-plugin.jar中有一份struts-plugin.xml文件,里面有一个配置: 

<bean type="com.opensymphony.xwork2.UnknownHandler" name="convention" class="org.apache.struts2.convention.ConventionUnknownHandler"/>

 这个就是 struts2 convention plugin 默认使用的 UnknownHandler,它共实现了两个方法:handleUnknownAction、handleUnknownResult 

但是却没有实现  handleUnknownActionMethod, 而这个方法却是我们第②情况所需要的。

哎,即然convention plugin没实现它,那我们就自己来实现呗。

想法很简单,但是我花了2个多小时在网上查来查去,就是没查到个sample出来。哎,没办法,我就只能简单的让这个方法抛出一个 NoSuchMethodException 异常了。(如果有哪位兄弟知道如何实装这个方法的,请指导一下)。具体实现如下: 

public class CustomUnknownHandler implements UnknownHandler {

	/**
	 * Not used
	 * 
	 * @see org.apache.struts2.convention.ConventionUnknownHandler#handleUnknownAction
	 */
	@Override
	public ActionConfig handleUnknownAction(String namespace, String actionName) throws XWorkException {
		return null;
	}

	/**
	 * Not used
	 * 
	 * @see org.apache.struts2.convention.ConventionUnknownHandler#handleUnknownResult
	 */
	@Override
	public Result handleUnknownResult(ActionContext actionContext, String actionName, ActionConfig actionConfig,
			String resultCode) throws XWorkException {
		return null;
	}

	/**
	 * jump to the 404 page
	 */
	@Override
	public Object handleUnknownActionMethod(Object action, String methodName) throws NoSuchMethodException {
		throw new NoSuchMethodException(action.getClass().getName() + " does not have the mehod named '" + methodName
				+ "'.");
	}
}

实现了这个方法还只是第一步。接下来还要通过配置来使用它,打开你的 struts.xml,添加如下配置: 

    <package name="test-package" extends="rest-default">        
        <global-results>
            <result name="notFoundRes" type="chain">
                <param name="actionName">forward</param>
                <param name="method">notFound</param>
            </result>
        </global-results>
        
        <global-exception-mappings>
            <exception-mapping result="notFoundRes" exception="java.lang.NoSuchMethodException"></exception-mapping>
        </global-exception-mappings>

    </package>
    
    <!-- this is the handler of convention plugin. This is required by the convention plugin. -->
    <bean name="conventionUnknownHandler" class="org.apache.struts2.convention.ConventionUnknownHandler" type="com.opensymphony.xwork2.UnknownHandler" />
    <!-- this is the custom handler -->
    <bean name="myUnknownHandler" class="com.test.test.handler.CustomUnknownHandler" type="com.opensymphony.xwork2.UnknownHandler" />
    <unknown-handler-stack>
        <unknown-handler-ref name="conventionUnknownHandler"></unknown-handler-ref>
        <unknown-handler-ref name="myUnknownHandler"></unknown-handler-ref>
    </unknown-handler-stack>

首先,声明了两个bean,一个是convetion自己实现的handler,一个是我自己实现的handler,然后在,在unknown-handler-stack标签中引用它们,这样一来,在上面的“methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);”处,就会使用这里所引用的handler去处理未知情况。当然, handleUnknownAction、handleUnknownResult还是使用的是convention plugin中的实现,handleUnknownActionMethod使用的是我的实现。

 

最后,因为我的实现里,只是简单的返回 一个 NoSuchMethodException,所以,还需要配置一个global-exception-mappings 来捕获它。在上面的配置里也写了出来了。

另外,因为使用了 “unknown-handler-stack”,所以 struts.xml的必须使用 struts-2.1.dtd 或者 更高版本。

好了,至此,第③种情况解决完了。

 

最后,我给出我的struts.xml。见附件。

分享到:
评论

相关推荐

    pager-taglib 分页扩展实例

    pager-taglib 是个很好的jsp分页标签,使用它结合jstl可以实现灵活的分页导航功能...2.只在jboss5.1内测试通过,注意检查jdbc驱动包等相关依赖,tomcat可能出现缺包等问题,相信入门级别的朋友们应该可以找到解决方案的

    会员管理系统(struts+hibernate+spring)130226.rar

    "会员管理系统(struts+hibernate+spring)130226.rar" 是一个针对计算机专业的JSP源码资料包,它集成了Struts、Hibernate和Spring框架,旨在为用户提供一个高效、稳定且易于维护的会员管理解决方案。这个系统通过使用...

    源码基于JSP的车辆管理系统(struts+hibernate+spring+oracle).rar

    【源码】基于JSP的车辆管理系统(Struts+Hibernate+Spring+Oracle)是一个功能强大且技术先进的车辆管理解决方案源码包。该源码包融合了多种主流技术,包括JSP、Struts、Hibernate、Spring和Oracle数据库,以提供...

    Java知识拾遗:三大框架的技术起源

    Struts、Hibernate和Spring是我们Java开发中的常用关键,他们分别针对不同的应用场景给出最合适的解决方案。但你是否知道,这些知名框架最初是怎样产生的? 我们知道,传统的Java Web应用程序是采用JSP+Servlet+...

    DisplayTag1.2 扩展(自定义分页、排序、导出、页面导航)

    DisplayTag是个很好的jsp标签,目前最新版本为1.2,支持了自定义分页,解决了之前...3.只在jboss5.1内测试通过,注意检查jdbc驱动包等相关依赖,tomcat可能出现缺包等问题,相信入门级别的朋友们应该可以找到解决方案的

    网上体育商城的设计与实现毕业设计答辩PPT.pptx

    对于用户的请求是通过拦截器来处理的,当用户在客户端对浏览器发出请求,根据struts.xml中的配置找到对应的Action类和方法,返回结果result,并跳转到相应页面,返回HTTP响应到客户端浏览器,简单的说,就是用户在...

    ARCH4系统开发指南

    而不跳转页面 82 3 页面多行录入的处理方案 83 3.1 概述 83 3.2 设计思想 84 3.3 方法调用顺序 85 3.3.1 点击增加按钮时 85 3.3.2 点击删除按钮时 85 3.4 Javascript API 85 3.4.1 insertRow 85 3.4.2 deleteRow 85 ...

    工程硕士学位论文 基于Android+HTML5的移动Web项目高效开发探究

    其中使用Struts作为系统的整体基础架构,负责MVC的分离,在Struts框架的模型部分,控制业务跳转,利用Hibernate框架对持久层提供支持,Spring做管理,管理Struts和Hibernate。 WebStorage HTML新增的本地存储解决...

    JspRun!社区论坛系统 v6.0 bulid 090423 GBK 源码版.rar

    的基础架构采用世界上最先进流行的 web 编程组合 JAVA MySQL 实现,是一个经过完善设计,适用于各种服务器环境的高效论坛系统解决方案。系统采用struts、hibernate框架及中间件的结合既实现了业务逻辑与控制逻辑的...

    JspRun!社区论坛系统 v6.0 bulid 090424 GBK 安装版.rar

    的基础架构采用世界上最先进流行的 web 编程组合 JAVA MySQL 实现,是一个经过完善设计,适用于各种服务器环境的高效论坛系统解决方案。系统采用struts、hibernate框架及中间件的结合既实现了业务逻辑与控制逻辑的...

    java面试题

    并发问题解决方案 59 71.7. Hibernate是如何延迟加载? 60 71.8. Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系) 60 71.9. 说下Hibernate的缓存机制 60 71.10. Hibernate的查询方式 60 71.11. 如何...

    最新Java面试宝典pdf版

    8、说说struts1与struts2的区别。 121 9、hibernate中的update()和saveOrUpdate()的区别,session的load()和get()的区别。 122 10、简述 Hibernate 和 JDBC 的优缺点? 如何书写一个 one to many 配置文件. 122 11、...

    Java面试宝典2010版

    8、说说struts1与struts2的区别。 9、hibernate中的update()和saveOrUpdate()的区别,session的load()和get()的区别。 10、简述 Hibernate 和 JDBC 的优缺点? 如何书写一个 one to many 配置文件. 11、iBatis与...

    Java面试笔试资料大全

    8、说说struts1与struts2的区别。 121 9、hibernate中的update()和saveOrUpdate()的区别,session的load()和get()的区别。 122 10、简述 Hibernate 和 JDBC 的优缺点? 如何书写一个 one to many 配置文件. 122 11、...

    JAVA面试宝典2010

    8、说说struts1与struts2的区别。 121 9、hibernate中的update()和saveOrUpdate()的区别,session的load()和get()的区别。 122 10、简述 Hibernate 和 JDBC 的优缺点? 如何书写一个 one to many 配置文件. 122 11、...

    Java面试宝典-经典

    8、说说struts1与struts2的区别。 121 9、hibernate中的update()和saveOrUpdate()的区别,session的load()和get()的区别。 122 10、简述 Hibernate 和 JDBC 的优缺点? 如何书写一个 one to many 配置文件. 122 11、...

    java面试题大全(2012版)

    8、说说struts1与struts2的区别。 121 9、hibernate中的update()和saveOrUpdate()的区别,session的load()和get()的区别。 122 10、简述 Hibernate 和 JDBC 的优缺点? 如何书写一个 one to many 配置文件. 122 11、...

Global site tag (gtag.js) - Google Analytics