Struts2输入结果:它是如何工作的? 如何处理转换/验证错误?
主要问题
工作流程应该是这样的:如果输入的字符串不是一个数字,首先它应该通过一个异常拦截器,当通过param拦截器,而转换为int类型,它不会能够使用Integer.parseInt
会发生异常; 应该不是那个异常(即NumberFormatException
)被推入到值栈? 为什么不显示NumberFormatException
并显示结果,即使结果不应被打印,而不是?
侧面问题
每当我在表单中添加一个字母,它就会变成零…? 为什么这样 ?
的index.jsp
<%@ taglib uri="/struts-tags" prefix="s"%> <s:form action="divide"> <s:textfield name="number1" label="number1"/> <s:textfield name="number2" label="number2"/> <s:submit value="divide"/> </s:form>
divide.java
package actions; public class divide { int number1,number2,result; public String execute() throws Exception { result=number1/number2; return "success"; } public int getNumber1() { return number1; } public void setNumber1(int number1) { this.number1 = number1; } public int getNumber2() { return number2; } public void setNumber2(int number2) { this.number2 = number2; } public int getResult() { return result; } }
result.jsp中
<%@taglib uri="/struts-tags" prefix="s" %> <b> the result of division is <s:property value="result"/> </b> <jsp:include page="index.jsp"></jsp:include>
处理程序jsp
<%@taglib uri="/struts-tags" prefix="s"%> <b> following exception occured during the processing <s:property value="exception"/> </b> <jsp:include page="index.jsp"/>
在struts.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="yo" extends="struts-default"> <action name="divide" class="actions.divide"> <exception-mapping result="error" exception="Exception"/> <result name="success">/result.jsp</result> <result name="error">/handler.jsp</result> </action> </package> </struts>
主要问题:
工作流程应该是这样的,如果输入的字符串不是一个数字,首先它应该通过一个异常拦截器,而当通过param拦截器,而转换为int类型,它不会能够使用整数。 parseInt和一个异常会发生,数字格式异常的异常应该被推入到价值堆栈?为什么它不显示numberformatexception并显示结果,即使结果不应被打印,而不是?
概念
Struts 2自动处理转换错误和验证错误 :它不会引发异常,因为它们不会阻塞错误 ,而是输入错误 ,因此最好的方法是通知用户输入错误,请求他为新的,有效的输入。 为了达到这个目的,返回一个INPUT结果,而Exception被忽略。
详细的工作流程
-
Parameters Interceptor
试图设置参数。 如果捕获到RuntimeException
(如NumberFormatException
)并且devMode
为true
,则会向Action Errors
添加一条错误消息,否则该异常将被简单吞下。 从源代码:for (Map.Entry<String, Object> entry : acceptableParameters.entrySet()) { String name = entry.getKey(); Object value = entry.getValue(); try { newStack.setParameter(name, value); } catch (RuntimeException e) { if (devMode) { String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{ "Unexpected Exception caught setting '" + name + "' on '" + action.getClass() + ": " + e.getMessage() }); LOG.error(developerNotification); if (action instanceof ValidationAware) { ((ValidationAware) action).addActionMessage(developerNotification); } } } }
-
Conversion Errors Interceptor
检查是否发生任何转换错误:找到每一个,它添加一个Field Error
; 它也保存了原来的值,这样任何后续的对这个值的请求都会返回原来的值而不是action中的值 。 从文档:这个拦截器将ActionContext的conversionErrors映射中发现的任何错误添加为一个字段错误(假设该操作实现了ValidationAware)。 另外,包含验证错误的任何字段都会保存其原始值,以便后续对该值的请求将返回原始值而不是操作中的值。 这很重要,因为如果提交值“abc”并且不能转换为int,我们希望再次显示原始字符串(“abc”),而不是int值(可能为0,这会使得很少意义给用户)。
-
Validation Interceptor
执行所有请求的验证(在XML中,Annotations中定义或者通过Action的validate()
或validateXXX()
方法),为每个字段添加一个或多个错误消息给未通过一个或多个验证条件的Field Errors
。 -
Workflow Interceptor
检查是否有Field Errors
(都来自转换错误或验证错误)。 如果没有发现错误,它会继续下一个拦截器。 如果找到一个或多个错误,则返回一个INPUT结果。
为了确保这个机制有效,如果你没有使用Default Interceptors Stack(你不需要做任何事情),你需要在你的自定义堆栈中以正确的顺序定义这四个拦截器。 来自struts-default.xml
:
<!-- others interceptors here... --> <interceptor-ref name="params"> <param name="excludeParams">^dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,^parameters\..*,^action:.*,^method:.*</param> </interceptor-ref> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <!-- ... others interceptors here -->
边问题:
每当我在表单中添加一个字母,它就会变成零…? 为什么这样?
原来的答案是:在将请求发送到服务器时,框架无法将String
设置为int
字段,并且在检索结果页面中的值时,会调用该变量的Getter; 既然你定义了一个int
而不是一个Integer
,并且一个int
不能为null,它将返回一个int
: 0的默认值。
但是我不记得Conversion Interceptor声明(读取点n.2)保存原始值,以便在以后的请求中提供这些值,而不是Action值(可能为null或0)。 类型转换错误处理中也提到了这一点:
类型转换错误处理提供了一种简单的方法来区分输入验证问题和输入类型转换问题。
类型转换过程中发生的任何错误都可能希望或不希望被报告。 例如,报告输入“abc”不能转换为数字可能很重要。 另一方面,报告空字符串“”不能被转换为数字可能并不重要 – 特别是在一个网络环境中,很难区分用户没有输入值和输入空白值。
…
相反,我记得你的问题中描述的行为。 所以这个案子已经被处理了…为什么它不工作呢? 罪魁祸首,在我的情况(可能是你的),是value
属性:
发布abc
时这会给你0
<s:textfield name = "myIntField" value = "%{getText('format.number',{myIntField})}" />
因为进一步的转换错误发生。
这两个情况,而不是像上面所描述的,发布abc
时给你abc
:
<s:textfield name = "myIntField" /> <s:textfield name = "myIntField" value = "%{myIntField}" />
结论
- 确保拦截器堆栈配置正确,并且
- 仔细检查你的代码(这很可能不是这里发布的代码),看看你的
value
属性做什么。
出于测试目的,首先尝试删除value
属性,以正确的方式查看它,然后开始寻找bug。