Add Error on GET METHOD Spring annotated controller - java

I have this scenario:
I display a list of assets to users.
User selects an asset and then clicks add item
On my requestmapping, during GET operation. I use the service class to check if indeed this asset still exist in DB
If not, user should be notified by a message. I use the form:error tag
My problem is when I add the error object in the method signature, I got this error:
Errors/BindingResult argument declared without preceding model attribute
Code:
#RequestMapping(value = "/addItemsToAsset.htm", method = RequestMethod.GET)
public String setupForm(#RequestParam("assetID") Long assetID,
Errors error, ModelMap model) {
AssetItemVo voAsset = null;
if (assetID != null && assetID != 0) {
//Get data for asset from DB using assetID
List<AssetDraftTempVo> lstDraft = service.getAssetDraftByLngID(assetID);
if (lstDraft.size() == 0) {
voAsset = new AssetItemVo();
// I wanted to add validation here. If no data for asset id is found, I would like to add an error to the error object
error.reject("123","Unable to find info for the asset in the database.");
} else {
AssetDraftTempVo voDraft = lstDraft.get(0);
voAsset = new AssetItemVo();
voAsset.setStrPlant(voDraft.getStrPlant());
.
. /*other DTO property here*/
.
}
}
model.put("assetItemDetail", voAsset);
return "additemstoasset";
}
My goal is that during the display of the form, I wanted to populate the error object right away (if there is an error)
Here's my form for clarity.
<form:form modelAttribute="assetItemDetail" method="post">
<div id="error_paragraph">
<form:errors path="*" cssClass="errors" />
</div>
</form:form>
To get past the error, I manually change the method signature and added the model attribute but it still cannot populate the form:error tag
#RequestMapping(value = "/addItemsToAsset.htm", method = RequestMethod.GET)
public String setupForm(#RequestParam("assetID") Long assetID,
#ModelAttribute("assetItemDetail") AssetItemVo voAssetData, Errors error,
ModelMap model)

If you want to associate a BindingResult with a model attribute that doesn't present in the method signature, you can do it manually:
BindingResult result = new Errors();
...
model.put(BindingResult.MODEL_KEY_PREFIX + "assetItemDetail", result);
model.put("assetItemDetail", voAsset);

Spring MVC needs to associate a bunch of errors with some objects on the model, so tags such as <form:errors path="..." /> will correspond to model objects according to the path attribute. In this case, it does so by looking for the Errors argument directly after the ModelMap argument in your controller method.
Try swapping the error and model method arguments and see if it clears up the "Errors/BindingResult argument declared without preceding model attribute" error.
Change this:
public String setupForm(#RequestParam("assetID") Long assetID,
#ModelAttribute("assetItemDetail") AssetItemVo voAssetData, Errors error,
ModelMap model)
to this:
public String setupForm(#RequestParam("assetID") Long assetID,
#ModelAttribute("assetItemDetail") AssetItemVo voAssetData, ModelMap model,
Errors error)

Related

Spring boot webapp values not showing up in html page from controller

OK this has to be an easy one for someone who is experienced but I am just not seeing it. Trying to return a value from the controller back to the screen. All I ever get is "${user.email}" to show up in my input field.
<input type="email" class="form-control" name="email" disabled=true value=${user.email}>
My java controller below. Debugging it i verified values are all there and nothing is missing as the "credentials" object are coming from another screen. The screen renders fine to the "loggedIn" screen but the input field(above) as it passes the "exists" check. Why wont the email render on the input field?
#Controller
public class HomeController {
#RequestMapping(value = "/loggedIn", method = RequestMethod.POST)
public ModelAndView loggedIn(Credentials credentials, Model model) {
ModelAndView mv = new ModelAndView();
ScraperMqSQL aDb = new ScraperMqSQL();
boolean exists = false;
try {
User aUser = new User();
aUser.setUSer(credentials.getUserName());
aUser.setEmail(credentials.getUserName());
aUser.setPassWord(credentials.getPassWord());
mv.addObject("user", aUser);
model.addAttribute("user", aUser);
exists = aDb.doesUserExist(aUser);
} catch (SQLException e) {
e.printStackTrace();
}
if(exists) {
mv.setViewName("loggedIn");
}else {
mv.setViewName("notify");
}
return mv;
}
}
It might be possible because you are using this.
aUser.setEmail(credentials.getUserName());
You should set email here.
Since Thymeleaf is built in such a way that you can view and edit the HTML templates as plain HTML, you must identify to the Thymeleaf engine when you want it to perform replacement. In this case, value (as most HTML tags) is simply used as-is. If you want Thymeleaf to insert a dynamic value, use th:value or data-th-value.

Spring MVC 4 display an error message [duplicate]

