I have a Spring Mvc Application using Spring 5.2.8.RELEASE, which is deployed in a Tomcat 9.0.37 instance.
The dependencies section of my pom.xml is the following:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.8.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
<type>maven-plugin</type>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>6.1.5.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
My WebConfig class (which is returned in the getServletConfigClasses()) is:
@Configuration
@EnableWebMvc
@ComponentScan({"com.example.myapp.web", "com.example.myapp.data"})
public class WebConfig implements WebMvcConfigurer {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
I have the following bean that contains some validation constraints:
public class User {
private Long id;
@NotBlank
@Size(min = 5, max = 16)
private String firstName;
...
}
I have a JSP file for registering a new user:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>
<%@ page session="false" %>
<html>
<head>
<title>User</title>
</head>
<body>
<h1>Register</h1>
<sf:form method="POST" modelAttribute="user">
First Name: <sf:input path="firstName"/><br/>
<!-- other fields -->
<input type="submit" value="Register"/>
</sf:form>
</body>
</html>
The controller that processes the request is the following:
@Controller
@RequestMapping("/user")
public class UserController {
// ...
@RequestMapping(value = "/register", method = POST)
public String processRegistration(@Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "registerForm";
}
userRepository.save(user);
return "redirect:/user/" + user.getUsername();
}
// ...
}
The validation doesn't get triggered whenever I click the "Register" button in the browser.
However, when I invoke the method from a unit test, the validation is performed as expected (the test below fails because the redirection is not performed):
public class UserControllerTest {
@Test
public void shouldProcessRegistration() throws Exception {
UserRepository mockRepository = mock(UserRepository.class);
User unsaved = new User("John", "Doe", "jdoe", "pass");
User saved = new User(1L, "John", "Doe", "jdoe", "pass");
when(mockRepository.save(unsaved)).thenReturn(saved);
UserController controller = new UserController(mockRepository);
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(post("/user/register")
.param("firstName", "John")
.param("lastName", "Doe")
.param("username", "jdoe")
.param("password", "pass"))
.andExpect(redirectedUrl("/user/jdoe"));
verify(mockRepository, atLeastOnce()).save(unsaved);
}
}
UserController is in the web package, WebConfig in the config package and User in the package above these two (com.example.myapp).
Anyone has any idea what am I doing wrong?
I have read all related questions on this problem, but wasn't able to find any solution for my problem.
I eventually managed to figure it out.
It was not a code problem, but a project configuration one.
After adding some dependencies in the pom.xml (namely hibernate-validator and hibernate-validator-annotation-processor), the corresponding artifacts were not automatically added in the output directory in WEB-INF/lib by IntelliJ IDEA.
I had to add these libraries manually: https://i.sstatic.net/rxIGT.jpg.
After doing so, the validation works as expected.