Struts2 INPUT 结果:它是如何工作的?如何处理转换/验证错误?

IT小君   2021-09-12T02:21:48

主要问题

工作流程应该是这样的:如果输入的是不是数字的String,首先要通过异常拦截器,通过param拦截器时,转换为int类型时,使用Integer.parseIntand an会发生异常;不应该将该异常(即NumberFormatException)推入 Value Stack 吗?NumberFormatException即使不应打印结果,为什么它不显示和显示结果?

副题

每当我在表单中添加一个字母表时,它就会变成零...?为什么这样 ?

索引.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>

除法.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;
    }


}

结果.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>
评论(1)
IT小君

主要问题:

工作流程应该是这样的,如果输入的不是数字的字符串,首先要通过异常拦截器,通过param拦截器时,转换为int类型时,使用Integer就不行了。 parseInt 并且会发生异常,该异常是数字格式异常应该被推入值堆栈?那么为什么它不显示 numberformatexception 并显示结果,即使结果不应该被打印出来呢?

概念

Struts 2 自动处理转换错误验证错误:它不会引发异常,因为它们不是阻塞错误,而是输入错误,因此最好的处理方式是通知用户提交的输入是错误的,询问他对于新的有效输入。为此,将返回 INPUT 结果,而忽略 Exception。

详细工作流程

  1. Parameters Interceptor试图设置的参数。如果一个RuntimeException(like NumberFormatException) 被捕获并且devModetrue,则会向 中添加一条错误消息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);
                }
            }
        }
    }
    
  2. Conversion Errors Interceptor为每一个找到,它增加了一个:如果任何转换误差发生的检查Field Error; 它还保存原始值,以便对该值的任何后续请求都返回原始值而不是 action 中的值从文档:

    此拦截器将在 ActionContext 的转换错误映射中发现的任何错误添加为字段错误(前提是该操作实现了 ValidationAware)。此外,任何包含验证错误的字段都会保存其原始值,以便对该值的任何后续请求返回原始值而不是操作中的值。这很重要,因为如果提交了值“abc”并且无法转换为 int,我们希望再次显示原始字符串(“abc”)而不是 int 值(可能是 0,这没有什么意义)给用户)。

  3. Validation Interceptor所有的验证请求的执行(在XML,注解或通过定义的validate()validateXXX()行动的方法),将一个或多个错误消息到Field Errors每个字段不通过一个或多个验证标准。

  4. Workflow Interceptor检查是否存在Field Errors(无论是从转换错误或验证错误来)。如果没有发现错误,它将继续链接到下一个拦截器。如果发现一个或多个错误,则返回 INPUT 结果。

为确保此机制有效,您需要在自定义堆栈中以正确的顺序定义这四个拦截器,如果您不使用默认拦截器堆栈(否则您无需执行任何操作)。来自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 -->

侧面问题:

每当我在表单中添加一个字母表时,它就会变成零...?为什么这样?

最初的答案是:框架在向服务器发布请求时无法将 a 设置Stringint字段,并且在检索结果页面中的值时,它调用该变量的 Getter;由于您定义了 anint而不是 an Integer,并且 anint不能为空,因此它将返回 an 的默认值int0

但我不记得转换拦截器声称(阅读第 2 点)保存原始值,在后续的未来请求中提供它们,代替 Action 值(即 null 或 0)。这也在类型转换错误处理中提到

类型转换错误处理提供了一种简单的方法来区分输入验证问题和输入类型转换问题。

类型转换期间发生的任何错误可能希望也可能不希望报告。例如,报告输入“abc”无法转换为数字可能很重要。另一方面,报告无法将空字符串 "" 转换为数字可能并不重要 - 特别是在难以区分用户未输入值与输入空白值的 Web 环境中。

...

相反,我记得你的问题中描述的行为。所以这个案子已经处理了......为什么它不起作用呢?罪魁祸首,在我的情况下(也可能是你的),是value属性:

这将0在发布时为您提供abc

<s:textfield name = "myIntField" 
            value = "%{getText('format.number',{myIntField})}" />

因为发生了进一步的转换错误。

这两种情况改为如上所述工作,abc在发布时为您提供abc

<s:textfield name = "myIntField" />

<s:textfield name = "myIntField" 
            value = "%{myIntField}" />

结论

  • 确保拦截器堆栈配置正确,并且
  • 仔细检查您的代码(这很可能不是此处发布的代码),看看您对您的value属性做了什么

出于测试目的,首先尝试删除该value属性,看看它是否以正确的方式工作,然后开始寻找错误。

2021-09-12T02:21:49   回复