Вопрос

I am trying to redirect to a previous page is a domain object is invalid. For example with a domain object of Address.

@Autowired 
private Address address;
@RequestMapping(method = RequestMethod.GET)
public String addressPage(
  ModelMap model
) throws StdAddFault {
  model.addAttribute("address", address);
  return TILE_GET_STARTED_ADDRESS;
}
@RequestMapping(value = "/validate", method = RequestMethod.POST)
public String selectAddress(
  @Valid Address address,
  BindingResult result,
  Model model,
  HttpSession session
) throws StdAddFault {
  if(result.hasErrors()) {
    model.addAttribute("address", address);
    return "/address";
  }
  ...
}

Problem is after the request instead of being /address it is /address/validate. This seems a little misleading but if i use a redirect or a forward i lose my information.

Is there any way to make the url after the contoller /address (for invalid data only)?

Update

I confirmed the above does not work. This does work but I am concerned one request may overwrite another's Address. Is this a valid concern?

if(result.hasErrors()) {
  this.address = address;
  return "redirect:/get-started/address";
}
Это было полезно?

Решение 4

This is the answer I finally went with

@Controller
@RequestMapping("/address")
public class AddressController {
  private String getMainPage(
    ModelMap model,
    HttpSession session
  ){
    if(!model.containsAttribute("address"))
      model.addAttribute("address", new Address());
...
    return TILE_GET_STARTED_ADDRESS;
}

@RequestMapping(value = URL_ADDRESS_SELECT, method = RequestMethod.POST)
public String validateAddress(
  @Valid Address address,
  BindingResult result,
  Model model,
  RedirectAttributes attr,
  HttpSession session
) throws StdAddFault {
  if(result.hasErrors()) {
    attr.addFlashAttribute("org.springframework.validation.BindingResult.address", result);
    attr.addFlashAttribute("address", address);
    return "redirect:/address";
  }
  ....
}
}

This question + you guys helped me out a bunch!

Другие советы

I think what you're after is an "address" model attribute stored in session. This will retain unique address object per user-session

@Controller
@SessionAttributes({"address"})
@RequestMapping("/address")
public class AddressController {

  /**
   * Injected model reference will contain 'address' if it exists in session
   */
  @RequestMapping(method = RequestMethod.GET)
  public String addressPage(Model model) {
    if(!model.containsAttribute("address"))
      model.addAttribute("address", new Address());
    return "address";
  }

  /**
   * This handler maps to form selecting / updating the address object
   * at the end of this method the address object is automatically
   * stored in session
   */
  @RequestMapping(value = "/validate", method = RequestMethod.POST)
  public String selectAddress(@ModelAttribute("address") Address address) {
    // do stuff with the newly updated address..
  }
}

If in any other handler you need the address object, all you need to do is just inject @ModelAttribute("address") Address address on the argument

I can do it one of 2 ways but I am not sure which one to use..

1.)

if(result.hasErrors()) {
  this.address=address;
  return "redirect:/get-started/address";
}

My concerns here revolve around object lifecycle using the Autowired. Could one call overwrite the address on another?

The other option is to use session...

private String getMainPage(
  ModelMap model,
  HttpSession session
){
  if(session.getAttribute("address") == null){
    session.setAttribute("address", new Address());
  }
  model.addAttribute("mainNav", MAIN_NAV);
  model.addAttribute("subNav", SUB_NAV);
  model.addAttribute("address", session.getAttribute("address"));
  return TILE_GET_STARTED_ADDRESS;
}

I would really like to avoid the session though.

Ok to get a new Object each time make your Controller implement BeanFactoryAware.

Then implement the method setBeanFactory something like this.

private Address address.

public void setBeanFactory(BeanFactory context) {
     address = (Address)context.getBean("myAddress");
  }

So everytime you call setBeanFactory this will get an Address object and because of the scope = prototype it will be a new object.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top