Spring 3.1 MVC and Security: both login and registration form (multiple forms) on same page get submitted - spring-mvc

I'm using Spring (3.1), Spring MVC (3.1) and Spring Security (3.0) in combination and I've put together a single JSP page that has two forms on it; One is a form to login and the other is a form to register (i.e. create a new user).
For the register form, I use the Spring form tags, backed up by a controller to handle the request but for the login form I don't bother with the Spring form tags as I don't believe they're needed. There is also no controller that I need to write to handle the form submission as Spring Security takes care of authenticating so long as the request is submitted to j_spring_security_check.
The register form is working fine but the login form is a problem. It seems that when I click the submit button on the login form, the registration form is also submitted, or at least Spring thinks I'm trying to submit that form. Here is the JSP:
<form id="loginform" method="POST" action="<c:url value='j_spring_security_check'/>">
<label for="existing_email">Email:</label>
<input name="j_username" id="existing_email" type="text" value="${SPRING_SECURITY_LAST_USERNAME}" />
<label for="existing_password">Password:</label>
<input name="j_password" id="existing_password" type="password" />
<input id="login-form-submit" type="submit" value="Sign in" />
</form>
<form:form id="registrationform" modelAttribute="user" method="POST" action="register">
<form:label path="username" for="email">Email:</form:label>
<form:input path="username" name="username" id="email" type="text" />
<form:errors path="username" cssClass="formError" />
<form:label path="password" for="password">Password:</form:label>
<form:input path="password" name="password" id="password" type="password" />
<form:errors path="password" cssClass="formError" />
<input id="registration-form-submit" type="submit" value="Sign up" />
</form:form>
Notice that form tags for the input of type submit are not present and this seems to be a normal thing to do in the examples I've seen. Adding form tags to the submit button I guess doesn't make sense as it doesn't map to anything on the target object (user in this case).
When I click the "Sign in" button I get the following exception:
SEVERE: Servlet.service() for servlet [appServlet] in context with path [/project1] threw exception [An exception occurred processing JSP page /WEB-INF/views/registration.jsp at line 29
28: <form:form id="registrationform" modelAttribute="user" method="POST" action="register">
29: <form:label path="username" for="username">Username:</form:label>
30: <form:input path="username" name="username" id="username" type="text" />
31: <form:errors path="username" cssClass="formError" />
32:
Stacktrace:] with root cause
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'user' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:141)
This I recognise from cases where you forget to include the modelAttribute attribute in form:form, but of course I don't want to submit this form to my controller.
I have a feeling there is a mistake I'm making or a simple solution. Can anyone recommend a way around this or perhaps a different approach?
Here is the controller method that handles requests to register in case that's needed:
#RequestMapping(value = "**/register", method = RequestMethod.POST)
public String registerUser(#ModelAttribute("user") #Validated User user, BindingResult errors, ModelMap model) {
if (errors.hasErrors()) {
return "registration";
}
// Other stuff then...
return "profile"
}

If you are using "user" modelAttribute in form tag then a non-null request attribute must be present with name "user".
One way to add that in request attribute is what you did in your answer above. Other ways are:
(1) Add in ModelMap:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model) {
model.addAttribute("user", new User());
model.addAttribute("error", "true");
return "registration";
}
(2) Add in request scope (Using WebRequest or HttpServletRequest):
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model, WebRequest webRequest) {
webRequest.setAttribute("user", new User(), WebRequest.SCOPE_REQUEST);
model.addAttribute("error", "true");
return "registration";
}
(3) Use #ModelAttribute on method:
#ModelAttribute("user")
public User user() {
return new User();
}
Please also see Using #ModelAttribute on a method and Using #ModelAttribute on a method argument
Also note that you don't have to use type attribute. See form:input and form:password.

I think the problem is specifically when a login fails and the same page is served up, albeit on a different URL path and so through a different controller method. Therefore my original suspicion that the issue is that both forms are submitted may be something of a red herring, though I still don't fully understand what's going on and that may yet have something to do with it. In any case, this is what corrected the problem for me:
I had a controller method that originally looked like this:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model) {
model.addAttribute("error", "true");
return "registration";
}
In the Spring Security context I specify /loginfailed as the path to go to by default if a login attempt fails. This is where it seems the user object is needed so if I alter the signature as follows it all works:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(#ModelAttribute("user") User user, BindingResult errors, ModelMap model) {
model.addAttribute("error", "true");
return "registration";
}
Any comments/clarification welcome from others.

Related

MVC + Forms: Post error

