在'class xxx上设置'xxx'时出现意外异常:错误设置表达式'xxx'的值['x',]

我通过查询字符串将一些参数传递给实现 ModelDriven 的动作类.

I'm passing some parameters to an action class implementing ModelDriven<Transporter> through a query-string.

<s:form namespace="/admin_side" action="Test" id="dataForm" name="dataForm">
    <s:url id="editURL" action="EditTest" escapeAmp="false">
        <s:param name="transporterId" value="1"/>
        <s:param name="transporterName" value="'DHL'"/>
    </s:url>
    <s:a href="%{editURL}">Click</s:a>
</s:form>

动作类如下.

@Namespace("/admin_side")
@ResultPath("/WEB-INF/content")
@ParentPackage(value = "struts-default")
public final class TestAction extends ActionSupport 
                           implements Serializable, Preparable, ModelDriven<Transporter>
{
    private static final long serialVersionUID = 1L;
    private Transporter transporter = new Transporter();

    @Action(value = "Test",
    results = {
        @Result(name = ActionSupport.SUCCESS, location = "Test.jsp"),
        @Result(name = ActionSupport.INPUT, location = "Test.jsp")},
    interceptorRefs = {
        @InterceptorRef(value = "paramsPrepareParamsStack", 
                 params = {"params.acceptParamNames", "transporterId, transporterName"})})
    public String load() throws Exception {
        return ActionSupport.SUCCESS;
    }

    @Action(value = "EditTest",
    results = {
        @Result(name = ActionSupport.SUCCESS, location = "Test.jsp"),
        @Result(name = ActionSupport.INPUT, location = "Test.jsp")},
    interceptorRefs = {
        @InterceptorRef(value = "paramsPrepareParamsStack", 
                 params = {"params.acceptParamNames", "transporterId, transporterName"})})
    public String edit() {
        System.out.println(transporter.getTransporterId() 
                         + " : " + transporter.getTransporterName());
        return ActionSupport.SUCCESS;
    }

    @Override
    public Transporter getModel() {
        return transporter;
    }

    @Override
    public void prepare() throws Exception {}
}

服务器终端显示以下消息.

The server terminal displays the following messages.

Jan 09, 2014 4:06:32 PM com.opensymphony.xwork2.interceptor.ParametersInterceptor error
SEVERE: Developer Notification (set struts.devMode to false to disable this message):
Unexpected Exception caught setting 'transporterId' on 'class actions.TestAction: Error setting expression 'transporterId' with value ['1', ]
Jan 09, 2014 4:06:32 PM com.opensymphony.xwork2.interceptor.ParametersInterceptor error
SEVERE: Developer Notification (set struts.devMode to false to disable this message):
Unexpected Exception caught setting 'transporterName' on 'class actions.TestAction: Error setting expression 'transporterName' with value ['DHL', ]

即使日志级别是 SEVERE,这些参数的值在操作类中也可以作为

Even though the log level is SEVERE, the values of these parameters are available in the action class as

System.out.println(transporter.getTransporterId() 
                 + " : " + transporter.getTransporterName());

edit() 方法中.

如果 paramsPrepareParamsStackdefaultStack 替换,那么这些消息就会消失.

If paramsPrepareParamsStack is replaced by defaultStack then, these messages disappear.

['DHL', ] 这样的表达式表示一个数组.模型中的 transporterIdtransporterName 分别是 LongString 类型.

Expressions like ['DHL', ] indicate an array. transporterId and transporterName in the model are, however of type Long and String respectively.

我做错了什么?

推荐答案

这里不涉及数组问题(即使看起来像那样):这种异常意味着Struts找不到你的参数的Setter:

No array problem is involved here (even if it seems like that): this kind of exception means that Struts can't find a Setter for your parameter:

来自 ParametersInterceptor 文档:

缺少参数的警告

当给定参数名称没有设置器时,会出现警告消息像下面这样会在 devMode 下登录:

When there is no setter for given parameter name, a warning message like below will be logged in devMode:

SEVERE: Developer Notification (set struts.devMode to false to disable this 
message):
Unexpected Exception caught setting 'search' on 'class demo.ItemSearchAction: 
Error setting expression 'search' with value ['search', ]
Error setting expression 'search' with value ['search', ] - [unknown location] 
  at com.opensymphony.xwork2.ognl.OgnlValueStack.handleRuntimeException(OgnlValueStack.java:201)
  at com.opensymphony.xwork2.ognl.OgnlValueStack.setValue(OgnlValueStack.java:178)
  at com.opensymphony.xwork2.ognl.OgnlValueStack.setParameter(OgnlValueStack.java:152)

因此是允许开发人员发现缺少的 setter 的预期行为或参数名称或设置器中的拼写错误.

Thus is expected behaviour to allow developer to spot missing setter or typo in either parameter name or setter.

您可以通过在 JSP 中放入 Action 中不存在的元素来轻松重现此错误.

You can easily reproduce this error by putting an element in JSP that does not exist in the Action.

由于模型中存在您的属性(及其设置器),并且您正在使用 ModelDrivenparamsPrepareParamsStack,我认为正在发生的事情是:

Since your properties exist (with their Setters) in the Model, and you are using ModelDriven and paramsPrepareParamsStack, what I think is going on is:

  • ModelDriven Interceptor被委托处理Model对象;
  • 第一次调用ParametersInterceptorModelDrivenInterceptor还没有运行;
  • 那么您的 Action 对 Model 对象一无所知,并尝试在 Action 中而不是在 Model 中查找参数的 Setter.
  • 第二个拦截器在 ModelDriven 之后运行,并且确切地知道在哪里设置参数.这就是您在 Action 方法中正确设置参数的原因.
  • ModelDriven Interceptor is delegated to handle the Model object;
  • The first time you call Parameters Interceptor, ModelDriven Interceptor has not run yet;
  • Then your Action knows nothing about the Model object, and try to find the Setters for your parameters in the Action, NOT in the Model.
  • The second interceptor instead runs after the ModelDriven one, and knows exactly where to set the parameters. This is why you have the parameters correctly set in the Action method.

但如果这是真的,那么您应该不能够在 prepare() 方法中检索这些参数(这就是您使用此堆栈的原因...):
请尝试,并在此处发布结果.

But if this is true, then you should NOT be able to retrieve those parameters in the prepare() method (that is the reason you are using this stack...):
please try, and post here the result.

我想到解决这个问题的第一件事是将 ModelDriven Interceptor 放在第一个 Parameters Interceptor 之前(通过复制或移动它,我不确定它会在这两种情况下产生哪种副作用,如果有的话,你应该再次尝试并在此处报告).

The first thing that comes to my mind to resolve this issue, is to place the ModelDriven Interceptor before the first Parameters Interceptor (by either copying it, or by moving it, I'm not sure which kind of side effect, if any, it could produce in both cases, you should again try and reporting it here).

然后定义下面的堆栈,并使用它.

Then define the following stack, and use it.

<interceptor-stack name="modelParamsPrepareParamsStack">
    <interceptor-ref name="exception"/>
    <interceptor-ref name="alias"/>
    <interceptor-ref name="i18n"/>
    <interceptor-ref name="checkbox"/>
    <interceptor-ref name="multiselect"/>

    <!-- NEW ModelDriven Position -->
    <interceptor-ref name="modelDriven"/>

    <interceptor-ref name="params">
        <param name="excludeParams">^dojo..*,^struts..*,^session..*,^request..*,^application..*,^servlet(Request|Response)..*,^parameters..*,^action:.*,^method:.*</param>
    </interceptor-ref>
    <interceptor-ref name="servletConfig"/>
    <interceptor-ref name="prepare"/>
    <interceptor-ref name="chain"/>

    <!-- OLD ModelDriven Position -->
    <!--interceptor-ref name="modelDriven"/-->

    <interceptor-ref name="fileUpload"/>
    <interceptor-ref name="staticParams"/>
    <interceptor-ref name="actionMappingParams"/>
    <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>
</interceptor-stack>

希望对您有所帮助.

相关文章