Custom validation and javax.validation.constraints doesnt work - java

I implement my own validation to check value of capacity od Budget entity in my SpringApp. Code below :
Annotation :
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Constraint(validatedBy = {CapacityValidator.class})
public #interface Capacity {
String message() default "Capacity over limit. Maximum value 9999";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
CapacityValidator class :
import com.example.CapacityGurdian.annotation.Capacity;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class CapacityValidator implements ConstraintValidator<Capacity, Float> {
private final static float MAX_CAPACITY_VALUE = 9999;
#Override
public boolean isValid(Float value, ConstraintValidatorContext constraintValidatorContext) {
return MAX_CAPACITY_VALUE < value;
}
}
Then I used it in REST controller like this :
#RestController
#RequestMapping("/budgets")
#Validated
#Api(
value = "GrantsBudgetController",
tags = "Capacity controller for budget"
)
public class BudgetController {
BudgetService budgetService;
#Autowired
public BudgetController(BudgetService budgetService) {
this.budgetService = budgetService;
}
#PutMapping({"/budget/{id}/capacity/{value}"})
#ApiOperation(value = "Updates capacity od specified budget by id",
notes = "Adds value to capacity of specified budget",
response = Budget.class)
public ResponseEntity<Budget> updateBudget(
#ApiParam(value = "Id of updated budget") #PathVariable #NotNull Long id,
#ApiParam(value = "Value which will be added to current capacity") #PathVariable
#Capacity Float value) {
return ResponseEntity.ok(budgetService.addCapacityById(id,value));
}
This code should work but not this time. Then I decided to check if standard javax.validations work by adding #Size( max = 3) and #NotEmpty on String property. Unfortunetly it also doesnt work. I checked Annotation processor in my intellij and it is set fine. Do someone know how to turn on my validations ?
Code of pom.xml :
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>CapacityGurdian</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>CapacityGurdian</name>
<description>CapacityGuardianApp</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- JPA - DATABASE-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<addResources>true</addResources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
</plugins>
</build>
</project>

Your dependencies don't contain hibernate-validator, try to add it like that:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>

Related

Spring does not see repository during scan on app start

Have sandbox springboot app. Decided to try reactive approach, postgres is used as database, I added r2dbc to the project to make my repository reactive. Here is my code:
#SpringBootApplication
public class Springboot2Application {
public static void main(String[] args) {
SpringApplication.run(Springboot2Application.class, args);
}
}
#Repository
public interface ToDoRepository extends
ReactiveCrudRepository<ToDo,String> {
}
#RestController
#RequestMapping("/api/v1")
public class ToDoController {
private final ToDoRepository repository;
public ToDoController(ToDoRepository repository) {
this.repository = repository;
}
#GetMapping(value = "/to-do", produces = {
MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE,
MediaType.TEXT_XML_VALUE})
public ResponseEntity<Flux<ToDo>> getToDos(#RequestHeader
HttpHeaders headers){
return ResponseEntity.ok().body(repository.findAll());
}
}
#Data
#RequiredArgsConstructor
#Entity
#Table(name = "todo")
public class ToDo {
#Id
#NotNull
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id;
#NotNull
#NotBlank
private String description;
#CreationTimestamp
private Timestamp created;
#UpdateTimestamp
private Timestamp modified;
private boolean completed;
}
r2dbc config:
#Configuration
#EnableR2dbcRepositories(basePackages =
"com.springboot2.repository")
public class R2DBCConfig extends AbstractR2dbcConfiguration {
#Bean
public ConnectionFactory connectionFactory() {
return ConnectionFactories.get(
ConnectionFactoryOptions.builder()
.option(DRIVER, "postgresql")
.option(HOST, "localhost")
.option(PORT, 5432)
.option(USER, "admin")
.option(PASSWORD, "admin")
.option(DATABASE, "springdb")
.build());
}
}
On application start I'm getting:
Description:
Parameter 0 of constructor in com.springboot2.controller.ToDoController required a bean of type 'com.springboot2.repository.ToDoRepository' that could not be found.
Action:
Consider defining a bean of type 'com.springboot2.repository.ToDoRepository' in your configuration.
I tried to add #ComponentsScan, tried to move ToDoRepository to the root near Springboot2Application, I dont understand why Spring doesn't see repository interface
pom.file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.springboot</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot2</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
<!-- <spring-shell.version>2.1.0</spring-shell.version>-->
</properties>
<dependencies>
<!-- DB,ORM, and plugins-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<version>0.8.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Reactive libs-->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
My guess the reason is that you have added r2dbc driver to your project, but haven't added spring-boot-starter-data-r2dbc
So, consider this dependency in your pom.xml file (this is the latest version at the time of writing this answer) :
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-r2dbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
<version>3.0.2</version>
</dependency>
EDIT:
Also you can try to use #EnableR2dbcRepositories and specify the package with your repository.
One more possible way to solve this is to extend your repository from R2dbcRepository, instead of ReactiveCrudRepository
This may help when you have multiple spring data reactive modules (r2dbc and something else) being used in the same project. Since ReactiveCrudRepository is a generic interface Spring does not know how to properly configure these beans.

