views:

117

answers:

3

Apologies if this question has been asked before. I'm hoping that someone can step in and help me figure out why my form validation errors aren't showing up.

I'm using Spring 3.0.3 and Hibernate, and I'm using jsr-303 validation to validate my form inputs. I have a Spring controller that handles GETting a page that contains a form that is created with the help of Spring's form taglib. In this form a user is able to change their name and have it saved to the database. If any of the input is empty then the page with the form should be displayed again with error messages. The same controller handles the page's submission. It seems that the controller is functioning properly in most respects, but when there is an error in the user submitted form, no errors are showing up on the page.

Here is what form looks like:

<form:form commandName="changeNameCommand">
    <form:errors path="*" cssClass="errorBox" />

    <table width="100%" border="0" cellspacing="5" cellpadding="5" align="left">
      <tr>
    <td><b>First Name:</b></td>
    <td><form:input path="firstName" value="${user.firstName}" /></td>
  </tr>
  <tr>
    <td><b>Last Name:</b></td>
    <td> <form:input path="lastName" value="${user.lastName}" /></td>
  </tr>
</table>
</form:form>    

Note that there is a user object in the view that is used to populate the form with the user's current first and last name. This is working properly.

The controller looks something like this:

@Controller
@RequestMapping(value =  "/account/settings/change-name")
@SessionAttributes("changeNameCommand")
public class ChangeNameController {

    @ModelAttribute("changeNameCommand")
    public ChangeNameCommand getCommand() {
        return new ChangeNameCommand();
    }

    @RequestMapping(method = RequestMethod.GET)
    public ModelAndView getChangeNamePage(HttpServletRequest req) {
    ModelAndView mav = new ModelAndView("Account.ChangeName");
        mav.addObject("page_title", "Change Name");

        return mav;
    }

    @RequestMapping(method = RequestMethod.POST)
    public String doChangeName(
                @ModelAttribute("changeNameCommand") 
                    @Valid ChangeNameCommand command, 
                    BindingResult result, SessionStatus status) {

        if (result.hasErrors()) {
            return "redirect:/account/settings/change-name"; 
        }

    // Code here to persist updated user first and last name to database...        

        status.setComplete();

        return "redirect:/home";
    }
}

I'm using Tiles 2.1.2 to compose pages and Urlrewrite 3.1.0 to help form friendly urls.

The ChangeNameCommand class looks something like this:

import org.hibernate.validator.constraints.NotEmpty;

public class ChangeNameCommand {

    @NotEmpty(message = "You must provide a first name.")
    private String firstName;

    @NotEmpty(message = "You must provide a last name.")
    private String lastName;

    @NotEmpty(message = "Your password is required to make changes.")
    private String currentPassword;

    // Getters and setters here...
}

When debugging, I see that when there is not input for either the first or last name the BindingResult instance does contain errors. My concern is the redirect when there is an error. I've seen other questions here that just return the view name instead of using the redirect: prefix. I tried that but (I think) because of the way I'm using Urlrewrite and the way my servlet mapping is set up Spring returns an error. I've tried returning

/account/settings/change-name
/web/account/settings/change-name
/mywebsite/web/account/settings/change-name

but to no avail. FWIW, here is my servlet-mapping:

<servlet-mapping>
    <servlet-name>mywebsite</servlet-name>
    <url-pattern>/web/*</url-pattern>
</servlet-mapping>

Any help much appreciated!

+2  A: 

using redirect makes all request attributes (including errors and the whole model) disappear. So, don't use redirect, or use the session to temporarily store the data, or use conversations. Or figure out how to use something like a flash-scope (I'm about to in a while)

Another thing - using UrlRewriteFilter with spring-mvc is uncalled for. You have full control over your beatuful REST-like URLs with spring-mvc only.

Bozho
Thanks Bozho. How might I change my code to store data in the session? I thought that was happening already, but obviously not. Thanks!
richever
A: 

What I may suggest is to forward your request not redirect.

return "forward:/home";

You can do this or just return to the home jsp itself

return "home";

Both will maintain the request data through the transition.

John V.
A: 

Here is how I solved my problem. To start off with, I didn't want to drop my use of UrlRewriteFilter and Tiles. However, the problem with this was that, in the case of errors, I couldn't just return the path, as indicated indicated in the controllers RequestMapping annotation. Below is my solution, with the redirect removed in the case of errors, in doChangeName().

@Controller
@RequestMapping(value =  "/account/settings/change-name")
@SessionAttributes("changeNameCommand")
public class ChangeNameController {

    @ModelAttribute("changeNameCommand")
    public ChangeNameCommand getCommand() {        
        return new ChangeNameCommand();
    } 

    @RequestMapping(method = RequestMethod.GET)
    public ModelAndView getChangeNamePage() {
        ModelAndView mav = new ModelAndView("Account.ChangeName");
        mav.addObject("page_title", "Change Name");

        return mav;
    }

    @RequestMapping(method = RequestMethod.POST)
    public ModelAndView doChangeName(@ModelAttribute("changeNameCommand") @Valid ChangeNameCommand command, 
            BindingResult result, SessionStatus status) {

        if (result.hasErrors
            ModelAndView mav = new ModelAndView("Account.ChangeName");
            mav.addObject("page_title", "Change Name");

            return mav;
         }

        // Code here to persist updated user first and last name to database...         

        status.setComplete();

        RedirectView view = new RedirectView("/home");
        return new ModelAndView(view);        
    }
}

Thanks to everyone who helped me out on this!

richever