My objective is to pass model attributes from controller to JSP page during a redirect and avoid the attribute being displayed in URL. The source code below is validating login from datastore using java data objects.
Controller:
#Controller
public class LoginController {
int count;
PersistenceManager pm = PMF.get().getPersistenceManager();
//Instance of data class
User user;
ModelAndView modelAndView=new ModelAndView();
#RequestMapping(value="/Login",method = RequestMethod.POST)
public ModelAndView loginValidate(HttpServletRequest req){
//Getting login values
String uname=req.getParameter("nameLogin");
String pswd1=req.getParameter("pswdLogin");
count=0;
user=new User();
//Generating Query
Query q = pm.newQuery(User.class);
q.setFilter("userName == userNameParam");
q.declareParameters("String userNameParam");
try{
List<User> results = (List<User>) q.execute(uname);
for (User u: results) {
String userName=u.getUserName();
if(userName.equals(uname)){
System.out.println(u.getPassword());
if(u.getPassword().equals(pswd1)){
count=count+1;
modelAndView.setViewName("redirect:welcome");
modelAndView.addObject("USERNAME",uname);
return modelAndView;
}
//rest of the logic
}
JSP:
<h1>Welcome ${USERNAME} </h1>
My current URL is /welcome?USERNAME=robin
My goal is to display it as /welcome
Also, my page is supposed to display "Welcome robin" whereas it displays only Welcome.
RedirectAttributes only work with RedirectView, please follow the same
#RequestMapping(value="/Login",method = RequestMethod.POST)
public RedirectView loginValidate(HttpServletRequest req, RedirectAttributes redir){
...
redirectView= new RedirectView("/foo",true);
redir.addFlashAttribute("USERNAME",uname);
return redirectView;
}
Those flash attributes are passed via the session (and are destroyed immediately after being used - see Spring Reference Manual for details). This has two interests :
they are not visible in URL
you are not restricted to String, but may pass arbitrary objects.
You need to be careful here because I think what are you trying to do is not supported for a good reason. The "redirect" directive will issue a GET request to your controller. The GET request should only retrieve existing state using request parameters, this is the method contract. That GET request should not rely on a previous interaction or on any object stored some where in the session as a result of it. GET request is designed to retrieve existing (persisted) state. Your original (POST) request should have persisted everything you need for you GET request to retrieve a state.
RedirectAttributes are not designed to support you in this case, and even if you managed to correctly use it it will only work once and then they will be destroyed. If you then refresh the browser you will get an application error because it cannot find your attributes anymore.

confused about #ModelAttribute annotation in Spring MVC

I have used the #ModelAttribute annotation on a get method in my controller:
#ModelAttribute
public TSetTestEmp get(#RequestParam(required=false) String id) {
TSetTestEmp entity = null;
if (StringUtils.isNotBlank(id)){
entity = tSetTestEmpService.get(id);
}
if (entity == null){
entity = new TSetTestEmp();
}
return entity;
}
and have requested the TSetTestEmp info like this:
#RequiresPermissions("set:emp:tSetTestEmp:list")
#RequestMapping(value = {"list", ""})
public String list(TSetTestEmp tSetTestEmp, HttpServletRequest request, HttpServletResponse response, Model model) {
Page<TSetTestEmp> page = tSetTestEmpService.findPage(new Page<TSetTestEmp>(request, response), tSetTestEmp);
// model.addAttribute("tSetTestEmp", tSetTestEmp); // fixed using this.
model.addAttribute("page", page);
return "modules/set/emp/tSetTestEmpList";
}
As beginning without the comment statement, the JSP code like below throw an exception:
<form:form id="searchForm" modelAttribute="tSetTestEmp" action="${ctx}/set/emp/tSetTestEmp/" method="post" class="form-inline">
<span>Employee Nameļ¼š</span>
<form:input path="empName" htmlEscape="false" maxlength="64" class=" form-control input-sm"/>
</form:form>
Error
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'tSetTestEmp' available as request attribute
According to my debugging, the form:input tag causes this exception. If I use input tag and bind the value empName using ${tSetTestEmp.empName}, this page will show the right result.
If I want to use the form:input tag in my JSP page, the comment in the list method must be removed which means I must assign the Model manually.
#M. Deinum suggested me to fix the problem like this and it works:
#RequiresPermissions("set:emp:tSetTestEmp:list")
#RequestMapping(value = {"list", ""})
public String list(#ModelAttribute("tSetTestEmp") TSetTestEmp tSetTestEmp, HttpServletRequest request, HttpServletResponse response, Model model) {
Page<TSetTestEmp> page = tSetTestEmpService.findPage(new Page<TSetTestEmp>(request, response), tSetTestEmp);
model.addAttribute("page", page);
return "modules/set/emp/tSetTestEmpList";
}
But as I can see, the initial code without #ModelAttribute before method argument can also reach the instance returned by get method. However the JSP page just cannot reach it same as back-end do.
I'm totally confused about how this situation happens.
Thanks for reading till here :)
Any suggestion will help.

Persisting object data between controllers in Spring

I have an object called Request which is the main object of my portal that stores all the information of a request, the user, their form selections, etc. How to I persist all the previous information in between the different forms? In each .GET I have to set the request object, and then in each .POST, the only information that is passed to it is what is in the forms on the .GET pages. So on each page I have to have hidden fields such as
<form:input path='requestId' style='display:none' />
<form:input path='currentUserId' style='display:none' />
<form:input path="step" style='display:none' />
I need these fields, and would also like to have the rest of the fields in the request object that are not on the form without having to repeat that for each and every field in my object.
#RequestMapping(value = "/review", method = RequestMethod.GET)
public ModelAndView showCorReview(#RequestParam(value = "requestId") String requestId,
#CookieValue(value = "EMP_ID", defaultValue = "168") int userId)
{
Request request = requestManager.getRequestById(Integer.parseInt(requestId));
request.setCurrentUserId(userId);
String pageTitle = "2.1: Initiate New Service Request -- (Review)";
ModelAndView mav = new ModelAndView();
mav.setViewName("newRequest/review");
mav.addObject("title", pageTitle);
mav.addObject("request", request);
mav.addObject("cpm", userManager.getUserById(request.getCpm()).getName());
return mav;
}
#RequestMapping(value = "/review", method = RequestMethod.POST)
public String saveReview(Request request, #RequestParam(value = "commentData", required = false) String[] additionalComments)
{
if (additionalComments != null)
commentLogManager.addCommentLog(additionalComments, request);
if (request.getRejectReason() == "")
{
request.setCpm(admin.getCPM(request.getContract()).getId());
request.setCor(admin.getCOR(request.getContract()).getId());
requestManager.updateRequest(request);
}
else
{
if (request.getSubmitType().equals("return"))
{
request.setNextStep(1);
requestManager.moveRequestToStep(request);
}
}
return worksheetUrl + request.getId();
}
Alternatately I could also in the .POST do the
Request request = requestManager.getRequestById(Integer.parseInt(requestId))
Then use setters on all the form fields, but again, I would prefer the data to actually persist on it's own without explicitly calling that.
#Tim, if I understood your requirement correctly, you have a sequence of forms and you would like to transfer information from one form to the next without having to hit a database or copy over request variables from one form to another. I could support #JB Nizel's suggestion of employing HTTP Session, but you may not want to make the session "heavy"; after all, it is the next most persistent scope after application-scope.
Spring Web Flow may be the answer. Flow-scope will allow you to build up form-state as the user progresses from one form to the next. Plus you don't have to worry about form-scoped variables when the flow finishes, unlike session variables that you don't want to have lingering around.

When the validator finds form errors, the form page is redisplayed at the POST url

An item is displayed at this URL:
/item/10101
using this Controller method:
#RequestMapping(value = "/item/{itemId}", method = RequestMethod.GET)
public final String item(HttpServletRequest request, ModelMap model,
#PathVariable long itemId)
{
model = this.fillModel(itemId);
return "item";
}
The page contains a form that submits to the following method in the same controller:
#RequestMapping(value = "/process_form", method = RequestMethod.POST)
public final String processForm(HttpServletRequest request,
#ModelAttribute("foo") FooModel fooModel,
BindingResult bindResult,
ModelMap model)
{
FooModelValidator validator = new FooModelValidator();
validator.validate(FooModel, bindResult);
if (bindResult.hasErrors())
{
model = this.fillModel(fooModel.getItemId());
return "item";
}
return "account";
}
If the validator finds errors in the form, it redisplays the item but instead of displaying it at the original url:
/item/10101
it displays it at its own url:
/process_form
Is it possible to redisplay the form at the original URL?
/item/10101
(I've tried getting the referrer and redirecting to it in processForm but then all of the model contents end up displayed as URL name/value pairs:)
#RequestMapping(value = "/process_form", method = RequestMethod.POST)
public final String processForm(HttpServletRequest request,
#ModelAttribute("foo") FooModel fooModel,
BindingResult bindResult,
ModelMap model)
{
String referrer = request.getHeader("referer");
FooModelValidator validator = new FooModelValidator();
validator.validate(FooModel, bindResult);
if (bindResult.hasErrors())
{
model = this.fillModel(fooModel.getItemId());
return "redirect:" + referrer;
}
return "account";
}
Short answer: No.
What happens is a server-side redirect (forward), which is within the same request, and so the submitted values are preserved (and displayed in the form)
The url will change if you use a client-side redirect (return "redirect:item";), but in that case a new request will come and the submitted values will be lost.
But here are two options that you have:
use the same URL in the mappings for both methods and distinguish them based on request method - GET for the former, POST for the latter. This might be confusing, so document it.
find / implement flash scope for spring-mvc. There's nothing built-in. The flash scope means that values are preserved (in the session usually) for a submit and the subsequent redirect. This option includes the manual handling, by putting the submitted object in the session, and later retrieving & removing it

Categories

Resources