Why AOP is not working in my Spring Boot project? It never execute the #Around annoted method

I was trying to follow this video tutorial to add AOP to my Spring Boot project in order to perform log operation: https://www.youtube.com/watch?v=RVvKPP5HyaA
But it is not working, it is not logging anything.
So this is my pom.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.easydefi</groupId>
<artifactId>GET-USER-WS</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>GET-USER-WS</name>
<description>Microservice that retrieves users from DB</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>nl.martijndwars</groupId>
<artifactId>web-push</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.68</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.6.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
As you can see I added this dependency to it:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.6.3</version>
</dependency>
Then I created this LoggingAOP class in order to configure AOP behavior:
package com.easydefi.aop.logging;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.java.Log;
#Aspect
#Component
#Log
public class LoggingAOP {
#Pointcut(value = "execution(* com.easydefi.users.*.*.*(..))")
public void myPointcut() {
}
#Around("myPointcut()")
public Object applicationLogger(ProceedingJoinPoint pjp) throws Throwable {
ObjectMapper mapper = new ObjectMapper();
String methodName = pjp.getSignature().getName();
String className = pjp.getTarget().getClass().toString();
Object[] argsArray = pjp.getArgs();
log.info("Method invoked " + className + ":" + methodName + "() arguments: " + mapper.writeValueAsString(argsArray));
Object response = pjp.proceed();
log.info(className + ":" + methodName + "() Response: " + mapper.writeValueAsString(response));
return response;
}
}
In theory the used #Pointcut expression:
#Pointcut(value = "execution(* com.easydefi.users.*.*.*(..))")
should define the execution o all method into all class of all package defined into the project root that is com.easydefi.users but it is logging nothing.
Running my application and calling a controller method or a service method I expected that he would enter into the applicationLogger() method. But it is not working (I also tried in debug mode and it never enter into this method).
This is my project structure:
So what is wrong? What am I missing? How can I try to fix it?
Your pointcut
execution(* com.easydefi.users.*.*.*(..))
looks a bit unflexible, but it works perfectly for intercepting methods like
public void com.easydefi.users.service.CoinService.doSomething()
public boolean com.easydefi.users.specification.MySpec.fooBar(int, String)
It works not for
public String com.easydefi.users.specification.criteria.MyCriteria.zot()
public void com.easydefi.users.aa.bb.cc.SomeService.zot()
for reasons M. Deinum has mentioned already. His suggestion to use the ..* notation, which means "any method in any class in the package preceded by ..*", is exactly right:
execution(* com.easydefi.users..*(..))
I also want to stress that the credit for this answer should be his, and if he likes to simply copy my answer and post it under his own name instead of just a comment, I shall remove this one gladly instead. But I do think questions deserve comprehensive answers.

Spring boot #Valid does not work properly

