Given below is my main controller from which I am calling the getPDFDetails method.
#RequestMapping(value=PATH_PRINT_CONTRACTS, method=RequestMethod.POST)
public ResponseEntity<?> printContracts(#RequestBody final UpdatePrintContracts updatePrintContracts) throws Exception {
System.out.println("contracts value is "+ updatePrintContracts);
Integer cancellationReasons = service.getPDFDetails(updatePrintContracts);
System.out.println("success!");
return ResponseEntity.ok(cancellationReasons);
}
Below is the UpdatePrintContracts class where I have defined all the variables with validation annotations and corresponding getter/setter methods.
public class UpdatePrintContracts {
#Valid
#NotBlank
#Pattern(regexp = "\\p{Alnum}{1,30}")
String isReprint;
#Valid
#NotBlank
Integer dealerId;
#Valid
#NotBlank
#Pattern(regexp = "\\p{Alnum}{1,30}")
String includeSignatureCoordinates;
#Valid
#NotBlank
java.util.List<Integer> contractNumbers;
public String getIsReprint() {
return isReprint;
}
public void setIsReprint(String isReprint) {
this.isReprint = isReprint;
}
public Integer getDealerId() {
return dealerId;
}
public void setDealerId(Integer dealerId) {
this.dealerId = dealerId;
}
public String getIncludeSignatureCoordinates() {
return includeSignatureCoordinates;
}
public void setIncludeSignatureCoordinates(String includeSignatureCoordinates) {
this.includeSignatureCoordinates = includeSignatureCoordinates;
}
public java.util.List<Integer> getContractNumbers() {
return contractNumbers;
}
public void setContractNumbers(java.util.List<Integer> contractNumbers) {
this.contractNumbers = contractNumbers;
}
}
I am trying to run the application as a Spring Boot app by right clicking on the project (Run As) and passing blank values for variables isReprint and includeSignatureCoordinates through Soap UI. However the validation doesn't seem to work and is not throwing any validation error in Soap UI. What am I missing? Any help is appreciated!
If you are facing this problem in latest version of spring boot (2.3.0) make sure to add the following dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Observation:
In earlier version of Spring Boot (1.4.7), javax.validation used to work out of the box. But, after upgrading to latest version, annotations broke. Adding the following dependency alone doesn't work:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
Because this provides JSR Specification but not the implementation. You can also use hibernate-validator instead of spring-boot-starter-validation.
For Anyone who is getting this issue with 2.0.1.Final:
In all SpringBoot versions above 2.2, Validations starter is not a part of web starter anymore
Check Notes here
So, all you have to do is add this dependency in your build.gradle/pom file
GRADLE:
implementation 'org.springframework.boot:spring-boot-starter-validation'
MAVEN
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
First you dont need to have #Valid annotation for those class variables in UpdatePrintContracts . You can delete them.
To trigger validation of a #Controller input, simply annotate the input argument as #Valid or #Validated:
#RequestMapping(value=PATH_PRINT_CONTRACTS, method=RequestMethod.POST)
public ResponseEntity<?> printContracts(#Valid #RequestBody final UpdatePrintContracts updatePrintContracts) throws Exception {
Refer here for full understanding of validating models in spring boot.
And If you want to check that a string contains only specific characters, you must add anchors (^ for beginning of the string, $ for end of the string) to be sure that your pattern matches all the string.Curly brackets are only to write a quantity,
#Pattern(regexp = "^[\\p{Alnum}]{1,32}$")
Lastly i assume you have following jars in your classpath,
.validation-api.jar (contains the abstract API and the annotation scanner)
.hibernate-validator.jar (contains the concrete implementation)
I faced the same error.
I had to use the below 2 dependencies alone:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
And use #Validated annotation(import org.springframework.validation.annotation.Validated) on rest controller level and #Valid annotation at method argument level(import javax.validation.Valid)
If there are any other extra dependencies like javax.validation.validation-api, org.hibernate.hibernate-validator, etc then the validations stopped working for me. So make sure that you remove these dependencies from pom.xml
I was using This dependency of validation in spring boot and didn't work ,
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
I replaced it with spring-boot-starter-validation and it worked .
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-
starter-validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.4.0</version>
this is for anyone here who still has the same issue after following the steps mentioned above. I had to restart my IDE (IntelliJ) for the changes to take effect.
My problem solved by this.
When we use classes inside classes that also need validations so #Valid needs to be annotated to all in that case.
Link for more details
Make sure to use #Valid annotation before #RequestBody
For newer versions of spring boot ensure all validation annotation are picked from jakarta.validation.* package and not javax.validation.*. As the annotations are named same in both.
Step-1: Add these two dependency in the pom.xml file
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Step-2: Create a Custom Exception class like this
package com.bjit.salon.auth.service.exceptions;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
import java.util.stream.Collectors;
#ControllerAdvice
public class AnynameApplicationException {
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public ResponseEntity<List<String>> processUnmergeException(final
MethodArgumentNotValidException ex) {
List<String> list = ex.getBindingResult().getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
return new ResponseEntity<>(list, HttpStatus.BAD_REQUEST);
}
}
Step-3: Add #Valid annotation to the method arguments like this way
public ResponseEntity<?> registerAccount(#Valid #RequestBody UserRegisterDto
registerDto) {
// rest of the codes
}
You have to add this dependency in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Note: SNAPSHOT, M1, M2, M3, and M4 releases typically WORK IN PROGRESS. The Spring team is still working on them, Recommend NOT using them.
You can use #NotEmpty will check for both blank and null values.
Add #Valid to your RestContoller class methods
Related
I have a spring boot application running a feature. I want to toggle that feature(on/off) at runtime without redeploying or restarting the application. Issue is that I can't deploy any rest endpoint as server has only exposed some specific port because of security.
I want to remotely control the toggle so that I can set that feature on and off. I tried reading the environment variable on my local machine using:
System.getEnv("envVariable")
but even after updating it using export envVariable=true it's not reflecting updated value in the code.
Can someone suggest any way to achieve this ?
Thanks,
To do this you need some more dependencies.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
in properties file you need to write
management.endpoints.web.exposure.include=*
and on the class wherever you are are using environment variables use Annotation #RefreshScope like
import org.springframework.web.bind.annotation.RestController;
import org.springframework.cloud.context.config.annotation.RefreshScope;
#RefreshScope
#RestController
public class DemoController {
#Value("${my.data}")
String str;
// code
}
and whenever you are changing environment variable just hit a post request http://localhost:PORT/actuator/refresh
using above configuration you can change the environment variables.
There is a programming pattern Feature Toggle that provides a way to turn on/off application components during the runtime. The core idea is to ask property files or database config table about current states of config fields and change application functionality if config changed. This pattern described here https://martinfowler.com/bliki/FeatureToggle.html. You can find more by using keyword "Feature Flags".
One of popular implementations of Feature Flags for Java is togglz (https://www.togglz.org/quickstart.html).
Here is an exaple of using togglz:
Create enum for features representation
public enum MyFeatures implements Feature {
#EnabledByDefault
#Label("First Feature")
FEATURE_ONE,
#Label("Second Feature")
FEATURE_TWO;
public boolean isActive() {
return FeatureContext.getFeatureManager().isActive(this);
}
Implement TogglzConfig
#ApplicationScoped
public class DemoConfiguration implements TogglzConfig {
public Class<? extends Feature> getFeatureClass() {
return MyFeatures.class;
}
public StateRepository getStateRepository() {
return new FileBasedStateRepository(new File("/tmp/features.properties"));
}
public UserProvider getUserProvider() {
return new ServletUserProvider("admin");
}
}
Describe the feature behavior depended on toggle:
if( MyFeatures.FEATURE_ONE.isActive() ) {
// new stuff here
}
Source: https://www.togglz.org/quickstart.html
With this code
#RequestMapping(value = "/bar/foo", method = RequestMethod.GET)
public ResponseEntity<foo> foo() {
Foo model;
...
return ResponseEntity.ok(model);
}
}
I get the following exception
java.lang.IllegalArgumentException: No converter found for return value of type
My guess is that the object cannot be converted to JSON because Jackson is missing. I don't understand why because I thought that Jackson was built in with spring boot.
Then I have tried to add Jackson to the pom.xml but I still have the same error
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.3</version>
</dependency>
Do I have to change any spring boot properties to make this work?
The problem was that one of the nested objects in Foo didn't have any getter/setter
Add the below dependency to your pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0.pr3</version>
</dependency>
Add the getter/setter missing inside the bean mentioned in the error message.
Use #ResponseBody and getter/setter. Hope it will solve your issue.
#RequestMapping(value = "/bar/foo", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<foo> foo() {
and update your mvc-dispatcher-servlet.xml:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
The answer written by #Marc is also valid. But the concrete answer is the Getter method is required. You don't even need a Setter.
The issue occurred in my case because spring framework couldn't fetch the properties of nested objects. Getters/Setters is one way of solving. Making the properties public is another quick and dirty solution to validate if this is indeed the problem.
#EnableWebMvc annotation on config class resolved my problem. (Spring 5, no web.xml, initialized by AbstractAnnotationConfigDispatcherServletInitializer)
I had the very same problem, and unfortunately it could not be solved by adding getter methods, or adding jackson dependencies.
I then looked at Official Spring Guide, and followed their example as given here - https://spring.io/guides/gs/actuator-service/ - where the example also shows the conversion of returned object to JSON format.
I then again made my own project, with the difference that this time I also added the dependencies and build plugins that's present in the pom.xml file of the Official Spring Guide example I mentioned above.
The modified dependencies and build part of XML file looks like this!
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
You can see the same in the mentioned link above.
And magically, atleast for me, it works. So, if you have already exhausted your other options, you might want to try this out, as was the case with me.
Just a side note, it didn't work for me when I added the dependencies in my previous project and did Maven install and update project stuff. So, I had to again make my project from scratch. I didn't bother much about it as mine is an example project, but you might want to look for that too!
I was getting the same error for a while.I had verify getter methods were available for all properties.Still was getting the same error.
To resolve an issue Configure MVC xml(configuration) with
<mvc:annotation-driven/>
.This is required for Spring to detect the presence of jackson and setup the corresponding converters.
While using Spring Boot 2.2 I run into a similiar error message and while googling my error message
No converter for [class java.util.ArrayList] with preset Content-Type 'null'
this question here is on top, but all answers here did not work for me, so I think it's a good idea to add the answer I found myself:
I had to add the following dependencies to the pom.xml:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.11.1</version>
</dependency>
After this I need to add the following to the WebApplication class:
#SpringBootApplication
public class WebApplication
{
// ...
#Bean
public HttpMessageConverter<Object> createXmlHttpMessageConverter()
{
final MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter();
final XStreamMarshaller xstreamMarshaller = new XStreamMarshaller();
xstreamMarshaller.setAutodetectAnnotations(true);
xmlConverter.setMarshaller(xstreamMarshaller);
xmlConverter.setUnmarshaller(xstreamMarshaller);
return xmlConverter;
}
}
Last but not least within my #Controller I used:
#GetMapping(produces = {MediaType.APPLICATION_XML_VALUE, MediaType. APPLICATION_JSON_VALUE})
#ResponseBody
public List<MeterTypeEntity> listXmlJson(final Model model)
{
return this.service.list();
}
So now I got JSON and XML return values depending on the requests Accept header.
To make the XML output more readable (remove the complete package name from tag names) you could also add #XStreamAlias the following to your entity class:
#Table("ExampleTypes")
#XStreamAlias("ExampleType")
public class ExampleTypeEntity
{
// ...
}
Hopefully this will help others with the same problem.
In my case i'm using spring boot , and i have encountered a similar error :
No converter for [class java.util.ArrayList] with preset Content-Type 'null'
turns out that i have a controller with
#GetMapping(produces = { "application/xml", "application/json" })
and shamefully i wasn't adding the Accept header to my requests
you didn't have any getter/setter methods.
In my case, I was returning Boolean in Response Entity
and had :
produces = MediaType.TEXT_PLAIN_VALUE,
When i changed it to below
produces = MediaType.APPLICATION_JSON_VALUE
It worked!
Example of what i had.
#PostMapping(value = "/xxx-xxxx",
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Boolean> yyyy(
I was facing same issue for long time then comes to know have to convert object into JSON using Object Mapper and pass it as JSON Object
#RequestMapping(value = "/getTags", method = RequestMethod.GET)
public #ResponseBody String getTags(#RequestParam String tagName) throws
JsonGenerationException, JsonMappingException, IOException {
List<Tag> result = new ArrayList<Tag>();
for (Tag tag : data) {
if (tag.getTagName().contains(tagName)) {
result.add(tag);
}
}
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(result);
return json;
}
I also experienced such error when by accident put two #JsonProperty("some_value") identical lines on different properties inside the class
In my case, I forgot to add library jackson-core.jar, I only added jackson-annotations.jar and jackson-databind.jar. When I added jackson-core.jar, it fixed the problem.
I saw the same error when the scope of the jackson-databind dependency had been set to test:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
<scope>test</scope>
</dependency>
Removing the <scope> line fixed the issue.
Faced same error recently - the pojo had getters/setters and all jackson dependencies were imported in pom correctly but some how "< scope > " was "provided" for jackson dependency and this caused the issue. Removing " < Scope > " from jackson dependency fixed the issue
I faced the same problem but I was using Lombok and my UploadFileResponse pojo was a builder.
public ResponseEntity<UploadFileResponse>
To solve I added #Getter annotation:
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Getter
public class UploadFileResponse
Add below dependency in pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.10.1</version>
</dependency>
Was facing the same issue as the return type cannot be bind with the MediaType of Class Foo. After adding the dependency it worked.
This might also happen due low Jackson version; e.g. Spring Boot 2.4 default Jackson version is too low when using Java records; you need at least 2.5 to serialize them properly.
I also encountered the same error on a Spring 5 project (not Spring Boot), by running a SpringMVC JUnit test-case on a method that returns ResponseEntity<List<MyPojo>>
Error: No converter found for return value of type: class java.util.ArrayList
I thought I had all the correct Jackson artifacts in my pom, but later realized that I had the legacy versions. The Maven groupId changed on the Jackson jars from org.codehaus.jacksonto com.fasterxml.jackson.core. After switching to the new jars the error went away.
Updated maven pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.7</version>
</dependency>
You are missing an Annotation #ResponseBody
I want a Feign client to consume a Spring Boot controller, and I want the contract between them to be specified in a common Interface to the degree possible.
The interface with method would look something like this:
#RequestMapping
public interface RuleManager {
#RequestMapping(value = "/addRule", method = RequestMethod.POST, consumes = {"application/json"}, produces = {"application/json"})
#ResponseBody Rule addRule(#RequestBody Rule rule);
}
The Feign client would look like:
#FeignClient(url = "http://localhost:8080")
public interface RuleManagerClient extends RuleManager { }
and the Spring boot controller:
#RestController
public class RuleManagerService implements RuleManager {
#Override
#Transactional
public Rule addRule(#RequestBody Rule rule) {
return rule;
}
}
It's nice that I don't have to specify #RequestMapping in two places, but unfortunately it seems I do have to specify #RequestBody twice. When #RequestBody is omitted from either the controller or the shared interface, the Rule object is instantiated but with all members set to null.
Is there a way around this ? Perhaps this is addressed in a newer version ? My dependencies include:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<exclusions>
<exclusion>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
<version>8.14.3</version>
</dependency>
I discovered this technique required at least feign-core 8.6 here:
https://jmnarloch.wordpress.com/2015/08/19/spring-cloud-designing-feign-client/
Thanks for any help.
Apparently this does work--#RequestBody need only appear in the shared Interface. The problem was that I had the following property set in application.properties for the controller but not for the client:
spring.jackson.property-naming-strategy=CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
That's why the object was instantiated on the server side but with all members null--effectively, the wrong properties were sent across the wire, for example "ruleName" instead of the expected "rule_name".
I use Resteasy in combination with Google Guice using Resteasy-Guice. I have been looking for ways to validate my request bodies. I want to do for example:
public static class MyPojo {
#NotEmpty private String contents;
}
And then use in my resource
#POST
#ValidateRequest
public void doPost(#Valid MyPojo myPojo) {
// use myPojo only if valid
}
I have been working with the resteasy-hibernate-validator-provider. But since I switched to newer versions, this introduced the (unwanted?) dependency to EJB. See also: RESTEASY-1056. In the comments is stated that you should switch to the newer validator-11 instead:
Switch to resteasy-validator-provider-11, which implements the newer Bean Validation 1.1 specification.
The docs say:
Validation is turned on by default (assuming resteasy-validator-provider-11-.jar is available), though parameter and return value validation can be turned off or modified in the validation.xml configuration file. See the Hibernate Validator documentation for the details.
I however do not manage to get this working to my configuration, because I find myself including dependencies like hibernate-validator, javax.el-api, javax.el and hibernate-validator-cdi and annotations like ValidateOnExecution. I however do not find any of this being instantiated or invalid requests being rejected.
What is the preferred, lightweight, and working way to do validation with Resteasy?
You do not have to specify any annotation on the resource itself or do additional configuration. Just the constraint annotations on POJOs are enough get it working.
My setup is as follows:
The resource method:
#POST
public void doPost(#Valid MyPojo myPojo) {
// use myPojo only if valid
}
The POJO:
public static class MyPojo {
#NotEmpty private String contents;
}
Tested with the following dependencies:
javax.validation version 1.1.0.Final
resteasy-validator-provider-11 version 3.0.11.Final
hibernate-validator version 5.0.0.Final and 5.0.1.Final
I accidentally had a transitive dependency to the hibernate-validator-provider which caused previous tries to fail. Ensure that you do not have a transitive dependency to the hibernate-validator-provider. For me this caused the following exception: issues.jboss.org/browse/RESTEASY-826 .
Based on Thomas answer I added dependencies to javax.validation, resteasy-validator-provider-11, hibernate-validator.
Then I still received exceptions (java.lang.NoClassDefFoundError: javax/el/PropertyNotFoundException). Based on this answer I added javax.el-api and el-impl as dependencies. I think this is because I use an embedded servlet container.
I had to remove the #ValidateOnRequest annotation on the resources, they are not necessary anymore
Final working configuration:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-validator-provider-11</artifactId>
<version>3.0.11.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.0.1.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>el-impl</artifactId>
<version>2.2</version>
</dependency>
What configuration is needed to use annotations from javax.validation.constraints like #Size, #NotNull, etc.? Here's my code:
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class Person {
#NotNull
private String id;
#Size(max = 3)
private String name;
private int age;
public Person(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
When I try to use it in another class, validation doesn't work (i.e. the object is created without error):
Person P = new Person(null, "Richard3", 8229));
Why doesn't this apply constraints for id and name? What else do I need to do?
For JSR-303 bean validation to work in Spring, you need several things:
MVC namespace configuration for annotations: <mvc:annotation-driven />
The JSR-303 spec JAR: validation-api-1.0.0.GA.jar (looks like you already have that)
An implementation of the spec, such as Hibernate Validation, which appears to be the most commonly used example: hibernate-validator-4.1.0.Final.jar
In the bean to be validated, validation annotations, either from the spec JAR or from the implementation JAR (which you have already done)
In the handler you want to validate, annotate the object you want to validate with #Valid, and then include a BindingResult in the method signature to capture errors.
Example:
#RequestMapping("handler.do")
public String myHandler(#Valid #ModelAttribute("form") SomeFormBean myForm, BindingResult result, Model model) {
if(result.hasErrors()) {
...your error handling...
} else {
...your non-error handling....
}
}
You should use Validator to check whether you class is valid.
Person person = ....;
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
Set<ConstraintViolation<Person>> violations = validator.validate(person);
Then, iterating violations set, you can find violations.
In my case, I was using spring boot version 2.3.0. When I changed my maven dependency to use 2.1.3 it worked.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
</dependencies>
I come here some years after, and I could fix it thanks to atrain's comment above. In my case, I was missing #Valid in the API that receives the Object (a POJO in my case) that was annotated with #Size. It solved the issue.
I did not need to add any extra annotation, such as #Valid or #NotBlank to the variable annotated with #Size, just that constraint in the variable and what I mentioned in the API...
Pojo Class:
...
#Size(min = MIN_LENGTH, max = MAX_LENGTH);
private String exampleVar;
...
API Class:
...
public void exampleApiCall(#RequestBody #Valid PojoObject pojoObject){
...
}
Thanks and cheers
You would have to call a Validator on the Entity if you want to validate it. Then you will get a set of ConstraintViolationException, which basically show for which field/s of your Entity there is a constraint violation and what exactly was it. Maybe you can also share some of the code you expect to validate your entity.
An often used technique is to do validation in #PrePersist and rollback transaction if using multiple data modifications during transaction or do other actions when you get a validation exception.
Your code should go like this:
#PrePersist
public void prePersist(SomeEntity someEntity){
Validator validator = Validation.buildDefaultValidatorFactory.getValidator();
Set<ConstraintViolation<SomeEntity>> = validator.validate(someEntity);
//do stuff with them, like notify client what was the wrong field, log them, or, if empty, be happy
}
In my case the reason was the hibernate-validator version.
Probably something is not supported in the newer version any more.
I changed:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
I downgraded the version from 7.0.1.Final to 6.0.2.Final and this helped me.
You can also simply use #NonNull with the lombok library instead, at least for the #NotNull scenario. More details: https://projectlombok.org/api/lombok/NonNull.html
After the Version 2.3.0 the "spring-boot-strarter-test" (that included the NotNull/NotBlank/etc) is now "sprnig boot-strarter-validation"
Just change it from ....-test to ...-validation and it should work.
If not downgrading the version that you are using to 2.1.3 also will solve it.
You need to add #Valid to each member variable, which was also an object that contained validation constraints.
in my case i had a custom class-level constraint that was not being called.
#CustomValidation // not called
public class MyClass {
#Lob
#Column(nullable = false)
private String name;
}
as soon as i added a field-level constraint to my class, either custom or standard, the class-level constraint started working.
#CustomValidation // now it works. super.
public class MyClass {
#Lob
#Column(nullable = false)
#NotBlank // adding this made #CustomValidation start working
private String name;
}
seems like buggy behavior to me but easy enough to work around i guess
I also faced the same problem. Javax annotations ( #NotNull, #Valid) were not performing any validation. Their presence was not making any difference.
I have to use 'springboot-starter-validation' dependency to make the javax validations effective.
Here is the related dependencies configuration. Also don't miss to add #Valid annotation on the Object you want to validate.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
.....
.....
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependencies>
Recently I faced the same. I upgraded hibernate-validator to ver 7.x but later I noticed this release note
Hibernate Validator 7.0 is the reference implementation for Jakarta Bean Validation 3.0.
The main change is that all the dependencies using javax. packages are now using jakarta.* packages.
Upgrade to Hibernate Validator 7 is only recommended if you are moving to Jakarta EE 9.
My project should target java 8, so keeping javax.validation instead of switiching to jakarta.validation, I've had to downgrade to
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.2.Final</version>
</dependency>
So #Valid at service interface would work for only that object. If you have any more validations within the hierarchy of ServiceRequest object then you might to have explicitly trigger validations. So this is how I have done it:
public class ServiceRequestValidator {
private static Validator validator;
#PostConstruct
public void init(){
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
public static <T> void validate(T t){
Set<ConstraintViolation<T>> errors = validator.validate(t);
if(CollectionUtils.isNotEmpty(errors)){
throw new ConstraintViolationException(errors);
}
}
}
You need to have following annotations at the object level if you want to trigger validation for that object.
#Valid
#NotNull
for method parameters you can use Objects.requireNonNull() like this:
test(String str) {
Objects.requireNonNull(str);
}
But this is only checked at runtime and throws an NPE if null. It is like a preconditions check. But that might be what you are looking for.
Great answer from atrain,
but maybe better solution to catch exceptions is to utilize own HandlerExceptionResolver
and catch
#Override
public ModelAndView resolveException(
HttpServletRequest aReq,
HttpServletResponse aRes,
Object aHandler,
Exception anExc
){
// ....
if(anExc instanceof MethodArgumentNotValidException) // do your handle error here
}
Then you're able to keep your handler as clean as possible.
You don't need BindingResult, Model and SomeFormBean in myHandlerMethod anymore.
I came across this problem recently in a very similar situation:
Met all requirements as the top-rated answer listed but still got the wrong result.
So I looked at my dependencies and found I was missing some of them. I corrected it by adding the missing dependencies.
I was using hibernate, the required dependencies were:
*Snapshot taken in class "Spring & Hibernate for Beginners" # Udemy
If you are using lombok then, you can use #NonNull annotation insted.
or Just add the javax.validation dependency in pom.xml file.
For those who have not been able to perform server-side validation through Hibernate validation dependency.
Just remove Hibernate validator +javax validation dependency and add spring-boot-starter validation. It provides Hibernate Validator Internally, and it worked just fine for me.
Credits:- a comment from youtube.
By default javax validation in spring works for Rest controller method input variables. But for other places to use the same we have to annotate class containing #Valid annotation with #Validated class level annotation.
I was facing same issue with kafka listener and after that I annotated it with #Validated it started working.
#Component
#Log4j2
#Validated
public class KafkaMessageListeners {
#KafkaListener(topics = "message_reprocessor", errorHandler = "validationErrorHandler")
public void processMessage(#Payload #Valid CustomPojo payload,
#Header(KafkaHeaders.OFFSET) List<Long> offsets, Acknowledgment acknowledgment) {
}
}
I fell into the same issue, but I got solution
your servlet configuration xml file i.e {servlet-name}-servlet.xml file
should be like
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package = "spring.tutorial.controller" />
->>> Step 4: Add support for conversion, formatting and validation support
<mvc:annotation-driven/>
<bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name = "prefix" value = "/WEB-INF/views/" />
<property name = "suffix" value = ".jsp" />
</bean>
</beans>
step 4 is important one
Just add the following in your pom.xml under tag if you are using Maven.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
For Gradle you can do the following.
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-validation'
}
In my case i removed these lines
1-import javax.validation.constraints.NotNull;
2-import javax.validation.constraints.Size;
3- #NotNull
4- #Size(max = 3)