LoginFormController-->Post is invoked after the form is submitted/posted. At the end, it invokes another Controller called LandingFormController-->loadForm.
Well, in the loadForm the values in the Model seems to be empty. Is there a way I can persist a Bean in session or request and get it in the loadForm method? Bonus points: if you could point to some documents to refer :)
Thanks
#Controller
#RequestMapping(value="/login")
public class LoginFormController {
#RequestMapping(method=RequestMethod.POST)
public ModelAndView post(#ModelAttribute User user, BindingResult result, SessionStatus status) {
logger.info("post");
new ReceiptUserValidator().validate(user, result);
if (result.hasErrors()) {
return new ModelAndView("login");
}
else {
logger.info("Email Id: " + user.getEmailId());
//status.setComplete();
Map<String, Object> model = new HashMap<String, Object>();
model.put("userId", user.getEmailId());
model.put("now", new Date().toString());
return new ModelAndView("redirect:/landing.htm", "model", model);
}
}
Controller B below that gets called
#Controller
#RequestMapping(value="/landing")
public class LandingFormController {
protected final Log logger = LogFactory.getLog(getClass());
#RequestMapping(method=RequestMethod.GET)
public String loadForm(Model model) {
logger.info("LandingFormController loadForm: " + model.asMap().keySet());
return "landing";
}
}
The code is performing a redirect which causes the properties placed in the model to be lost. Use flash attributes to pass the attributes to the next controller.
Flash attributes provide a way for one request to store attributes
intended for use in another. This is most commonly needed when
redirecting — for example, the Post/Redirect/Get pattern. Flash
attributes are saved temporarily before the redirect (typically in the
session) to be made available to the request after the redirect and
removed immediately.
LoginFormController
#Controller
#RequestMapping(value="/login")
public class LoginFormController {
#RequestMapping(method=RequestMethod.POST)
public ModelAndView post(#ModelAttribute User user, BindingResult result,
SessionStatus status, final RedirectAttributes redirectAttrs) {
logger.info("post");
new ReceiptUserValidator().validate(user, result);
if (result.hasErrors()) {
return new ModelAndView("login");
}
else {
logger.info("Email Id: " + user.getEmailId());
//status.setComplete();
redirectAttrs.addFlashAttribute("userId", user.getEmailId());
redirectAttrs.addFlashAttribute("now", new Date().toString());
return new ModelAndView("redirect:/landing.htm", "model", model);
}
}
Documentation
As an alternative solution you could simply not perform a redirect from the controller.
Appending retrieving solution.
Modify the loadForm
public String loadForm(#ModelAttribute("user") User user) {
logger.info("LandingFormController loadForm: " + user.getEmailId());
return "landing";
}
Related
In a Spring-Boot controller during user registration if there is a binding error my controller returns the user to the registration page but in my implementation the model seems to be missing.
For example I commonly see on tutorials
#PostMapping
public String registerUserAccount(#ModelAttribute("user") #Valid UserRegistrationDto userDto,
BindingResult result){
User existing = userService.findByEmail(userDto.getEmail());
if (existing != null){
result.rejectValue("email", null, "There is already an account registered with that email");
}
if (result.hasErrors()){
return "registration";
}
userService.save(userDto);
return "redirect:/registration?success";
}
Which, if there are binding errors returns "registration"
My controller is very similar:
#PostMapping("/user/register")
public String registerNewUser(#ModelAttribute("user") #Valid RegisterUserDTO registerUserDTO,
BindingResult bindingResult,
Model model,
HttpServletRequest request,
Errors errors) {
User existing = userService.findUserByEmail(registerUserDTO.getEmail());
if (existing != null) {
bindingResult.rejectValue("email", null, "There is already an account with that Email");
}
if (bindingResult.hasErrors()) {
return "register";
}
userService.createUser(registerUserDTO);
return "redirect:/registration?success";
}
}
However when I return "register" there is a binding error on the stack at what corresponds to my first thymeleaf tag relating to the object.
If I change the POST controller to add the model explicitly:
if (bindingResult.hasErrors()) {
model.addAttribute(registerUserDTO);
return "register";
}
Then it works, returning the page with the model and previously entered data.
Why am I having to explicitly add the model to the return?
UPDATE
This does not work either:
#PostMapping("/user/register")
public String registerNewUser(#ModelAttribute("user") #Valid RegisterUserDTO registerUserDTO,
BindingResult bindingResult) {
User existing = userService.findUserByEmail(registerUserDTO.getEmail());
if (existing != null) {
bindingResult.rejectValue("email", null, "There is already an account with that Email");
}
if (bindingResult.hasErrors()) {
return "register";
This worked:
#PostMapping("/user/register")
public String registerNewUser(#ModelAttribute("registerUserDTO") #Valid RegisterUserDTO registerUserDTO,
BindingResult bindingResult) {
User existing = userService.findUserByEmail(registerUserDTO.getEmail());
if (existing != null) {
bindingResult.rejectValue("email", null, "There is already an account with that Email");
}
if (bindingResult.hasErrors()) {
return "register";
}
userService.createUser(registerUserDTO);
return "redirect:/registration?success";
}
As M.Denium pointed out the #ModelAttribute name needed to be the same as the instance name of the backing object used in the GET controller. In my GET controller I had :
#GetMapping("/user/register")
String registerForm(Model model) {
RegisterUserDTO registerUserDTO = new RegisterUserDTO();
model.addAttribute(registerUserDTO);
return "register";
}
So the #ModelAttribute needs to match, ie #ModelAttribute("registerUserDTO")
I have two different Spring applications (app-one, app-two), app-one should receive a response and then redirect to app-two with some parameters. So, I have the following REST controller in app-one:
#RestController
public class RedirectController
{
#GetMapping(value = "/redirect")
public ResponseEntity<Void> redirectEndpoint(HttpServletRequest request,
RedirectAttributes redirectAttributes)
{
// Do some business logic
// Set parameters
redirectAttributes.addAttribute("attribute", "Value 1");
redirectAttributes.addFlashAttribute("flashAttribute", "Value 2");
// Redirect to success URL
String redirectURL = "http://app-two/success";
HttpHeaders headers = new HttpHeaders();
headers.setLocation(URI.create(redirectURL));
return ResponseEntity.status(HttpStatus.FOUND).headers(headers).build();
}
}
And the following REST controller in app-two:
#RestController
public class SuccessController
{
#GetMapping(value = "/success")
public ResponseEntity<Void> successEndpoint(HttpServletRequest request, Model model,
#RequestParam(value = "attribute", required = false) String attribute,
#ModelAttribute(value = "flashAttribute") String flashAttribute)
{
// Get parameters
System.out.println("attribute: " + attribute);
System.out.println("flashAttribute: " + flashAttribute);
String flashAttributeFromModelMap = (String) model.asMap().get("flashAttribute");
System.out.println("flashAttributeFromModelMap: " + flashAttributeFromModelMap);
Map<String, ?> flashMap = RequestContextUtils.getInputFlashMap(request);
if (flashMap != null)
{
String flashAttributeFromFlashMap = (String) flashMap.get("flashAttribute");
System.out.println("flashAttributeFromFlashMap: " + flashAttributeFromFlashMap);
}
// Do some business logic
return ResponseEntity.status(HttpStatus.OK).build();
}
}
I was able to redirect successfully by returning FOUND (302). But when adding attributes to RedirectAttributes (in this case attribute and flashAttribute), these attributes are not found after the redirection done (attribute gets null and flashAttribute gets empty).
I tried to get the attributes values by different ways (#RequestParam, #ModelAttribute, model.asMap().get(), and RequestContextUtils.getInputFlashMap(request).get()) but none of them gets the correct value.
What I need is to get the correct attributes' values in successEndpoint. Any suggestions on how to accomplish that?
Thanks in advance.
Using Springboot to build a project. You can use the configuration to solve the problem when you don't use Springboot to build the project, but now I don't know how to configure it.
#RestController
public class RestShowNameController {
#RequestMapping("/myself")
public ModelAndView index() {
return new ModelAndView("index");
}
#RequestMapping("/object")
public User object() {
User user = new User();
user.setName("绿秋");
user.setComment("系统管理员用户");
return user;
}
}
This is IE's property to prompt for opening the JSON file. If you will try this in other browsers (like chrome) it will directly display the results on screen.
If you want to change the method to display on screen for IE also. One way can be to change the method to return String, as
you are returning an object as response and its getting converted to JSON by default:
#RestController
public class RestShowNameController {
#RequestMapping("/myself")
public ModelAndView index() {
return new ModelAndView("index");
}
#RequestMapping("/object")
#ResponseBody
public String object() {
User user = new User();
user.setName("绿秋");
user.setComment("系统管理员用户");
return user.toString();
}
}
In User class override the toString() method and format the result.
#Override
public String toString() {
return "User [name =" + name + ", comment =" + comment + "]";
}
I have this POST method which only validates a form and returns a confirmation view if the form is validated and I want to send back to the register screen if any field is wrong. In this case if the BindingResult object has errors, the system send the user back to the form screen but the URL shown is "/registerConfirmation" which should only be in case the form has no errors.
#RequestMapping(value="/registerConfirmation", method = RequestMethod.POST)
public ModelAndView confirmRegister(#Valid #ModelAttribute("form") RegistrationForm form, BindingResult result){
logger.info("Sending registration data");
ModelAndView modelAndView = new ModelAndView();
if(result.hasErrors()){
modelAndView.setViewName("register");
modelAndView.addObject("form", form);
return modelAndView;
}
//more code here
return modelAndView;
}
I dont know what I'm missing as I have seen methods like this in many other posts. Any help??
Many thanks!!!
One way to solve your issue is to use redirect:
#RequestMapping(value="/registerConfirmation", method = RequestMethod.POST)
public String confirmRegister(#Valid #ModelAttribute("form") RegistrationForm form, BindingResult result, RedirectAttributes attr){
logger.info("Sending registration data");
if(result.hasErrors()){
attr.addFlashAttribute("org.springframework.validation.BindingResult.form", result);
attr.addFlashAttribute("form", form);
return "redirect:/register";
}
//more code here
return "redirect:/registerConfirmation";
}
and in your register GET method you should check:
#RequestMapping(value="/register", method = RequestMethod.GET)
public String showRegister(Model model) {
....
if (!model.containsAttribute("form")) {
model.addAttribute("form", new RegistrationForm());
}
.....
}
you can read more in this article
Also don't forget to create GET method with registerConfirmation value.
I am developing an application using Spring. I have a trouble about login and logout. I logged in application using a login credentials(e.g. userName:john, pass: doe) and go to Admin page and than I logged out from application. But this time I used different login credentials(e.g. userName: jack, pass: white) for login. When I go to Admin page and debug my application #ModelAttribute(value = "myUser") User loggedInUser at AdminController shows old user value. I couldn't understand why this occurs. Anyone can help?
My source codes are below:
#Controller
#RequestMapping("/LoginController")
#SessionAttributes({"myUser"})
public class LoginController
{
private static String LOGIN_URL = "login/login_";
private static String INDEX_URL = "main/index";
#Autowired
private IUserService userService = null;
#RequestMapping("/login")
public ModelAndView login(#RequestParam(value="userName", required=false) String argUserName, #RequestParam(value="password", required=false) String argPassword, HttpServletRequest req)
{
ModelAndView modelAndView = new ModelAndView();
// Assume argUserName and argPassword not null
User loginUser = this.userService.getUser(argUserName, argPassword);
HttpSession ses = req.getSession();
// Assume loginUser not null
ses.setAttribute("myUser", loginUser);
modelAndView.setViewName(LoginController.INDEX_URL);
return modelAndView;
}
#RequestMapping("/logout")
public String logout(HttpServletRequest argReq, HttpServletResponse argResp) throws ServletException, IOException
{
HttpSession session = argReq.getSession(false);
Enumeration<?> attributeNames = session.getAttributeNames();
while(attributeNames.hasMoreElements())
{
String attrName = (String)attributeNames.nextElement();
if(session.getAttribute(attrName) != null)
{
session.setAttribute(attrName,null);
//session.removeAttribute(attrName);
attributeNames = session.getAttributeNames();
}
}
// close session
session.invalidate();
return LoginController.LOGIN_URL;
}
}
AdminController
#Controller
#RequestMapping("/AdminController")
#SessionAttributes({"myUser"})
public class AdminController
{
private static String SETTINGS_PAGE = "settings/index";
#RequestMapping("/index")
public ModelAndView index(#ModelAttribute(value = "myUser") User loggedInUser, HttpSession ses)
{
ModelAndView modelAndView = new ModelAndView();
Map<String, Object> map = new HashMap<String, Object>();
map.put("loggedInUserId", loggedInUser.getUserID());
map.put("userName", loggedInUser.getUserName());
modelAndView.addAllObjects(map);
modelAndView.setViewName(AdminController.SETTINGS_PAGE);
return modelAndView;
}
}
Remove this annotation
#SessionAttributes({"myUser"})
For starters #SessionAttributes isn't designed to store data in the session between different controllers. Its intended use is only to store data for the same controller in between requests. If you want to store items in the session between requests store them in the session yourself and don't rely on #SessionAttributes. This is also mentioned in the javadoc of the annotation (although a bit cryptic maybe).
If you want to remove object cached by #SessionAttributes you cannot simply clear the session but you would have to use the SessionStatus object (which you can add as an argument) to mark the use of these objects complete.
Your logout method is way to verbose, simply calling session.invalidate() should be enough, but I guess this was one of your attempts to fix things. Also when you are on a Servlet 3.0 container simply calling request.logout() could be enough (or call it in conjunction with session.invalidate())
My final advice would be to use Spring Security instead of trying to develop your own security solution.