Spring #RestController with #RequestBody #Valid by default - java

Usually we write
#RestController
public class TestController {
#RequestMapping(value = "/test")
public String test2(#RequestBody #Valid TestClass req) {
return "test2";
}
}
But since it is a REST controller is it possible to configure Spring to use #RequestBody #Valid by default, so these annotations could be omitted?

Related

Controller with #Validated not working for validating request body with custom annotation & validator but working when used with method parameter

Bean class
#NotBlankIfAnotherFieldHasValue(fieldName = "addRequest", fieldValue = "false", dependentFieldName = "userId")
public class AddEditUserVo {
private String userId;
#NotBlank
private String userName;
#NotBlank
private String password;
#NotBlank
private String email;
private boolean addRequest;
}
Not working when the #Validation is only at class level
#Controller
#RequestMapping("/user")
#Validated
public class UserController {
#Autowired
private UserService userService;
#PostMapping(value="/update", consumes = "application/json;charset=UTF-8", produces = "application/json;charset=UTF-8")
public ResponseEntity<Response> addEditUser(#RequestBody AddEditUserVo addEditTagTypeVo) {
return userService.addEditUser(addEditTagTypeVo);
}
But working when the annotation is also added at method parameter level.
#Controller
#RequestMapping("/user")
#Validated
public class UserController {
#Autowired
private UserService userService;
#PostMapping(value="/update", consumes = "application/json;charset=UTF-8", produces = "application/json;charset=UTF-8")
public ResponseEntity<Response> addEditUser(#RequestBody #Validated AddEditUserVo addEditTagTypeVo) {
return userService.addEditUser(addEditTagTypeVo);
}
Is there anything I need to do for keeping #Validtion at class level only and without mentioning explicitly at method parameter level?

Concurrency issues on #Autowire HttpServletRequest request in Spring #Controller,#Service and #Component

As i consume a lot of data in httpservletrequest header and set a lot of values in request attribute in service class, I'm not sure if this would cause thread safety issues, I looked over the web if autowiring httpservlet request would cause threadsafety issues and i got mixed opinion
Following are the places where i autowire httpservletrequest
#RestController
Public class UserController {
#Autowire
HttpServletRequest httpServletRequest;
#Autowire
IAMService iamservice;
#PostMapping("/addUser")
public String addUser(#RequestBody UserDto userdto){
return iamservice.addUser(userdto);
}
}
#Service
Public Class IAMService {
#Autowire
HttpServletRequest httpServletRequest;
#Autowire
UserDao userDao;
public String addUser(UserDto userdto){
Long primaryKey = userDao.save(userdto,httpServletRequest.getHeader("loggedInUserId"));
httpServletRequest.setAttribute("userPrimaryKey",primaryKey);
return "User is added successfully";
}
}
We should not #Autowire HttpServletRequest. Consider modifying your code as below to have valid usage of request object and avoid thread-safety issues-
#RestController
Public class UserController {
#Autowire
IAMService iamservice;
#PostMapping("/addUser")
public String addUser(#RequestBody UserDto userdto, HttpServletRequest httpServletRequest){
return iamservice.addUser(userdto, httpServletRequest);
}
}
#Service
Public Class IAMService {
#Autowire
UserDao userDao;
public String addUser(UserDto userdto, HttpServletRequest httpServletRequest){
Long primaryKey = userDao.save(userdto,httpServletRequest.getHeader("loggedInUserId"));
httpServletRequest.setAttribute("userPrimaryKey",primaryKey);
return "User is added successfully";
}
}

How to use #valid annotation in complex model

I have complex model request class and I am using valid annotation. But it doesnt work in subclasses.
cause=java.lang.NullPointerException detailMessage=HV000028:
Unexpected exception during isValid call.
public class ChangeBlackListStatusRequest {
#Valid
List<CategoryChangeRequest> categoryChangeRequestList;
}
public class CategoryChangeRequest {
#Valid
List<Category> categoryList;
#Valid
List<Service> serviceList;
#Valid
List<Merchant> merchantList;
#Valid
List<Aggregator> aggregatorList;
}

Spring boot, how to use #Valid with List<T>

I am trying to put validation to a Spring Boot project. So I put #NotNull annotation to Entity fields. In controller I check it like this:
#RequestMapping(value="", method = RequestMethod.POST)
public DataResponse add(#RequestBody #Valid Status status, BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
return new DataResponse(false, bindingResult.toString());
}
statusService.add(status);
return new DataResponse(true, "");
}
This works. But when I make it with input List<Status> statuses, it doesn't work.
#RequestMapping(value="/bulk", method = RequestMethod.POST)
public List<DataResponse> bulkAdd(#RequestBody #Valid List<Status> statuses, BindingResult bindingResult) {
// some code here
}
Basically, what I want is to apply validation check like in the add method to each Status object in the requestbody list. So, the sender will now which objects have fault and which has not.
How can I do this in a simple, fast way?
My immediate suggestion is to wrap the List in another POJO bean. And use that as the request body parameter.
In your example.
#RequestMapping(value="/bulk", method = RequestMethod.POST)
public List<DataResponse> bulkAdd(#RequestBody #Valid StatusList statusList, BindingResult bindingResult) {
// some code here
}
and StatusList.java will be
#Valid
private List<Status> statuses;
//Getter //Setter //Constructors
I did not try it though.
Update:
The accepted answer in this SO link gives a good explanation why bean validation are not supported on Lists.
Just mark controller with #Validated annotation.
It will throw ConstraintViolationException, so probably you will want to map it to 400: BAD_REQUEST:
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
#ControllerAdvice(annotations = Validated.class)
public class ValidatedExceptionHandler {
#ExceptionHandler
public ResponseEntity<Object> handle(ConstraintViolationException exception) {
List<String> errors = exception.getConstraintViolations()
.stream()
.map(this::toString)
.collect(Collectors.toList());
return new ResponseEntity<>(new ErrorResponseBody(exception.getLocalizedMessage(), errors),
HttpStatus.BAD_REQUEST);
}
private String toString(ConstraintViolation<?> violation) {
return Formatter.format("{} {}: {}",
violation.getRootBeanClass().getName(),
violation.getPropertyPath(),
violation.getMessage());
}
public static class ErrorResponseBody {
private String message;
private List<String> errors;
}
}
#RestController
#Validated
#RequestMapping("/products")
public class ProductController {
#PostMapping
#Validated(MyGroup.class)
public ResponseEntity<List<Product>> createProducts(
#RequestBody List<#Valid Product> products
) throws Exception {
....
}
}
with using Kotlin and Spring Boot Validator
#RestController
#Validated
class ProductController {
#PostMapping("/bulk")
fun bulkAdd(
#Valid
#RequestBody statuses: List<Status>,
): ResponseEntity<DataResponse>> {...}
}
data class Status(
#field:NotNull
val status: String
)

CGLib Proxy for Integer (Final class) in Spring MVC

I need such a usage:
For each request I want to inject userId into DemoController But because of being a final class without empty constructor I can not inject it. What is the best practice in such cases? A service with request scope is fine?
#Configuration
public class CityFactory{
#Bean(name = {"currentUserId")
#Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode = ScopedProxyMode.TARGET_CLASS)
#Autowired
public Integer getUserId(HttpServletRequest request) {
return UserUtil.getCurrentUserId(request.getServerName());
}
}
#RequestMapping("/demo")
#Controller
public class DemoController {
#Autowired
Ingeter userId;
#RequestMapping(value = "/hello/{name}", method = RequestMethod.GET)
public ModelAndView helloWorld(#PathVariable("name") String name, Model model) {
Map<String, Object> myModel = new HashMap<String, Object>();
model.addAttribute("user", userId);
return new ModelAndView("v3/test", "m", model);
}
}
Your best bet is to create an explicit class called UserId, which in turn contains an integer. Not only will this play nicer with CGLIB's proxying, it also clarifies your design.
You can use Supplier or Provider
#Configuration
public class CityFactory{
#Bean
#Autowired
public Supplier<Integer> getUserId(HttpServletRequest request) {
return () -> UserUtil.getCurrentUserId(request.getServerName());
}
}
#RequestMapping("/demo")
#Controller
public class DemoController {
#Autowired
Supplier<Ingeter> getUserId;

Categories