I am getting the following error whenever I try to do a simple form post in my MVC website.
Either BinaryRead, Form, Files, or InputStream was accessed before the internal storage was filled by the caller of HttpRequest.GetBufferedInputStream.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: Either BinaryRead, Form, Files, or InputStream was accessed before the internal storage was filled by the caller of HttpRequest.GetBufferedInputStream.
My sample form and actions are pretty basic...
#using (Html.BeginForm("Create", "Form"))
{
<div class="row action">
<div class="row">
First name: <input type="text" name="fname"><br>
Last name: <input type="text" name="lname"><br>
</div>
<input type="submit" id="save" class="btn" value="Save"/>
<input type="button" id="cancel" class="btn" value="Cancel"/>
</div>
}
And my Controller action is even more basic...
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
Please share your route.config file that might help solving this problem.
Just in case also try removing the perimeters from Html.BeginForm() remove the name of the action and controller. As MVC has strong naming systems because of which we don't need to add that info.
if above doesn't solve your issue Share your route file.

Spring MVC is confusing get and post methods with the same request mapping

I have a login controller that I've mapped the "/login" path to two different methods. One will be called for get and the other for post.
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(Model model){
LoginDto loginDto = new LoginDto();
model.addAttribute("loginDto", loginDto);
return "home/login";
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String doLogin(#Valid LoginDto loginDto, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "home/login";
}
return "redirect:/";
}
I have the thymeleaf form
<form method="POST" th:action="#{/login}" th:object="${loginDto}">
<div class="form-group-row">
<label> Email </label>
<input type = "text" th:field = "*{email}"/>
<span th:if="${#fields.hasErrors('email')}" th:errors = "*{email}"></span>
</div>
<div>
<label> Password </label>
<input type = "text" th:field = "*{password}"/>
</div>
<input type="submit" />
</form>
When enter data and click submit the method with the GET request is called. I know this from inserting breakpoints in both methods. Also the url now has a ?errors at the end of it. I've also changed the url mapping to the second method to "doLogin" like this
#RequestMapping(value = "/dologin", method = RequestMethod.POST)
public String doLogin(#Valid LoginDto loginDto, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "home/login";
}
return "redirect:/";
}
and changed the form to this
<form method="POST" th:action="#{/dologin}" th:object="${loginDto}">
<div class="form-group-row">
<label> Email </label>
<input type = "text" th:field = "*{email}"/>
<span th:if="${#fields.hasErrors('email')}" th:errors = "*{email}"></span>
</div>
<div>
<label> Password </label>
<input type = "text" th:field = "*{password}"/>
</div>
<input type="submit" />
</form>
and it works. I can enter data and hit the submit button and I'm in the doLogin method. However, I would like to keep this mapping of GET and POST to the same url to do different things based on the request method.
Further more, when I created the form at first, I forgot to specify a method="post" and while testing it submitted get requests to "/login" from this form. Perhaps that wired something up that needs to be unwired.
Is this a bug? I can map the same url with different request methods to other controller methods but this one doesn't seem to want to work. Any Ideas?
I've figured it out. The reason the method that is mapped to the POST request is because I'm using spring security and it's not completely set up. The login page for spring security was mapped to /login as well and the appended
localhost/login?error
to the url string is something spring security appends when there is an error with the login process. I have not set up authentication with spring security yet so it believes there's an error. I will continue on setting up spring security but this is the reason my POST request was not mapped to the doLogin method.

SPRING MVC post method call not working

