Neither BindingResult nor plain target object for bean in spring 3 - spring

Hi I am facing an issue and looked all over internet but still not able to find out the root cause. I am posting my code snippet please help me out as I am new to spring 3. I am using modelAttribute in form and what I want that in controller all the values from request should be backed in the object so that I can perform validation and other business logic.
I know there is mistake only in my controller.
1) index.jsp
<form:form action="login" method="POST" modelAttribute="login">
<table>
<tr><td>User Id:</td><td><form:input path="userId"/></td></tr>
<tr><td>Password:</td><td><form:password path="userPassword"/></td></tr>
<tr><td></td><td><input type="submit" value="Login"/></td></tr>
</table>
</form:form>
2) Controller
#RequestMapping(value="/login/", method=RequestMethod.POST)
public String login(#ModelAttribute("login") #Valid Login login,BindingResult result)
{
System.out.println("We have entered into controller class");
if(result.hasErrors())
{
System.out.println("Errors:"+result.getFieldError("userReject"));
return "redirect:/login";
}
else
{
return "home";}
}
}
3) JBoss Log
04:35:29,067 ERROR [org.springframework.web.servlet.tags.form.InputTag] (http--0.0.0.0-8090-1) Neither BindingResult nor plain target object for bean name 'login' available as request attribute: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'login' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:141) [spring-webmvc-3.0.5.Release.jar:3.0.5.RELEASE]

The problem is not in the method you posted, which handles the login form submission. It's in the method used to display the form. The form needs to populate its fields from a bean named "login", and you didn't place any bean named "login" in the model, i.e. in a request attribute.
Side note: a login form should never use GET. It should use POST. You really don't want the password to appear in the browser address bar. And you don't want it to appear in the browser history, the server and proxy logs, etc.

Related

Modify and return ModelAttribute object if validation fails