I'm facing a problem with Spring boot validation. Currently, I'm using Oracle JDK 11.0.12 and Spring boot 2.5.4 to build my project. I added constraints to validate the fields but it does not work. My code here:
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
#Data
public class LoginFormDTO implements Serializable {
private static final long serialVersionUID = 1L;
#NotBlank(message = "Login must not be blank")
private String login;
#NotNull(message = "Password must be provided")
private String password;
private boolean rememberMe = false;
}
#RestController
#RequestMapping("/api")
public class AccountResource {
// Logger and autowired components
#PostMapping("/authenticate")
#ResponseBody
public ResponseEntity<JWTToken> authorize(#Valid #RequestBody LoginFormDTO account) {
// Some code lines
return ResponseEntity.ok().body(new JWTToken(jwt));
}
}
Dependencies in pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>libphonenumber</artifactId>
<version>8.12.31</version>
</dependency>
<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>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
I expect when I send POST request with body
{
"login": null, // or empty string "", or blank string " "
"password": "string",
"rememberMe": true
}
then the server should validate fields then throw exceptions or errors (because of null constraint violation) before executing code in my authorize(#Valid #RequestBody LoginFormDTO account) function, but it does not. So what's wrong with spring validation or am I missing something?
You can simplify your Controller. You have way too many annotations doing the same thing and even some weird stuff (you return ResponseEntity but then you tell Spring that the return of the method should be the Response Body).
This may not fix the #Valid issue but for sure will make it simpler:
#RestController
#RequestMapping("/api")
public class AccountResource {
// Logger and autowired components
#PostMapping("/authenticate")
#ResponseStatus(HttpStatus.OK)
public JWTToken authorize(#Valid #RequestBody LoginFormDTO account) {
// Some code lines
return new JWTToken(jwt);
}
}
#RestController is itself annotated with #Controller and #ResponseBody, which means that #RequestMapping methods assume #ResponseBody semantics by default, so you don't need to explicitly add it.
I found out that my SwaggerConfig class should use BeanValidatorPluginsConfiguration.class instead of SpringValidatorAdapter.class in #Import({}). Now my validator works normally. Thank João Dias for supporting me in figuring it out.

Mapstruct not see related mapper

I have a mapper UserMapper which maps the filter for further DB selection.
#Mapper(uses = {RoleMapper.class})
public interface UserMapper {
#Mapping(source = "roleIds", target = "roles")
UserFilter toUserFilter(UserFilterDto dto);
}
#Mapper
public interface RoleMapper {
default List<Role> roleIdListToRoleList(List<Long> roleIds) {
return Objects.isNull(roleIds)
? Lists.newArrayList()
: roleIds.stream().map(id -> Role.builder().id(id).build()).collect(Collectors.toList());
}
}
}
Source DTO
#Data
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(callSuper = true)
#JsonIgnoreProperties(ignoreUnknown = true)
public class UserFilterDto extends FilterDto {
#JsonProperty("username")
private String username;
#JsonProperty("isEnabled")
private Boolean isEnabled;
#JsonProperty("roleIds")
private List<Long> roleIds;
}
Target object
#Data
#Builder
class UserFilter {
private String username;
private Boolean isEnabled;
private List<Role> roles;
}
I expect each List<roleIds> will be a map to List<Role> with RoleMapper and mapstruct generate something like this:
public UserFilter toUserFilter(UserFilterDto dto) {
UserFilterBuilder userFilter = UserFilter.builder();
userFilter.roles( roleMapper.roleIdListToRoleList(dto.getRoleIds()));
userFilter.username( dto.getUsername() );
userFilter.isEnabled( dto.getIsEnabled() );
return userFilter.build();
}
But instead, I get a compile error:
No property named "roleIds" exists in source parameter(s). Did you mean "null"?
Why it's happening, and how to fix it?
UPDATE
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.tradeshare</groupId>
<artifactId>tradeshare</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>tradeshare</name>
<description>tradeshare</description>
<properties>
<java.version>11</java.version>
<jsonwebtoken.version>0.9.1</jsonwebtoken.version>
<mapstruct.version>1.4.1.Final</mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>13.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>2.4.7</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jsonwebtoken.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<compilerArg>-Amapstruct.defaultComponentModel=spring</compilerArg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
Seems that you use Lombok v. > 1.18. If so you are missing
lombok-mapstruct-binding
Adding next dependency in the pom:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</dependency>
and
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
in the annotation processor should fix your mapstruct error.
Mapstruct Lombok doc

spring validation for requestparam not working

I am trying to validate my parameter in controller using #Min(1) annotation. When I test it in unit test, it comes as 200. I am not sure what I am doing wrong. Here is my code:
Controller:
#GetMapping(produces = "application/json")
public Response search(#RequestParam(name = "firstname", required = false) String firstName,
#RequestParam(name = "lastname", required = false) String lastName, #RequestParam("ibid") #Min(1) long ibId,
#RequestParam(name = "workstationids", required = false) List<Long> workstationIds,
#RequestParam("timerange") int timeRange,
#RequestParam(name = "receivingstatus", required = false) String receivingStatus,
#RequestParam(name = "displaystart", required = false) Integer displayStart,
#RequestParam(name = "displaylength", required = false) Integer displayLength,
#RequestParam("timezone") #NotBlank String timeZone) {
...
...
Test:
#Test
public void searchWithInvalidParams() throws Exception {
mockMvc.perform(get(BASE_URL)
.param(COLUMN_IB_ID, INVALID_IB_ID_VALUE) //invalid ib id is "-1"
.param(COLUMN_TIME_RANGE, TIME_RANGE_VALUE)
.param(COLUMN_TIME_ZONE, TIME_ZONE)
.andExpect(status().isBadRequest());
}
I am expecting it to return 400, but result is 200.
Update:
So I added #Validated in controller and added dependency to pom and it still doesn't work(still 200)
my pom:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.nuance.powershare</groupId>
<artifactId>psh-app-dispatch-reporter</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>psh-app-dispatch-reporter-service</artifactId>
<properties>
<sqljdbc4.version>4.0.0</sqljdbc4.version>
</properties>
<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>com.nuance.powershare</groupId>
<artifactId>psh-lib-dispatch-reporter-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc4</artifactId>
<version>${sqljdbc4.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.10.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
You have done the following 2 steps correctly:
Included the hibernate validator to the pom.xml
Added #Validated annotation to the controller
Two more steps are required. Can you do these two in addition to above:
Add one more entry to the pom.xml (not sure about why the javax.el is needed)
<dependency>
<groupId>org.glassfish </groupId>
<artifactId>javax.el </artifactId>
<version>3.0.1-b11 </version>
</dependency>
Add the following to your Java Configuration class
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
(refer - https://www.baeldung.com/spring-validate-requestparam-pathvariable)
Starting with Boot 2.3, the required steps for #RequestParam and #PathVariable validation are:
Add dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Add #Validated to controller class
Source: https://www.baeldung.com/spring-boot-bean-validation

Categories