How can I force Spring DATA REST #PathVariable entity exists - java

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

Related

spring rest #RequestBody does not validate with #Valid

I'm learning java and spring boot and I am trying to validate a controller parameter which was bound from json.
I've got simple Entity:
#Getter
#Setter
class Resource {
#NotBlank
String uri;
}
Which I want to persist through the following controller:
#BasePathAwareController
public class JavaResourcePostController {
private final ResourceRepository repository;
public JavaResourcePostController(ResourceRepository repository) {
this.repository = repository;
}
#RequestMapping(value = "/resources", method = RequestMethod.POST)
ResponseEntity<Resource> create(
#Valid #RequestBody Resource resource
) {
repository.save(resource);
return ResponseEntity.ok(resource);
}
}
My understanding is that the resource argument should be valid when entering the method. But posting an empty uri field does not trigger the validation of the method. it does however get triggered in the hibernate validation in repository.save()
Why does the #Valid annotation on the argument not ensure I get a validated entity?
You need to add #Validated to your controller class.

Using #BindingResult with #Validated

Note:
I read almost all stack post that i get when i search #valid vs #validated and could not find answer so i am posting this.
I am so confused what #Validated is doing here. If inputParms is not valid, it is throwing javax.validation.ConstraintViolationException and not even going inside code. But if I replace #Validated with #Valid No exception thrown and bindingResult.hasErrors() is catching it.
#RestController
#Validated // what is this doing ??
public class MyRestController{
#PostMapping(value="/my-data",produces= {MediaType.APPLICATION_JSON_UTF8_VALUE})
public ResponseEntity<?> doSomething(#Valid #RequestBody MyInputParam inputParms,BindingResult bindingResult){
if(bindingResult.hasErrors()) {
//do something here
}
}
}
So if i use #Validated, BindingResult is not useful at all ?
Or even simply how #Validated is different from #Valid
See Difference between #Valid and #Validated in Spring.
The problem in your particular case is that #Validated is mixed with #Valid. Options to fix this:
remove the #Validated declared on the class and use either #Validated or #Valid for the parameter
use either #Valid or #Validated in both places (class and parameter)
#RestController
public class MyRestController {
#PostMapping(value = "/my-data", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
public ResponseEntity<?> doSomething(#Validated #RequestBody MyInputParam inputParms,
BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
//do something here
}
}
}

java.lang.annotation.AnnotationFormatError is happened when i use validation in spring mvc

When I'm using validation I get these errors. It finds the errors but when I return to the same jsp page again it throws these exception, otherwise it works. Any suggestions would be appreciated.
This is happening because you may have defined groups and payload as #NotNull annotation in your entity.
And your form submission is not submitting any value for that so it is going null and because of that your are getting these errors.
Better to define BindingResult and control on errors like below.
#RequestMapping(method = RequestMethod.POST, produces = "text/html")
public String create(#Valid EntityPojo entity, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
if (bindingResult.hasErrors()) {
return getPath() + "/update"; //here you return the same page with errors
}
//here you proceed further if there is no error
}

InitBinder for method

I have a rest controller:
#RestController
public abstract class CrudController {
#RequestMapping(
path = "delete",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON,
consumes = MediaType.APPLICATION_JSON)
public ResponseEntity<ResponseDTO<Void>> delete(
#RequestBody IdDTO request,
#RequestHeader("X-auth-token") String token,
BindingResult bindingResult
) {
//delete logic
}
#RequestMapping(
path = "read",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON,
consumes = MediaType.APPLICATION_JSON)
public ResponseEntity<ResponseDTO<DTO>> read(
#RequestBody IdDTO<User> request,
#RequestHeader("X-auth-token") String token,
BindingResult bindingResult
) {
//read logic
}
}
I wanted to add Spring Validation. It is obvious that for read method and for delete method should be different validators. I want to know it it possible to make two #InitBinder one for read and another for delete?
I think you're not quite familiar with usage of BindingResult. It is used in process of binding object from request to Java object.
It searches the class validator, or uses validation annotations used on class' fields. Object you want to bind needs to be annotated with #Valid.
For example:
#PostMapping
public ResponseEntity<Note> addNote(#Valid Note note, BindingResult result) {
if(result.hasErrors())
return ResponseEntity.badRequest().build();
noteService.save(note);
return ResponseEntity.ok(noteService.getNoteById(note.getId()));
}
What you're trying to do is validate something, but not quite bind it.
I also suggest using different http methods for delete and read(GET) in the same path rather than POST method in different paths.

Update method in Rest like controllers

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

Categories

Resources