How to invoke a method on submit button in Struts 2 using convention plugin?

12.1k views Asked by At

In the following piece of code about Struts action class,

@Namespace("/admin_side")
@ResultPath("/WEB-INF/content")  // Default.

public final class TestAction extends ActionSupport implements Serializable
{
    private static final long serialVersionUID = 1L;
    private static final String SUCCESS = "success";

    private String name;
    private String email;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    @Action(value="test",results={@Result(name="success",location="Test.jsp")})
    public String execute() throws Exception
    {
        System.out.println("name = "+name);
        System.out.println("email = "+email);
        return SUCCESS;
    }

    // Some annotations to map this method to <s:submit>
    public String postAction()
    {
        System.out.println("postAction() invoked.");
        System.out.println("name = "+name);
        System.out.println("email = "+email);
        return SUCCESS;
    }
}

I want to invoke the postAction() method on the given submit button like so,

<s:form action="test">
    <s:textfield id="name" name="name" label="Enter your name"/>
    <s:textfield id="email" name="email" label="Enter your email"/>

    <s:submit value="Submit" method="postAction"/>
</s:form>

I have seen some questions like this one but all of them use XML configurations in struts.xml.

How to make it possible using convention plugin?

2

There are 2 answers

4
Roman C On BEST ANSWER

You need to use namespace attribute because your action is mapped with @Namespace.

<s:form namespace="/admin_side" action="test">
    <s:textfield id="name" name="name" label="Enter your name"/>
    <s:textfield id="email" name="email" label="Enter your email"/>

    <s:submit value="Submit" method="postAction"/>
</s:form>

If this doesn't work in the latest version according to WW-4023 then you can invoke a method directly

<s:url var="myUrl" namespace="/admin_side" action="test" method="postAction"/> 
<s:form action="%{#myUrl}">
    <s:textfield id="name" name="name" label="Enter your name"/>
    <s:textfield id="email" name="email" label="Enter your email"/>

    <s:submit value="Submit"/>
</s:form>

or configure params interceptor parameter excludeParams to remove method parameter from excluded parameters.

Note, either you use DMI and the methods above or don't use it, in this case you should map an action to the method and don't use attributes method or submit tag's method and action as the issue above restrict using special parameters. If you have multiple submit buttons if you follow that restriction you have to dynamically change form tag's action attribute using JavaScript onclick event.

5
Andrea Ligios On

Different methods are achievable by:

  1. moving the @Action annotation from method-level to class-level:

    @Action(value="test",results={@Result(name="success",location="Test.jsp")})
    public final class TestAction extends ActionSupport implements Serializable {
    
  2. renaming the execute() method into something else, eg. test():

    public String test() throws Exception {
        System.out.println("name = "+name);
        System.out.println("email = "+email);
        return SUCCESS;
    }
    
    public String postAction() {
        System.out.println("postAction() invoked.");
        System.out.println("name = "+name);
        System.out.println("email = "+email);
        return SUCCESS;
    }
    

From the documentation:

Applying @Action and @Actions at the class level

There are circumstances when this is desired, like when using Dynamic Method Invocation. If an execute method is defined in the class, then it will be used for the action mapping, otherwise the method to be used will be determined when a request is made (by Dynamic Method Invocation for example)

, and obviously by enabling Dynamic Method Invocation in Struts.xml.

But this should be avoided; read this article to understand why: Making Struts2 app more secure: disable DMI. Note that Struts2 Maven archetypes already has DMI set to false, because it is discouraged.

Then, you should turn off DMI, and start using multiple Actions instead of multiple methods, by simply annotating the other Action's methods too, and using different action attributes in the <s:submit/> button :

@Action(value="test",results={@Result(name="success",location="Test.jsp")})
public String test() throws Exception {
    System.out.println("name = "+name);
    System.out.println("email = "+email);
    return SUCCESS;
}

@Action(value="postAction",results={@Result(name="success",location="Test.jsp")})
public String postAction() {
    System.out.println("postAction() invoked.");
    System.out.println("name = "+name);
    System.out.println("email = "+email);
    return SUCCESS;
}

and in JSP:

<s:form namespace="/admin_side">
    <s:textfield id="name"  name="name"  label="Enter your name"  />
    <s:textfield id="email" name="email" label="Enter your email" />

    <s:submit value="Submit to test()"       action="test"       />
    <s:submit value="Submit to postAction()" action="postAction" />
</s:form>

EDIT: Since Struts 2.3.15.3, you need to explicitly enable the action: suffix with:

<constant name="struts.mapper.action.prefix.enabled" value="true"/>

Because it won't work by default.