레거시 프로그램에 validator를 적용하기로 했다.
validator는 총 3가지 버전이 있는데 다음과 같다
Bean Validation 1.0(JSR-303)
Bean Validation 1.1(JSR-349)
Bean Validation 2.0(JSR-380)
그래서 추가하기 위해서 다음의 패키지를 추가한다
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.17.Final</version>
</dependency>
그리고 servlet.xml 에 bean을 등록했다
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
테스트를 위해 Controller에 Post API를 하나 생성한다.
@RestController
public class TestValidatorController {
@PostMapping("/test/validator")
public String testValidator(@RequestBody @Valid TestValidatorRequest request){
System.out.println("request = " + request);
return request.toString();
}
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString
static class TestValidatorRequest {
@NotEmpty(message = "이름은 필수 입니다")
private String name;
private int age;
}
}
POSTMAN이나 CURL을 통해 /test/validator 를 호출하면 @Valid 가 동작하는지 확인한다
정상건
curl -X POST localhost:8080/test/validator \
-H "Content-Type: application/json" \
-d '{"name":"테스터","age":"10"}'
request = TestValidatorController.TestValidatorRequest(name=테스터, age=10, gender=null)
validator 오류 확인하기
curl -X POST localhost:8080/test/validator \
-H "Content-Type: application/json" \
-d '{"age":"10"}'
Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String kr.dgpark.test.TestValidatorController.testValidator(kr.dgpark.test.TestValidatorController$TestValidatorRequest): [Field error in object 'testValidatorRequest' on field 'name': rejected value [null];
codes [NotEmpty.testValidatorRequest.name,NotEmpty.name,NotEmpty.java.lang.String,NotEmpty];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable:
codes [testValidatorRequest.name,name];
arguments [];
default message [name]]; default message [이름은 필수 입니다]] ]
의도한대로 이름값을 넣지않아 에러가 발생함을 확인했다.
문제는 테스트를 작성하면서였다. (사실 테스트 먼저 작성하다 에러가 발생해서 완전 헛다리 짚고 있었다....)
Test를 하는데 다음의 에러가 발생한다.
@Test
@DisplayName("validator 테스트")
void validatorTest() throws Exception {
// given
Map param = new HashMap<>();
param.put("age", 10);
String content = objectMapper.writeValueAsString(param);
// when
MvcResult mvcResult = mockMvc.perform(post("/test/validator")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(content))
.andReturn();
// then
MethodArgumentNotValidException resolvedException = (MethodArgumentNotValidException) mvcResult.getResolvedException();
List<FieldError> fieldErrors = resolvedException.getBindingResult().getFieldErrors();
for (FieldError fieldError : fieldErrors) {
System.out.println("fieldError.getDefaultMessage() = " + fieldError.getDefaultMessage());
}
}
'Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use' 문구 그대로 검색하면 maven이나 gradle에 다음의 패키지를 추가하라고 한다.
<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>
그럼에도 불구하고 동일한 현상이 반복되었다.
그러다 몇몇 블로그에서 버전을 낮추면(Bean Validation 2.0(JSR-380) => 1.1(JSR-349)) 사용할 수 있다고 해서 해봤는데 되긴된다.
만약 버전을 낮춰서 사용할 거라면 의존성을 다음으로 교체하면 된다.
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
</dependency>
다만 개인적으로 Validation 2.0을 써보고 싶어서 좀더 알아보니 우연히 stackoverflow에서 jsp-api 버전을 변경해보라고 써 있었다.
이전엔 2.1 이었는데 2.2로 변경해봤다.
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<!-- <version>2.1</version> -->
<version>2.2</version>
<scope>provided</scope>
</dependency>
테스트를 돌려보니 이전의 오류는 없어졌고 테스트가 완료되었다.
fieldError.getDefaultMessage() = 이름은 필수 입니다
stackoverflow에 있는 글을 여기다 복사한다.
There are newer versions of that dependency that you might want to take a look at. When using the 2.1 version there were no validators being registered/added to ExtendedServletRequestDataBinder or more precisely the SpringValidatorAdapter targetValidator was null.
The root cause was that the exception below was being thrown in OptionalValidatorFactoryBean.afterPropertiesSet
javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead
2.1 버전에서는 SpringValidatorAdapter targetValidator가 null이고 본 문제는 OptionalValidatorFactoryBean에서 HV000183: Unable to initialize 'javax.el.ExpressionFactory' 가 발생했기 때문이다.
혹시나 jsp-api 패키지를 빼보니 다음 에러가 발생했다.
Caused by: java.lang.NoClassDefFoundError: javax/servlet/jsp/JspFactory
at org.springframework.web.servlet.view.tiles3.TilesConfigurer$SpringTilesContainerFactory.createAttributeEvaluatorFactory(TilesConfigurer.java:386)
at org.apache.tiles.factory.BasicTilesContainerFactory.createContainer(BasicTilesContainerFactory.java:88)
at org.apache.tiles.startup.AbstractTilesInitializer.createContainer(AbstractTilesInitializer.java:114)
at org.apache.tiles.startup.AbstractTilesInitializer.initialize(AbstractTilesInitializer.java:64)
at org.springframework.web.servlet.view.tiles3.TilesConfigurer.afterPropertiesSet(TilesConfigurer.java:279)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774)
... 68 more
Caused by: java.lang.ClassNotFoundException: javax.servlet.jsp.JspFactory
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 75 more
Springboot 에는 없어도 잘 돌드만... 왜 그런지는 좀더 공부해봐야 할거 같다.
정리하자면,
1)
JSR-380 버전을 사용할거라면 체크해봐야 할 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>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.17.Final</version>
</dependency>
2)
jsp-api 버전을 확인한다
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
끝.
참조:
https://medium.com/@gaemi/java-%EC%99%80-spring-%EC%9D%98-validation-b5191a113f5c
'공부 > 프로그래밍' 카테고리의 다른 글
[springboot, oauth] Authorization Server(인증서버) 구축하기 (2) | 2020.03.21 |
---|---|
[react + next.js] 페이지 이동(push, href 차이) (0) | 2020.03.19 |
[react] 다국어 처리(react-i18next) 적용하기 (1) | 2020.03.14 |
[springboot, oauth2] 라인(LINE) 소셜 로그인 연동(jwt, jwkSetUri) (0) | 2020.03.09 |
[spring, axios] Content-Type 을 json 또는 application/x-www-form-urlencoded 로 전송 테스트 (0) | 2020.03.04 |
댓글