I have my controller-A class like this:
#PostMapping("/otp")
public String otpSubmit(#RequestParam("token") String token, HttpSession session, Model model) throws IOException {
Long enrollment = (Long) session.getAttribute("enrollment");
BaseResponse otpResponse = otpRestClient.validateOTP(enrollment, token);
if(otpResponse.getCode().equals("1020")) {
model.addAttribute("object", otpResponse.getPayload());
return "redirect:/password";
}
model.addAttribute("errorCode", otpResponse.getCode());
model.addAttribute("errorMessage", otpResponse.getMessage());
return "/otp";
}
What I want is simple (I think) pass the model.addAttribute("object", otpResponse.getPayload()); to controller-B class so I can access that data in the other view.
How can I inject this into controller-B class?.
By adding redirectAttributes we can pass model data
Here is the Controller one.
public String controlMapping1(
#ModelAttribute("mapping1Form") final Model model,
final RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("mapping1Form", model);
return "redirect:mapping2";
}
Here is Controller2
public String controlMapping2(
#ModelAttribute("mapping1Form") final Model model) {
model.addAttribute("transformationForm", model);
return "view_name";
}
you can save this "Object o = otpResponse.getPayload()" object in a global variable so later you can access it from any controller.
Related
I'm learning Spring MVC and while in process I came across this problem:
// http://localhost:8080/todo-list/welcomeWithParam?user=Stefan
#GetMapping("welcomeWithParam")
public String welcome91(#RequestParam String user, Model model) {
model.addAttribute("helloThroughParam", demoService.getHelloMessage(user));
return "welcome-with-model";
}
// http://localhost:8080/todo-list/welcomeWithParam?user=Stefan&age=31
#GetMapping("welcomeWithParam")
public String welcome92(#RequestParam String user, #RequestParam int age, Model model) {
model.addAttribute("helloThroughParam", demoService.getHelloMessage(user));
model.addAttribute("age", age);
return "welcome-with-model";
}
I'm getting this error:
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'demoController' method
academy.learnprogramming.controller.DemoController#welcome92(String, int, Model)
to {GET [/welcomeWithParam]}: There is already 'demoController' bean method
academy.learnprogramming.controller.DemoController#welcome91(String, Model) mapped.
So, Spring is telling me we cant have two GET mappings with same URL but different number/type of parameters?
If I change value of one of #GetMapping("") values, it works fine.
Something like this:
#RequestMapping(value = "/welcomeWithParam", params = "user")
public String welcome91(#RequestParam String user, Model model) {
// ...
}
#RequestMapping(value = "/welcomeWithParam", params = {"user","age"})
public ModelAndView welcome92(#RequestParam String user, #RequestParam int age, Model model) {
// ...
}
In this example I am trying to redirect from handleSaveContact() controller method from contactSuccessMsg() controller method but after transfer I need to display success or update or failure msg to the UI which is only possible if I transfer Model data from 1st method to 2nd.
Could any one please suggest me how I can trasfer model data from one controller method to another controller method.
#GetMapping(value={"/", "/loadForm"})
public String loadContactForm(Model model) {
model.addAttribute("contact", new Contact());
return "index";
}
#PostMapping("/saveContact")
public String handleSaveContact(Contact contact, Model model) {
String msgTxt = null;
if(contact.getContactId()==null) {
msgTxt = "Contact Saved Successfully..!!";
}else {
msgTxt = "Contact Updated Successfully..!!";
}
contact.setIsActive("Y");
boolean isSaved = contactService.saveContact(contact);
if(isSaved) {
model.addAttribute("successMsg", msgTxt);
}else {
model.addAttribute("errorMsg", "Failed To Save Contact..!!");
}
return "redirect:/contactSuccessMsg";
}
/**
* To resolve Double Posting problem, redirecting the post req method to get request.
* #param contact
* #param model
* #return
*/
#GetMapping(value="/contactSuccessMsg")
public String contactSuccessMsg(Model model) {
model.addAttribute("contact", new Contact());
return "index";
}
I used Spring 3.2.3
1.)Added RedirectAttributes redirectAttributes to the method parameter list in controller1.
public String controlMapping1(
#ModelAttribute("mapping1Form") final Object mapping1FormObject,
final BindingResult mapping1BindingResult,
final Model model,
final RedirectAttributes redirectAttributes)
Inside the method added code to add flash attribute to redirectAttributes
redirectAttributes.addFlashAttribute("mapping1Form", mapping1FormObject);
Then, in the second contoller use method parameter annotated with #ModelAttribute to access redirect Attributes :
#ModelAttribute("mapping1Form") final Object mapping1FormObject
Here is the sample code from Controller 1:
#RequestMapping(value = { "/mapping1" }, method = RequestMethod.POST)
public String controlMapping1(
#ModelAttribute("mapping1Form") final Object mapping1FormObject,
final BindingResult mapping1BindingResult,
final Model model,
final RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("mapping1Form", mapping1FormObject);
return "redirect:mapping2";
}
From Contoller 2:
#RequestMapping(value = "/mapping2", method = RequestMethod.GET)
public String controlMapping2(
#ModelAttribute("mapping1Form") final Object mapping1FormObject,
final BindingResult mapping1BindingResult,
final Model model) {
model.addAttribute("transformationForm", mapping1FormObject);
return "new/view";
}
i have to use one form for two modelAttributes : "groupes" which is a list of Groupe and "matieres" is a list of Matiere, i know that one form support only one modelAttribute , i tried two options but both doesn't work,one by using spring bind tag and the other one by wrapping groupes and matieres in one class,
Any ideas how to solve this ?
#RequestMapping(method = RequestMethod.GET)
public ModelAndView showForm() {
ModelAndView mv = new ModelAndView("abs");
mv.addObject("matiere", matiereservice.findAlmatieres());
mv.addObject("groupe", groupeservice.findAllGroupes());
return mv;
}
#RequestMapping( method = RequestMethod.POST)
public String submitForm( #ModelAttribute("groupe") Groupe groupe, #ModelAttribute("matiere") Matiere matiere,
ModelMap map,
BindingResult result
) {
// BindingResult treatment
return "listeleve" ;
}
Instead of sending those attributes separately, simply make a DTO class which contains List fields of both of your Groupe and Matiere classes.
public class ListeleveDTO {
private List<Groupe> groupe;
private List<Matiere> matiere;
public ListeleveDTO(List<Groupe> groupe, List<Matiere> matiere) {
// Assign to fields
}
// getters and setters
}
And set the instance of this class as a model attribute.
#RequestMapping(method = RequestMethod.GET)
public ModelAndView showForm() {
ModelAndView mv = new ModelAndView("abs");
mv.addObject("listeleveDTO", new ListeleveDTO(groupeservice.findAllGroupes(), matiereservice.findAlmatieres());
return mv;
}
#RequestMapping( method = RequestMethod.POST)
public String submitForm( #ModelAttribute("listeleveDTO") ListeleveDTO,
ModelMap map,
BindingResult result
) {
// BindingResult treatment
return "listeleve" ;
}
I have next working code in my SpringMVC controller:
#RequestMapping(value = "/register", method = RequestMethod.GET)
public void registerForm(Model model) {
model.addAttribute("registerInfo", new UserRegistrationForm());
}
#RequestMapping(value = "/reg", method = RequestMethod.POST)
public String create(
#Valid #ModelAttribute("registerInfo") UserRegistrationForm userRegistrationForm,
BindingResult result) {
if (result.hasErrors()) {
return "register";
}
userService.addUser(userRegistrationForm);
return "redirect:/";
}
In short create method try to validate UserRegistrationForm. If form has errors, it leaves user on the same page with filled form fields where error message will be shown.
Now I need to apply the same behaviour to another page, but here I have a problem:
#RequestMapping(value = "/buy/{buyId}", method = RequestMethod.GET)
public String buyGet(HttpServletRequest request, Model model, #PathVariable long buyId) {
model.addAttribute("buyForm", new BuyForm());
return "/buy";
}
#RequestMapping(value = "/buy/{buyId}", method = RequestMethod.POST)
public String buyPost(#PathVariable long buyId,
#Valid #ModelAttribute("buyForm") BuyForm buyForm,
BindingResult result) {
if (result.hasErrors()) {
return "/buy/" + buyId;
}
buyForm.setId(buyId);
buyService.buy(buyForm);
return "redirect:/show/" + buyId;
}
I faced with issue of dynamic url. Now if form has errors I should specify the same page template to stay on current page, but also I should pass buyId as a path variable. Where are a conflict in this two requirements. If I leave this code as is, I get an error (I'm using Thymeleaf as a template processor):
Error resolving template "/buy/3", template might not exist or might not be accessible by any of the configured Template Resolvers
I can write something like return "redirect:/buy/" + buyId, but in this case I lose all data and errors of form object.
What should I do to implement in buyPost method the same behaviour as in create method?
I tried the solution metioned in this post at this weekend, but it doesn't work for BindingResult.
The code below works but not perfect.
#ModelAttribute("command")
public PlaceOrderCommand command() {
return new PlaceOrderCommand();
}
#RequestMapping(value = "/placeOrder", method = RequestMethod.GET)
public String placeOrder(
#ModelAttribute("command") PlaceOrderCommand command,
ModelMap modelMap) {
modelMap.put(BindingResult.MODEL_KEY_PREFIX + "command",
modelMap.get("errors"));
return "placeOrder";
}
#RequestMapping(value = "/placeOrder", method = RequestMethod.POST)
public String placeOrder(
#Valid #ModelAttribute("command") PlaceOrderCommand command,
final BindingResult bindingResult, Model model,
final RedirectAttributes redirectAttributes) {
if (bindingResult.hasErrors()) {
redirectAttributes.addFlashAttribute("errors", bindingResult);
//it doesn't work when passing this
//redirectAttributes.addFlashAttribute(BindingResult.MODEL_KEY_PREFIX + "command", bindingResult);
redirectAttributes.addFlashAttribute("command", command);
return "redirect:/booking/placeOrder";
}
......
}
*I'm using Hibernate Validator APIs to validate my beans. To preserve form data along with displaying error messages, you need to do these 3 things:
Annotate your bean (eg. #NotEmpty, #Pattern, #Length, #Email etc.)
Inside controller:
#Controller
public class RegistrationController {
#Autowired
private RegistrationService registrationService;
#RequestMapping(value="register.htm", method=RequestMethod.GET, params="new")
public String showRegistrationForm(Model model) {
if (!model.containsAttribute("employee")) {
model.addAttribute("employee", new Employee());
}
return "form/registration";
}
#RequestMapping(value="register.htm", method=RequestMethod.POST)
public String register(#Valid #ModelAttribute("employee") Employee employee, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
if (bindingResult.hasErrors()) {
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.employee", bindingResult);
redirectAttributes.addFlashAttribute("employee", employee);
return "redirect:register.htm?new";
}
registrationService.save(employee);
return "workspace";
}
// ....
}
Update your view/jsp to hold error messages:
This article can surely be helpful.
You can change your POST implementation to this:
#RequestMapping(value = "/buy/{buyId}", method = RequestMethod.POST)
public String buyPost(#PathVariable long buyId,
#Valid #ModelAttribute("buyForm") BuyForm buyForm,
BindingResult result) {
buyForm.setId(buyId); // important to do this also in the error case, otherwise,
// if the validation fails multiple times it will not work.
if (result.hasErrors()) {
byForm.setId(buyId);
return "/buy/{buyId}";
}
buyService.buy(buyForm);
return "redirect:/show/{buyId}";
}
Optionally, you can also annotate the method with #PostMapping("/buy/{buyId}") if you use Spring 4.3 or higher.
I am reading a book about spring and in the chapter about spring mvc the author list the following controller code that is responsible for form submission.
My question (since the author is not referring to it is why and where we should use HttpServletRequest)
Here is the method :
#RequestMapping(value = "/{id}", params = "form", method = RequestMethod.POST)
public String update(#Valid Contact contact, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest, RedirectAttributes redirectAttributes, Locale locale)
{
logger.info("Updating contact");
if (bindingResult.hasErrors())
{
uiModel.addAttribute("message", new Message("error", messageSource.getMessage("contact_save_fail", new Object[]{}, locale)));
uiModel.addAttribute("contact", contact);
return "contacts/update";
}
uiModel.asMap().clear();
redirectAttributes.addFlashAttribute("message", new Message("success", messageSource.getMessage("contact_save_success", new Object[]{}, locale)));
contactService.save(contact);
return "redirect:/contacts/" + UrlUtil.encodeUrlPathSegment(contact.getId().toString(), httpServletRequest);
}
Use it whenever you need to use it...
In this example, the author is using it get the character encoding :
return "redirect:/contacts/" + UrlUtil.encodeUrlPathSegment(contact.getId().toString(), httpServletRequest);
Here is the code from the UrlUtil class :
public class UrlUtil {
public static String encodeUrlPathSegment(String pathSegment, HttpServletRequest
httpServletRequest) {
String enc = httpServletRequest.getCharacterEncoding();
if (enc == null) {
enc = WebUtils.DEFAULT_CHARACTER_ENCODING;
}
try {
pathSegment = UriUtils.encodePathSegment(pathSegment, enc);
} catch (UnsupportedEncodingException uee) {
}
return pathSegment;
}
}
More information about the HttpServletRequest class :
It extends the ServletRequest interface to provide request information for HTTP servlets. You might consider reading the javadoc if you want to learn more about the methods of the class.