Can we use BindingResult for REST API in Spring? - spring

Usually, BindingResult is used in Spring MVC for the form validation. Recently I came to know that some developers are using BindingResult in the Spring REST API as well. I went through the official document but it's not clear to me whether it would be allowed to use BindingResult in REST API or not.
Any thoughts, please?

Of course it is allowed to use BindingResult in a RestAPI, especially if you have a Front-End and you want to do the validation of data in the back end.
A very simple example:
#PostMapping("/signup")
public ResponseEntity signUp(#Valid #RequestBody RegisteringUserDto registeringDto, BindingResult bindingResult)
{
if (bindingResult.hasErrors())
{
bindingResult
.getFieldErrors()
.forEach(f -> System.out.println(f.getField() + ": " + f.getDefaultMessage()));
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
return authService.signUp(registeringDto);
}
This is just printing the errors in the Console, but you can send them to the front end via the ResponseEntity.

Related

#RequestBody not restricting to POJO type and BindingResult hasErrors always false

I was not experiencing this problem early in development but just noticed that this was happening when debugging another problem. This happens on all REST endpoints, but below is an example:
#RestController
#RequestMapping("/editlisting")
public class EditParkingSpaceListingController {
#Autowired
ParkingSpaceRepository parkingSpaceRepository;
#Autowired
ParkingSpaceListingRepository parkingSpaceListingRepository;
#RequestMapping(method = RequestMethod.PUT)
public ResponseEntity<String> editParking(#RequestBody ParkingSpaceListingClient pslc, BindingResult result) {
if (result.hasErrors()) {
return new ResponseEntity<String>("", HttpStatus.BAD_REQUEST);
}
// Code to save pslc data to database.
Now, if I send an HTTP request with the body as
{ }
I get a 200 response and when I check MongoDB, there is a new empty document in the collection. If I send an empty body with no brackets, as expected it will return 400. If I send a body with random garbage data that does not exist in the POJO, BindingResult does not seem to pick up the error and a new blank document is still created.
You need to follow the below steps for the input document validations:
(1) Add the javax.validation package constraints (like #NotNull, #Size, etc..) to your ParkingSpaceListingClient bean class.
(2) Add #Validated annotation to your controller method, to capture the validation errors into BindingResult object.
You can look here for more details on Input Validations.

400 Bad request when validation Model Attribute with Spring MVC

I am writing a simple controller that accepts request on user sign-up I want to validate the Model Attribute but I receive 400 bad request.
I've checked this question here and is pretty much the problem I have but the solutions does not work for me.
#RequestMapping(method = RequestMethod.POST, value = "/sign-up", consumes = "application/x-www-form-urlencoded")
public ModelAndView addUser(ModelMap model,#Valid #ModelAttribute
UserRegistrationInfo userRegistrationInfo,
HttpServletRequest httpRequest,
HttpSession httpSession,
BindingResult result) { ..... }
EDIT
Spring Version: 4.0.6.RELEASE
I've read SpringMVC architecture and set a break points at DefaultHandlerExceptionResolverclass, doResolveException method and found that a BindException exception is thrown.However, I did not know why BindingResult is not filled and method execution is not called to let me determine what behavior I want ? the execution ends up at return handleBindException((BindException) ex, request, response, handler); which is
protected ModelAndView handleBindException(BindException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { response.sendError(HttpServletResponse.SC_BAD_REQUEST); return new ModelAndView(); }
As you've rightly figure out, you're getting a bad request on account of the binding errors. All the info that you need are in fact contained in the BindingResult object but in order to use it properly you should set your BindingResult to immediately follow your ModelAttribute e.g.
#RequestMapping(method = RequestMethod.POST, value = "/sign-up", consumes = "application/x-www-form-urlencoded")
public ModelAndView addUser(ModelMap model,#Valid #ModelAttribute
UserRegistrationInfo userRegistrationInfo, BindingResult result
HttpServletRequest httpRequest,
HttpSession httpSession,
) { ..... }
While ordering of arguments generally doesn't matter, BindingResult parameter is an exception, as the method can contain several ModelAttribute parameters, each having its own dedicated instance of BindingResult. In this case the association is established by having the BindingResult parameter immediately follow the ModelAttribute it applies to.
When you reorder your parameters you will no longer get the 400 error, rather the request will enter the controller, and the logs will show the exact binding problem, or you simply check if result.hasErrors() and iterate through field errors by calling result.getAllErrors(). Then it should be simple enough to resolve your binding issue and the consequent bad request.
Check the section od the doc http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-methods

Handle (TaskId) Redirect in Spring MVC

User's controllers needs to concatenate the task ID to be obtained in a .js that is loaded by the Architecture (me) when the view is loaded. This mechanism is necessary to tasks control. In this context:
#RequestMapping(method = RequestMethod.POST)
public String create(#Valid Vote vote, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
if (bindingResult.hasErrors()) {
uiModel.addAttribute("vote", vote);
addDateTimeFormatPatterns(uiModel);
return "votes/create";
}
uiModel.asMap().clear();
vote.persist();
return "redirect:/votes/" + encodeUrlPathSegment(vote.getId().toString(), httpServletRequest) +
"**?ArcSpring.idTask=" + httpServletRequest.getParameter("ArcSpring.idTask")**;
}
Is there a way to handle this redirect in the way I can concatenate the task ID to make it transparent for users?
I tried with a customized class extending from UrlBasedViewResolver, capturing the url and adding the attributes by my own, but with no kind of success.

RedirectAttributes giving IllegalStateException in Spring 3.1

I'm want to use RedirectAttibutes property that has come up in Spring 3.1, I have the following handler method for post in my controller
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String register(#ModelAttribute("admin") Admin admin, BindingResult bindingResult, SessionStatus sessionStatus, RedirectAttributes redirectAttributes) {
redirectAttributes.addAttribute("admin", admin);
if (bindingResult.hasErrors()) {
return REGISTRATION_VIEW;
}
sessionStatus.setComplete();
return "redirect:list";
}
But when I submit the form I'm getting the following exception:
java.lang.IllegalStateException: Argument [RedirectAttributes] is of type Model or Map but is not assignable from the actual model. You may need to switch newer MVC infrastructure classes to use this argument.
org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:322)
I have come across a few gotcha's with redirectAttributes that you cannot use ModelAndView as the return type. So I returned just the string view.
Can anyone pl. tell me where I'm going wrong?
Thanks.
Spring 3.1 introduced new version of Spring MVC backend implementation (RequestMappingHandlerMapping/RequestMappingHandlerAdapter) to replace the old one (DefaultAnnotationHandlerMapping/AnnotationMethodHandlerAdapter).
Some new features of Spring MVC 3.1, such as RedirectAttributes, are only supported by the new implementation.
If you use <mvc:annotation-driven> or #EnableWebMvc to enable Spring MVC, new implementation should be enabled by default. However, if you declare HandlerMapping and/or HandlerAdapter manually or use the default ones, you need to switch to the new implementation explicitly (for example, by switching to <mvc:annotation-driven>, if it doesn't break your configuration).

Spring portlet MVC. Validation before #ResourceMapping. #ResourceMapping -> #RenderMapping

My case: a user can download files. There is a list of files he can select.
There is a spring mapping:
#ResourceMapping(DOWNLOAD)
public void downloadSelected(ResourceRequest request, ResourceResponse response, AuditView auditView, BindingResult bindingResult) {
}
auditView has a list of files.
If user didn't select any I need to validate and display the same page with error displayed.
i can validate: validator.validate(auditView, bindingResult);
The question is how to forward to Render phase in case of errors?
It might be late to answer but, It may be helpful to others.
There is no way you can forward a Request to RenderPhase from ResourcePhase.
Please refer this link for a solution to a similar requirement.
I only tested this with WebSphere Liberty Profile's portlet container, so I don't know if it works with other containers:
#ResourceMapping
public void downloadSelected(#Valid #ModelAttribute("entity") Entity entity, BindingResult bindingResult, ResourceResponse response)
{
if (bindingResult.hasErrors()) {
response.setProperty(ResourceResponse.HTTP_STATUS_CODE, "302");
response.setProperty("Location", response.createRenderURL().toString());
} else {
response.setContentType("application/pdf");
response.setProperty("Content-disposition", "attachment; filename=\"mydownload.pdf\"");
/* ... */
}
}
However, it seems, that binding result gets lost and error messages aren't appearing in the render phase if Spring MVC's <form:errors /> JSP tag is used.
just check for errors and return the form view and annotate the AuditView with #Valid and #ModelAttribute anotations. #Valid annotation will triger validate method of controler validator. #ModelAttribute will put the AuditView into the model.
#ResourceMapping(DOWNLOAD)
public void downloadSelected(ResourceRequest request, ResourceResponse response,#Valid #ModelAttribute("auditView") AuditView auditView, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "thedownloadpage";
}

Resources