I want to write rest like method for entity update. In this case I retrieve entity id from url and data from request body. The issue is in binding id with bean. Because neither EntityManager nor Spring-Data Crud Repo haven't update(id, bean) method. So I can set it myself
#RequestMapping(value = "/{id}", method = RequestMethod.POST)
public String update(#PathVariable("id") Long id, #Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
user.setId(id); //Very bad
return "usersEdit";
}
user.setId(id); //Bad
repository.save(user);
return "redirect:/users/" + id;
}
or dismiss DRY and put id in forms as private field to.
Is there are any other solutions?
In Spring 3.1 a #ModelAttribute will be instantiated from a path variable if the path variable and the model attribute names are the same and there is a converter to instantiate the model attribute from the path variable value:
#RequestMapping(value="/{account}", method = RequestMethod.PUT)
public String update(#Valid #ModelAttribute Account account, BindingResult result) {
if (result.hasErrors()) {
return "accounts/edit";
}
this.accountManager.saveOrUpdate(account);
return "redirect:../accounts";
}
The full example is available at:
https://github.com/rstoyanchev/spring-mvc-31-demo
Related
I want to inject into the new object a class that contains the user.
#GetMapping
public String listTopic(Principal principal, Model model){
Optional<Users> user = usersService.findByUsername(principal.getName());
if (user.isPresent()){
Topics topic = new Topics();
topic.setUsers(user.get());
model.addAttribute("newTopic", topic);
model.addAttribute("topics", topicsService.listTopics());
return "forum/forum";
}
return "/error";
}
#PostMapping
public String addTopic(#Valid #ModelAttribute("newTopic") Topics topic, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "forum/forum";
}
topicsService.addTopic(topic);
System.out.println(topic);
return "redirect:/forum";
}
When I pass sysout after setting user obect or adding attribute at getmapping section it shows me the exact object, but when I want to see it at the postmapping it throws nullpointerexception.
Your model is a request scope object. After each request it is lost. You need to pass this information to a session object that is alive through different requests in the same session
https://stackoverflow.com/a/18795626/7237884
I have a request mapping for a controller let's say it's A, it receives post action and uses its post values as parameter, sometimes the parameters will be very long so that's the reason why it's POST not GET apart from the best practices and security;
RequestMapping(value = "/reports/performance/indicator/{indicatorType}", method = RequestMethod.POST)
public String generatePerformanceReportsIndicator(ModelMap map,HttpServletResponse response, #PathVariable("indicatorType") Long indicatorType,
#RequestParam(value = "siteIds", required = false) List<Long> siteIds,
#RequestParam(value = "timeframeIds", required = false) List<String> timeframeIds,
#RequestParam(value = "showTarget", required = false) String showTarget,Locale locale) {
And then it turned out that in another spring controller I need to forward the request to the first one.
The problem is how I can add post parameters to the request before forwarding it to the first request mapping? is that healthy to say for example?
new FirstController().generatePerformanceReportsIndicator(....);
Given that:
I don't want the first request mapping to use get instead of post of the mentioned reasons.
I don't want to write redundant code by creating another method that extract the parameters as attribute from the model map.
You should not manually call other controllers! What you can do is redirect to them with RedirectAttributes like:
#RequestMapping(value = "/doctor/doEditPatientDetails", method = RequestMethod.POST)
public String editPatientDetails(Model model, #ModelAttribute(value = "user") #Valid User user,
BindingResult result, RedirectAttributes attr, Principal principal) {
if (null != principal) {
if (result.hasErrors()) {
attr.addFlashAttribute("org.springframework.validation.BindingResult.user", result);
attr.addFlashAttribute("user", user);
attr.addAttribute("id", user.getId());
return "redirect:/doctor/editPatient/{id}";
}
}
....
return "redirect:/doctor/patients";
}
#RequestMapping(value = "/doctor/editPatient/{id}", method = RequestMethod.GET)
public String showEditPatient(Model model, #ModelAttribute("id") String id, Principal principal) {
if (null != principal) {
//here you can access the model and do what everything you want with the params.
if (!model.containsAttribute("user")) {
model.addAttribute("user", user);
}
....
}
return "/doctor/editPatient";
}
Note that, to redirect to a link like "redirect:/doctor/editPatient/{id}" you have to add the id in RedirectAttributes. Also not that there are many ways you can achive the same functionality like HttpServletRequest
For inject #PathVariable entity I mean
#RequestMapping(method = RequestMethod.PUT, path = "{project-id}")
#Transactional
#PreAuthorize("#user.id == #project.userId")
public Object update(#P("user") #Current User user,
#P("project") #PathVariable Project entity,
#RequestBody #Valid ProjectPost request) {
setProjectPostToEntity(entity, request);
return ResponseEntity.ok(ImmutableMap.of("message", "Project update successful"));
}
But the #PathVariable Project entity will be null if project-id not found in repository, what I want is something like this
#RequestMapping(method = RequestMethod.PUT, path = "{project-id}")
#Transactional
#PreAuthorize("#user.id == #project.userId")
public Object update(#P("user") #Current User user,
#P("project") #PathVariable #Valid #NotNull(message="Update project not exists") Project entity,
#RequestBody #Valid ProjectPost request) {
setProjectPostToEntity(entity, request);
return ResponseEntity.ok(ImmutableMap.of("message", "Project update successful"));
}
If project-id not found in repository will return a message Update project not exists, but #Valid #NotNull(message="Update project not exists") not works here, how can I do something like this ?
Create service with method setProjectPostToEntity(entity, request) and annotate it with #Transactional and #PreAuthorize. That approach garantee you that your variables will exists.
And about #Transactional on controller methods: https://stackoverflow.com/a/23120647/5649869
I have a requirement where the user selects some data from a form and we need to show that selected data on the next page.
At present we are doing this with a session attribute, but the problem with this is that it overwrites the data if the first page is open in another browser tab, where the data is again selected and submitted. So I just want to get rid of this session attribute while transferring data from one controller to another.
Note: I am using an XML based Spring configuration, so please show a solution using XML, not annotations.
Define RedirectAttributes method parameter in the the handler method that handles form submission from first page:
#RequestMapping("/sendDataToNextPage", method = RequestMethod.POST)
public String submitForm(
#ModelAttribute("formBackingObj") #Valid FormBackingObj formBackingObj,
BindingResult result,
RedirectAttributes redirectAttributes) {
...
DataObject data = new DataObject();
redirectAttributes.addFlashAttribute("dataForNextPage", data);
...
return "redirect:/secondPageURL";
}
The flash attributes are saved temporarily before the redirect (typically in the session) and are available to the request after the redirect and removed immediately.
The above redirect will cause the client (browser) to send a request to /secondPageURL. So you need to have a handler method to handle this request, and there you can get access to the DataObject data set in the submitForm handler method:
#RequestMapping(value = "/secondPageURL", method = RequestMethod.GET)
public String gotoCountrySavePage(
#ModelAttribute("dataForNextPage") DataObject data,
ModelMap model) {
...
//data is the DataObject that was set to redirectAttributes in submitForm method
return "pageToBeShown";
}
Here DataObject data is the object that contains data from the submitForm method.
I worked with this requirement and I used RedirectAttributes, then you can add this redirect attributes to your model. This is an example:
#RequestMapping(value = "/mypath/{myProperty}", method = RequestMethod.POST)
public String submitMyForm(#PathVariable Long myProperty, RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("message", "My property is: " + myProperty);
return "redirect:/anotherPage";
}
#RequestMapping(method = RequestMethod.GET)
public String defaultPage(Model model, #RequestParam(required = false) String message) {
if(StringUtils.isNotBlank(message)) {
model.addAttribute("message", message);
}
return "myPage";
}
Hope it helps.
You can use RedirectAttributes ; A specialization of the Model interface that controllers can use to select attributes for a redirect scenario.
public interface RedirectAttributes extends org.springframework.ui.Model
Plus this interface also provide a way to store "Flash Attribute" . Flash Attribute is in FlashMap.
FlashMap : A FlashMap provides a way for one request to store attributes intended for use in another. This is most commonly needed when redirecting from one URL to another.
Quick Example is
#RequestMapping(value = "/accounts", method = RequestMethod.POST)
public String handle(RedirectAttributes redirectAttrs) {
// Save account ...
redirectAttrs.addFlashAttribute("message", "Hello World");
return "redirect:/testUrl/{id}";
}
Reference and detail information are here
At the moment I just want to test redirecting from the update method back to the sox method. But instead I get an error complaining about a missing "update.jsp".
#RequestMapping(value = "/sox/update", method = RequestMethod.POST)
#ModelAttribute("formWrapper")
public final String update(HttpServletRequest request,
#ModelAttribute("formWrapper") FormWrapper formWrapper,
BindingResult bindResult,
ModelMap model)
{
return "redirect:/sox";
}
#ModelAttribute("formWrapper")
FormWrapper setupForm()
{
FormWrapper formWrapper = new FormWrapper();
return formWrapper;
}
#RequestMapping(value = "/sox", method = RequestMethod.GET)
public final String sox(HttpServletRequest request, ModelMap model)
{
return "sox";
}
I think your problem is the #ModelAttribute on the update method. Try it this way :
#RequestMapping(value = "/sox/update", method = RequestMethod.POST)
public final String update(HttpServletRequest request,
#ModelAttribute("formWrapper") FormWrapper formWrapper,
BindingResult bindResult,
ModelMap model)
{
return "redirect:/sox";
}
When you add the #ModelAttribute to a method spring treats the return as a model attribute :
Any other return type is considered to
be a single model attribute to be
exposed to the view, using the
attribute name specified through
#ModelAttribute at the method level
(or the default attribute name based
on the return type class name). The
model is implicitly enriched with
command objects and the results of
#ModelAttribute annotated reference
data accessor methods.
Look at 15.3.2.8 of the Spring Docs for more info: (http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-modelattrib)