`
Rainbow702
  • 浏览: 1078194 次
  • 性别: 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。见附件。

分享到:
评论

相关推荐

    Struts2防止重复提交解决方案

    总的来说,Struts2的`token`拦截器是防止重复提交的有效解决方案,通过结合合理的拦截器配置和跳转策略,可以确保应用程序的稳定性和数据一致性。在实际开发中,还需要考虑其他因素,如异常处理、用户体验优化等,以...

    Struts2防止重复提交的解决方案

    ### Struts2防止重复提交的解决方案 #### 一、引言 在Web应用程序开发中,一个常见的问题是如何有效地防止表单的重复提交。这不仅能够提高用户体验,还能增强系统的安全性。Struts2作为一款广泛使用的Java Web应用...

    struts 1.2 跳转后css或js 无效解决方法

    ### Struts 1.2 跳转后 CSS 或 JS 无效解决方法 在使用Struts 1.2框架开发Web应用时,...通过上述解决方案和示例代码,可以有效地解决Struts 1.2应用中跳转后CSS或JS失效的问题,确保Action、CSS或JS能够正常工作。

    struts2列子源码

    Struts2是一个强大的Java web应用程序框架,它基于Model-View-Controller(MVC)设计模式,为开发者提供了构建可维护性高、结构清晰的Web应用的解决方案。在本压缩包中,包含的是张孝祥关于Struts2的视频教程源码,...

    struts2学习笔记

    此外,Struts2还支持动态方法调用、国际化、主题和模板、AJAX集成等特性,为开发者提供了全面的Web应用开发解决方案。在实际开发中,熟练掌握Struts2的各种机制和最佳实践,能提高开发效率和代码质量。

    Struts2项目实例

    Struts2是一个强大的Java web框架,它为开发MVC(模型-视图-控制器)架构的应用程序提供了全面的解决方案。这个"Struts2项目实例"很可能是为了帮助开发者理解和掌握Struts2框架的实际应用。 首先,让我们深入了解...

    struts2+cas单点登陆例子

    CAS是一个开放源码的SSO解决方案,由耶鲁大学开发,用于验证用户身份并提供单一登录服务。它的主要工作原理是:当用户尝试访问受保护的应用时,会被重定向到CAS服务器进行身份验证,一旦验证成功,CAS会返回一个票据...

    黑马Struts2视频(day2)

    - Struts2还可以与其他框架如Spring、Hibernate等无缝集成,提供一站式解决方案。 7. **异常处理**: - Struts2提供了一套完善的异常处理机制,包括全局和局部的异常配置,可以统一处理应用程序可能出现的错误。 ...

    struts2

    4. 集成其他框架:Struts2能很好地与Spring、Hibernate等其他流行框架集成,提供一站式的Java web开发解决方案。 四、Struts2的实战应用 在实际项目中,Struts2可以用来开发各种web应用程序,例如企业级的信息管理...

    struts2.jar

    在提供的链接中(http://blog.csdn.net/zs20082012/article/details/79217642),博主详细讨论了Struts2的一些常见问题和解决方案,包括类型转换异常、国际化处理、Action跳转问题等。 在压缩包文件"struts-2.5....

    Struts2 文档.doc

    总的来说,Struts2框架以其优雅的架构、丰富的标签库和强大的拦截器机制,为Java Web开发提供了高效且易于维护的解决方案。开发者可以通过Struts2轻松实现MVC模式,从而专注于业务逻辑的实现,而非底层的细节。

    struts2框架实例

    Struts2是一个强大的Java web应用程序开发框架,它基于Model-View-Controller(MVC)设计模式,为开发者提供了构建高效、可扩展且易维护的Web应用的解决方案。本实例是针对初学者的一个实践教程,旨在帮助理解并掌握...

    struts2——docs部分

    Struts2可以很好地与其他技术结合,如Spring、Hibernate等,形成完整的应用开发解决方案。 综上所述,“struts2——docs部分”应该包含了以上各个方面的详细信息,帮助开发者理解和使用Struts2框架。通过阅读这些...

    Struts2 工作原理 Struts2框架 有图

    一旦Action执行完成,ActionInvocation将根据struts.xml配置文件中定义的结果类型(Result),决定下一步的动作,如跳转至特定的视图页面(通常是JSP或FreeMarker模板)。这个过程实现了模型与视图的分离,提高了...

    struts2技术的bbs系统

    它整合了WebWork和Struts1的优点,提供了更加灵活的控制层解决方案,支持多种视图技术,如JSP、FreeMarker和Velocity等。 2. **Struts2的架构** - **Action**:Struts2的核心组件,负责处理用户请求,执行业务逻辑...

    struts2标签下的用户注册

    通过阅读博文链接(由于这里无法直接访问,需要自行点击链接查看),你可以获取更多关于Struts2用户注册实现的细节,包括具体代码示例、配置文件设置以及可能出现的问题和解决方案。 总之,理解并掌握Strts2标签在...

    Struts2框架实例

    Struts2通过整合多种优秀框架,如Tiles、FreeMarker、OGNL等,提供了一种强大的解决方案来构建和维护复杂的Web应用。 在描述中提到的"Structs相关实例"是为了帮助开发者更好地理解和掌握Struts2框架的实际应用。...

Global site tag (gtag.js) - Google Analytics