Spring RequestParam validation in Kotlin is not working - spring-boot

I am trying to validate #RequestParam in Kotlin, however it does not work. Currently I am using Kotlin 1.4.20, Spring boot 2.3.5, and java 1.8.
Here is my controller:
#Validated
#RestController
#RequestMapping("api/v1/")
class myController{
#GetMapping("/age", produces = [MediaType.APPLICATION_JSON_VALUE])
fun findArticlesByAge(#RequestParam #Valid #Min(6) age: Int): ResponseEntity<Article> =
ResponseEntity
.ok()
.body(Article())
}
Hibernate validator is already in the effective pom:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.6.Final</version>
</dependency>
Request:
http://localhost:8080/api/v1/age?age=2
Response:
200
That is the simple validation which is not working, however I want to do more complex validations through custom annotations and ConstraintValidator. If I make it work with the simple case #Min(6) probably it will start working also with the custom annotation.

So there was a dependency missing:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
I added this and the validation started working. However, it throws 500 internal server error with the validation message, which can be fixed by a ControllerAdvice.

Related

SpringBoot with Jakarta Validation Api not validating with #Valid Annotation

i have a question to Spring boot and the dependency jakarta-validation-api.
Actually i have a simple DTO which holds some properties. But this properties are not being validated when I call the REST-function within the #Valid annotation.
Can someone find my error?
A snippet of my pom.mxml dependencies:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0-M1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.0.0</version>
</dependency>
My DTO class:
import jakarta.validation.Valid;
#Data
public class TestDTO implements Serializable {
private static final long serialVersionUID = -1362258531757232654L;
#NotEmpty(message = "Id could not be empty or null.")
#Size(min = 36, max = 36, message = "Id must contains exactly out of 36 characters.")
private String id;
#Min(value = 1, message = "Page size cannot be null or <= 0.")
private Integer page;
}
And also a snippet of the REST-Resource class where the DTO is been used in the body:
#PostMapping(path = "/")
public Integer testValidation(#Valid #RequestBody TestDTO body) {
LOGGER.info(body);
return 1;
}
Actually i would think that when I call the Post-REST method that it will be validated before it will go into the method body, but actually it goes into the method body without has been validated before.
Is it due to "jakarta" dependency instead of "javax"?
Hope you can help me :)
From what I understand in the Spring Boot 3.0.0 M1 Release Notes, Spring Boot 2.X does not support Jakarta EE, but support will come with Spring Boot 3.X.
For Spring Boot 2.X you should stick with javax.
For Spring Boot 3.X you should stick with jakarta.
In my case(spring boot 2.4.*).
I remove jakarta.validation-api dependencies,then it works.
use javax.*
not jakarta.*

How to POST MultipartFile with additional information in one POST request in Spring Boot and Swagger?

I have the following controller structure:
#PostMapping(
value = "/whatever",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<ResponseDTO> method(#RequestBody RequestDTO dto, #RequestPart MultipartFile file) {
...
}
Spring Boot version: 2.3.4.RELEASE
I am using swagger:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>
Tried and failed:
With specifying the parameters as #RequestBody and #RequestPart, when using the two together I am getting error 415.
If I remove any of the inputs, the request works, so I wither upload file or DTO.
If I am using #RequestPart for both of them, Swagger does not display the DTO json input field.
If the MultipartFile is not annotated with #RequestPart, the upload button does not appear.
If both of the parameters are annotated with #RequestParam, the upload fails with error 415
Question:
How can I upload a file with additional json information via Swagger?

De-serialization error spring boot reactive

I have a simple controller
#RestController
#RequestMapping("path")
public class MyController {
#PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<SomeObject> run(#RequestBody Flux<RequestObject> request){
//do something and return flux
}
...
}
On calling this url I'm getting the exception
"Type definition error: [simple type, class reactor.core.publisher.Flux]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Can not construct instance of reactor.core.publisher.Flux (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information\n at [Source: (PushbackInputStream); line: 1, column: 1]
I understand this error and usually, I would just add an
annotation if needed
#JsonDeserialize(as = SomeConcreteClass.class)
But in this case, to which Flux concrete example should I bind? Also, Doesn't Spring boot has a default auto-deserializers for Reactor Types (Mono, Flux)?
My pom (relevant stuff):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
You're actually using Spring MVC right now.
Remove the spring-boot-starter-web and make sure no other dependency brings it transitively.

JSR 303 Validator injection with Spring in Jersey classes