I have simple entity ...
#Entity
public class WatchedDirectory {
#Column(nullable=false)
#NotBlank(message="Filesystem path CANNOT be empty")
String filesystemPath;
}
... and GET endpoint for creating one ...
#GetMapping("/add")
public String add(#ModelAttribute WatchedDirectory watchedDirectory) {
return "mng-dir-add";
}
... that shows form made in Thymeleaf, with error validation and all. Once you hit sumbmit button data goes into POST endpoint ...
#PostMapping("/add")
public String addExecute(#Valid #ModelAttribute WatchedDirectory watchedDirectory, BindingResult result, RedirectAttributes redirect, Model model) {
if(result.hasErrors()) {
// here I want to iterate through
// errors and clean erroneous fields
return "mng-dir-add";
}
watchedDirectory = fs.persistDirectory(watchedDirectory);
redirect.addFlashAttribute("added", watchedDirectory);
return "redirect:/list";
}
... and everything is nice and dandy. When data is valid it get persisted and redirect to list is issued (POST/Redirect/GET). When data is invalid thymeleaf's error fields are populated and I list error messages below appropriate fields.
The only thing I want to change, but I can't figure out how to, is to clear some data from model.
Things I tried so far: modifying #ModelAttribute parameter, setting attributes in Model, setting attributes in RedirectAttributes. Every time I get the very same data user provided without any changes in output form, for some reason I can't change a thing. I tried also redirecting to GET method but it seems it clears slate clean, which I don't want.
If someone is interested this is how form in thymeleaf looks:
<form id="content" action="#" th:action="#{/add}" th:object="${watchedDirectory}" method="post" class="was-validated">
<div class="form-group has-feedback has-error">
<label for="filesystemPath">Filesystem path:</label>
<input th:field="*{filesystemPath}" type="text" id="filesystemPath" name="filesystemPath" class="form-control" placeholder="~/" required />
<label th:if="${#fields.hasErrors('filesystemPath')}" th:errors="*{filesystemPath}"></label>
</div>
<button type="submit" class="btn btn-outline-success">Save</button>
</form>
required attribute on input field will shut up when provided with whitespace but there will be error message from Spring's validation. Clearing this field and returning it to user will make things more consistent than showing mixed signals such as:
Any help would be really appreciated.
You need to define a BeanPropertyBindingResult object that provides the fields having the errors. Then make a model with this results,
#PostMapping( "/add" )
public String addExecute( #Valid #ModelAttribute WatchedDirectory watchedDirectory, BindingResult result, RedirectAttributes redirect, Model model )
{
if( result.hasErrors() )
{
BeanPropertyBindingResult result2 = new BeanPropertyBindingResult( watchedDirectory, theBindingResult.getObjectName() );
for( ObjectError error : theBindingResult.getGlobalErrors() )
{
result2.addError( error );
}
for( FieldError error : theBindingResult.getFieldErrors() )
{
result2.addError( new FieldError( error.getObjectName(), error.getField(), null, error.isBindingFailure(), error.getCodes(), error.getArguments(), error.getDefaultMessage() ) );
}
model.addAllAttributes( result2.getModel() );
return "mng-dir-add";
}
watchedDirectory = fs.persistDirectory( watchedDirectory );
redirect.addFlashAttribute( "added", watchedDirectory );
return "redirect:/list";
}
According to Validation by Using Spring’s Validator Interface from the Spring Framework Documentation:
The Validator interface works by using an Errors object so that,
while validating, validators can report validation failures to the
Errors object.
validate(Object, org.springframework.validation.Errors): Validates
the given object and, in case of validation errors, registers those
with the given Errors object.
And the Errors interface doesn't provide any API for deregistering binding errors.
So, it seems there is no Spring provided way to achieve what you want.

Changing back the url to original if exception thrown and return back to form

I have a thymeleaf signup form, which if we submit then a controller at "/signup_do" is called which validates and saves the user to database:
<form action="/signup_do" method="post">
...
</form>
The controller at "/signup_do" passes the request to the accountRegistration service method, which does the validation:
#PostMapping("/signup_do")
public String register(Account account, HttpSession session) {
session.setAttribute("accountToRegister", account);
accountManagement.accountRegistration(account);
return "Success";
}
The account registration method can throw an exception SignupFormException, which is handled by the #ExceptionHandler defined in that controller class:
#ExceptionHandler(value=SignupFormException.class)
public String handle(HttpSession session, Model response) {
Account returnDataToForm = (Account) session.getAttribute("accountToRegister");
response.addAttribute("name", returnDataToForm.getFirstName());
session.invalidate();
return "signup";
}
Now the problem is that when exception occurs, the inputs entered in the form is passed back to the signup form, and the entered data remains intact, but the url still remains as /signup_do.
I have tried using return "redirect:/signup" instead, which does change the url, but it ends up making a get request to the /signup url like
/signup?name=John...
but my /signup controller is not designed to handle a get request, it just knows to display the form, so the information is lost.
#GetMapping("/signup")
public String signupPage() {return "signup";}
I also tried using forward:/signup, but that just ended up throwing 405 error.
I figured out a clean workaround a few hours after asking this question.
What I did is change the name of the controller that handles the signup process to ("/signup") as well. Since the controller that displays the page is a #GetMapping("/signup") and the one that handles the signup process is a #PostMapping("/signup") there is no clash.
Now even if the controller changes, the url remains the same, since both of them are signup...
#GetMapping("/signup")
public String signupPage() {return "signup";}
#PostMapping("/signup")
public String register(Account account, HttpSession session) {
session.setAttribute("accountToRegister", account);
accountManagement.accountRegistration(account);
return "success";
}
And this works just like I wanted!!
Redirecting will make a get request to the controller looking for the view to display, which in your situation means losing your data for the reasons you give. I can think of two workarounds:
Don't do the redirect and change the URL manually with javascript everytime you enter this view. If you dislike having a "wrong" URL in a view, editing it manually looks the most reasonable and direct approach. You can see how to do this here, including it in a script that executes everytime the page loads/the submit button is pressed.
Do the redirect and avoid losing your info by storing it in the session for a while longer, accessing it in thymeleaf in this way, instead of getting it from a model attribute. This would mean you would have to be careful to remove this session attributes later. It's also not very "clean" that your get request for the form view includes the user info, so I wouldn't go with this solution if avoidable.

how to display bindingresult errors on JSP page

I have a requirement to display errors on JSP page.
User enters credentials in login screen and it goes to logincontroller once login success user will be redirected to AccountsummaryController it has some backend calls to fetch data and display those data on landing page that is AccountSummary page. There could be possibility that some of the data may not be fetched for various reasons. I want those errors to be passed to the JSP page to show on landing page after successful login. Below is the code snippet of controller method in accountsummary controller. Also I am planning to have some validations in modelattribute i.e. stmtForm. I hope form errors also goes as part of the result.
#RequestMapping(method = { RequestMethod.GET }, value = "stmtSummary")
public ModelAndView getStatementSummary(#ModelAttribute("stmtForm") StatementForm stmtForm, BindingResult result, ModelMap modelMap, HttpServletRequest request, HttpServletResponse response)
throws Exception {
result.addError(new ObjectError("error1","unable to get account number"));
result.addError(new ObjectError("error2","unable to get routing number"));
if(result.hasErrors()){
System.out.println("Total errors are >>>>"+result.getErrorCount()); // *this is getting printed as 2*
}
}
Here is the tag I am using in JSP page.
<form:errors path="*"/>
I am not able to display errors on the UI. I am not able to find the what is wrong here. Please help
Since you havent posted model class or form, it is difficult to understand whether you have error1 or error2 as attribute in your StatementForm Bean. You are trying to show custom business errors with the spring mvc bindingResult. There are two options to do that:
Use custom errorObject and add it in modelMap and check that in jsp.
For using it with BindingResult, use:
result.rejectValue("property", "unable to get account number");
//or
result.reject("unable to get account number");

Spring modelattribute not recognized anymore after ajax update

i´ve encountered the following issue several times:
I use a Controller to bind a dto to a html form (via thymeleaf). Please note the model named "invoiceDto"
#RequestMapping(value = {"/create"}, method = RequestMethod.GET)
public String create(Locale locale, Model model) throws Exception {
final String login = this.getAuthentication().getCurrentApplicationUser();
if (login == null || login.isEmpty())
throw new Exception(this.getMessageSource().getMessage("label.findError", null, locale));
final Future<Setting> setting = settingService.findFirst();
final Future<ApplicationUserContactProjection> applicationUserContactProjection = applicationUserService.findByLogin(login);
while (!setting.isDone() && !applicationUserContactProjection.isDone()) {
Thread.sleep(100);
}
if (setting.get() == null || applicationUserContactProjection.get() == null)
throw new Exception(this.getMessageSource().getMessage("label.error.findError",
null, locale));
model.addAttribute("invoiceDto", new InvoiceDto(setting.get(), applicationUserContactProjection.get()));
model.addAttribute("message", this.getMessageSource().getMessage("label.navigation.invoiceCreation", null, locale));
return "invoice/create";
}
I have a html form (thymeleaf generated) where i use the above Java pojo dto with the given modelattribute name to fill my Input fields. This is an excerpt of it. The important part is the div with the id of "invoiceLineItems" where thymeleaf replaces its child div with a lineItems Fragment:
<form action="#" th:action="#{/invoice/newinvoice}" th:object="${invoiceDto}" role="form" method="post"
class="form-signin" id="editInvoiceForm"
accept-charset="utf-8">
<div id="invoiceLineItems"><div th:replace="invoice/items :: lineItems"></div></form>
The fragement contains the following stuff - an excerpt of it:
<td>
<input type="text"
th:field="*{items[__${index.index}__].lineItemTotalPrice}"
readonly
class="form-control" disabled
id="lineItemTotalPrice"/>
</td>
Excerpt of the given pojo:
public class InvoiceDto implements Serializable {
private Invoice invoice;
private List<LineItem> items;
I access the list like this:
th:field="*{items[__${index.index}__].lineItemTotalPrice}"
The Problem:
I can add items dynamically via Ajax. I serialize the whole form (for convenience reasons) and call a Controller Method:
#RequestMapping(value = {"/newlineitem"}, method = RequestMethod.POST)
public String newLineItem(#ModelAttribute("invoiceDto") InvoiceDto invoiceDto,
Model model)
throws Exception {
invoiceDto.addItem(new LineItem());
final Future<InvoiceDto> calculatedInvoiceDto = invoiceService.calculateInvoice(invoiceDto);
while (!calculatedInvoiceDto.isDone()) {
Thread.sleep(100);
}
model.addAttribute("invoiceDto", calculatedInvoiceDto.get());
return "invoice/dynamicitems :: lineItems";
}
As you can see, i let thymeleaf render a Special view, because after the Ajax success spring cannot set the modelattributes proper.
In short: After the Ajax Returns the partial view, the following will throw an exception:
th:field="*{items[__${index.index}__].lineItemTotalPrice}"
whereas this works - note the prefixed invoiceDto:
th:field="*{invoiceDto.items[__${index.index}__].lineItemTotalPrice}"
Question:
What´s wrong here?
Why do i have to prefix the name of the modelattribute after the partial Ajax update, whereas in the first run i don´t have to?
Thank you for your help!
EDIT:
For my share it looks like the way that spring "forgets" the originally named form modelattribute "invoiceDto" if the page is partially updated by an ajax call (to another spring controller, modifying invoiceDto) through a partial thymeleaf html.
So after the controller returns the partial thymeleaf view i have to access its fields with prefixed "invoiceDto", as if there would be no invoiceDto attribute.
Thanks again for your help!
UPDATE
As there is no progress on this i have raised a thymeleaf issue:
https://github.com/thymeleaf/thymeleaf/issues/795
Nevertheless i think this is a spring issue, because i have the same results with JSP.
Repository to comprehend this issue
https://mygit.th-deg.de/tlang/thymefail
If I understand your problem correctly, you're trying to use the *{} notation when there is no active object. When the ajax method returns just the "lineItems" fragment, Thymeleaf has no way of knowing that it belongs to a form with a th:object on it.
I guess the best solution is to return the whole form then, in js, extract the lineItems.
Or maybe just get rid of th:object altogether (convenient only when you want to show validation errors imho).

Handle url pattern that has user name as part of the url

Suppose I have 3 url patterns that needs to be handled by Spring MVC as follows:
1) www.example.com/login (to login page)
2) www.example.com/home (to my home page)
3) www.example.com/john (to user's home page)
I would like to know what is the best practice way of handling the url pattern that has username as part of the url (real world example is facebook fanpage www.faceboo.com/{fanpage-name})
I have come up with my own solution but not sure if this is the clean way or possible to do it.
In my approach, I need to intercept the request before it being passed to Spring MVC's dispatchservlet, then query the database to convert username to userid and change the request URI to the pattern that Spring MVC can recognize like www.example/user/userId=45.
But I am not sure if this is doable since the ServletAPI does not have the setter method
for requestURI(it does have the getter method for requestURI)
Or if you have a better solution please share with me. Thank in advance :-)
Spring MVC should be able to handle this just fine with PathVariables.
One handler for /login, one handler for /home, and one handler for /{userName}. Within the username handler you can do the lookup to get the user. Something like this:
#RequestMapping(value="/login", method=RequestMethod.GET)
public String getLoginPage() {
// Assuming your view resolver will resolve this to your jsp or whatever view
return "login";
}
#RequestMapping(value="/home", method=RequestMethod.GET)
public String getHomePage() {
return "home";
}
#RequestMapping(value="/{userName}", method=RequestMethod.GET)
public ModelAndView getUserPage( #PathVariable() String userName ) {
// do stuff here to look up the user and populate the model
// return the Model and View with the view pointing to your user page
}

Resources