By JSP has below :
<h2>Student Information</h2>
<form:form method="POST" action="/HelloWeb/addStudent">
<table>
and my java controller code has below
#RequestMapping(value = "/addStudent", method = RequestMethod.POST)
public String addStudent(#ModelAttribute("SpringWeb") Student student,
ModelMap model) {
when i try to hit the post doesnt work i,e /HelloWeb/addStudent,
I tried making both places /HelloWeb/addStudent or just /addStudent that doesnt work.
FYI : HelloWeb here is the DispatchServletName given in web,xml
I am trying example given in site
http://www.tutorialspoint.com/spring/spring_mvc_form_handling_example.htm
I apologize if i am asking very basic easisest issue, BUt tried this # late nite and fed up so requesting ppl to help/suggest
The attribute in jsp require modelAddribute or commandName which is the class instance of the domain object. You did not specify it. So the
<form:form method="POST" modelAttribute="SpringWeb" action="/HelloWeb/addStudent">.
There is a standard way to do form post submission in spring. You need to do GET request mapping to map/bind the Student table with jsp, and POST mapping to submit jsp form data. An example in your case would be.
#RequestMapping(value = "/addStudent", method = RequestMethod.GET)
public String addStudent(#ModelAttribute("SpringWeb") Student student) {
return "addstudentJsp"; // your jsp page name where the spring:form is placed
In jsp page do this
<h2>Student Information</h2>
<form:form modelAttribute="SpringWeb">
<form:input id="name" path="name" type="text" class="form-control" />
<form:errors path="name" cssClass="text-danger"></form:errors>
// your student fields
<button type="submit">submit</button>
</form:form>
Now again in your controller have a post request method like
#RequestMapping(value = "/addStudent", method = RequestMethod.POST)
public String addStudent(#ModelAttribute("SpringWeb") Student student, #BindingResult result) {
//Call to your data persistence layer like StudentService
}
The modelAttribute does the binding for you
I faced same problem, I just rename my project "HelloWeb" and the problem was solved.

Default spring form field values are cleared after fail validation

I'm new to springMVC and came across this issue that I don't understand. I have a JSP with a form in which some fields get prepopulated in my get method with some
values I get from the database. when I submit my form through the POST method and the required fields are not filled then I call my GET method to show the form again with
error messages on the top of the page. Everything works good and my fields that need to be prepopulated gets populated correctly.
The issue that we came across in testing is that when we delete the prepopulated fields and submit again, my POST method calls my GET method to redisplay the page with error
messages and the error messages appear fine on the screen but my fields that suppose to be prepopulated with some values are not getting repopulated again even
though I am calling the database and populating my form with the model.put() method in my GET method everytime.
It seems like spring does not populate any field on the JSP if there are errors. Is that right?
This is a simplyfied version of my controller, but it will explain what I'm trying to do:
#RequestMapping(value = myPageURL, method = GET)
public String displayPage(#ModelAttribute("someForm") SomeForm someForm,
BindingResult bindingResult,
ModelMap model) {
//call the database and get some values
someform.setSomefield1("someValue1");
someform.setSomefield2("someValue2");
model.put("someform", someForm)
return VIEW;
}
#RequestMapping(value = myPageURL, method = POST)
public String submitform(#ModelAttribute("someForm") SomeForm someForm,
BindingResult bindingResult,
ModelMap model) {
validate(someForm, bindingResult);
if (!bindingResult.hasErrors()) {
//Display some page
}
return displayPage(someForm, bindingResult, model);
}
This is a section of the JSP:
<form:form action=myPageURL method="POST" modelAttribute="someForm">
<form:hidden path="cid"/>
<td>
<form:input path="someField1" size="10" maxlength="20" cssErrorClass="fieldError
error-text-first-name-label"/>
</td>
<td>
<form:input path="someField2" size="10" maxlength="20" cssErrorClass="fieldError
error-text-last-name-label"/>
</td>
<td>
<form:input path="someField3" size="10" maxlength="20" cssErrorClass="fieldError
error-text-last-name-label"/>
</td>
</form:form>
Thanks

Spring 3 MVC error: Neither BindingResult nor plain target object for bean name 'user' available

I know this issue might have been addressed elsewhere but I'm unable to find a satisfactory solution to my problem. Btw, I'm working with spring 3.0.2
Login.jsp
<form:form id="_LoginForm" name="LoginForm" modelAttribute="user" action="login" method="POST">
<form:input path="username" value=""/>
<form:input path="password" value=""/>
<input type="submit" value="Submit"/>
LoginController.java
#RequestMapping(value="login", method=RequestMethod.POST)
public String login(#ModelAttribute("user") User user, BindingResult result) {
System.out.println("recd request");
return null;
}
When I try to access the login.jsp page, I get the following error:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'user' available as request attribute
at org.springframework.web.servlet.support.BindStatus.(BindStatus.java:141)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:174)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:194)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:160)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:123)
at org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java:409)
at org.springframework.web.servlet.tags.form.InputTag.writeTagContent(InputTag.java:140)
at org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java:102)
Can you please provide your RequestMethod.GET method in the controller?
Just want to make sure you are adding the modelAttribute in the GET method as well.
I added the the following method to make this work, though I feel there must be a better way to get this working without having to write a setup method everytime.
applicationContext.xml
<mvc:view-controller path="/" view-name="index" />
index.jsp
<jsp:forward page="index.action"/>
LoginController.java
#RequestMapping(value="index.action", method=RequestMethod.GET)
public String setupLogin(Map<String, Object> modelMap) {
modelMap.put("user", new User());
return "Login";
}

Resources