I encountered a strange behaviour that I didn't manage to explain, with a validator injection in a Jersey class with DI managed by Spring.
To sumerize the situation :
I'm using Jeysey 2 and Spring 4 to produce REST web services using the jax-rs specification, and I'm validating the bean with the jsr303 bean validation annotations. The bean validation implementation is Hibernate Validator, bundled within jersey-bean-validation dependency.
Here is my POM:
<jersey.version>2.26-b07</jersey.version>
...
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring4</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>${jersey.version}</version>
</dependency>
In my spring configuration, I declare a Validator bean (with a custom message interpolator to retrieve the error messages in database), which I'll later want to inject in my web service classes :
#Bean
public Validator getValidator(MessageInterpolator messageInterpolator){
return Validation.byDefaultProvider().configure()
.messageInterpolator(messageInterpolator)
.buildValidatorFactory()
.getValidator();
}
At this point, I'm able to inject my validator in Jersey classes using the #Autowired annotation, but not with the jsr 330 #Inject annotation which inject another validator with the default Hibernate Validator message interpolator...
#Autowired
private Validator validator; // OK
#Inject
private Validator validator; // KO
#Inject
private OtherService otherService; // OK
Can you explain me what happen here ?
I understood that the dependency injection implementation bundled with Jersey is HK2, but the jersey-spring plugin is supposed to do the bridge between HK2 and Spring, I must have missed something because they seem completely dissociated, I think that :
#Autowired use Spring and inject the bean I've defined
#Inject use HK2 which is not aware of that bean, and inject the one found in the Hibernate Validator project...
By the way I'm perfectly able to inject my other Spring services with the #Inject annotation, that's why I'm loosing my mind and don't know what to think... The only problematic bean seems to be this Validator.
I also tried to use the #Valid annotation to validate my beans without having to inject the validator, and the problem is the same : Jersey use the default validator from Hibernate Validator...
After lots of time, I found a way to fix the #Valid problem by creating a ContextResolver Provider to modify Jersey default configuration with my custom message interpolator :
#Provider
public class ValidationConfigContextResolver implements ContextResolver<ValidationConfig> {
private final MessageInterpolator messageInterpolator;
#Inject
public ValidationConfigContextResolver(MessageInterpolator messageInterpolator) {
this.messageInterpolator = messageInterpolator;
}
#Override
public ValidationConfig getContext(Class<?> aClass) {
final ValidationConfig config = new ValidationConfig();
config.messageInterpolator(messageInterpolator);
return config;
}
}
Now Jersey use a validator with my message interpolator but I'm still trying to understand why I'm not able to inject it with the #Inject annotation in Jersey classes !
Thanks for the assistance

Hibernate Validator 5.1.1 is not working in Web application

I am using Spring 4.0 and latest Spring Security in my Web application. I want to use the Hibernate validation 5.1.1 Final. But It's not working. The same code works in my JUNIT but not in the web application.
The code which works:
#Test
public void testUserAuthenticationRequestValidation(){
try{
UserAuthenticationRequest req=new UserAuthenticationRequest().addUserName("bla").addPasswd("passw");
Validator validation=Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<UserAuthenticationRequest>> constraintViolations= validation.validate(req);
Assert.assertTrue("There are some errors", constraintViolations.size()>0);
System.out.println(constraintViolations);
}catch(Exception ex){
ex.printStackTrace();
Assert.fail();
}
But if I try to use the same code in my web application, it doesn't work, In the below code, I am expecting contraintViolation set not to be empty because I intentionally left both the username & password empty but I always find it empty.
#Override
public UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
LOGGER.debug("About to check authentication for username: {}", username);
final UserAuthenticationRequest userData = new UserAuthenticationRequest()
.addUserName(username).addPasswd(
authentication.getCredentials().toString());
Validator validation = Validation.buildDefaultValidatorFactory()
.getValidator();
Set<ConstraintViolation<UserAuthenticationRequest>> constraintViolations = validation
.validate(userData);
LOGGER.info("Constraints Violations: {}", constraintViolations);
LOGGER.info("Obejct: username: {} password: {}",
userData.getUsername(), userData.getPasswd());
UserDetailVO userDetailVO = new UserDetailVO(
userInfoService.authenticate(userData));
return userDetailVO;
}
So far I have also tried in vain initializing the validation bean as shown below and injecting in my Class as well as shown below:
#Configuration
#ComponentScan({ "com.sell.mystuff.web.security",
"com.sell.mystuff.web.service" })
#ImportResource({ "classpath*:spring/common-context.xml" })
public class CommonAppContext {
#Bean(name = "javaxValidator")
public Validator getValidator() {
return Validation.buildDefaultValidatorFactory().getValidator();
}
}
I have even tried to explicitly initialized in the validation bean as shown above but nothing seems to be working.
Below is my partial POM.xml file related to validation api:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.1.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>5.1.1.Final</version>
</dependency>
Do you have the validator bean configured?
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
This will bootstrap Bean Validation 1.0 or 1.1, depending on which version of Hibernate Validator you have on the classpath. See also http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html
Even if you plan to manually call the validation, you should do it by retrieving the configured factory bean, since this will guarantee proper caching of the factory.
I got this working by adding a later version of javax.el
e.g.
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
BTW I didn't need to define a LocalValidatorFactoryBean in my spring mvc config.

Resources