<form:form>
태그 사용BindingResult타입 객체를 컨트롤러 메서드의 매개변수로 선언하면,
예외가 발생했을 때 에러페이지로 가지 않고, 컨트롤러에 Binding 결과를 주고, 컨트롤러가 처리하도록 한다.
컨트롤러 메서드에 BindingResult를 선언 했을 때와 안했을 때 차이를 알자!
PropertyEditor, Converter, Formatter
양방향 타입 변환(String -> 타입, 타입 -> String).
특정 타입이나 이름의 필드에 적용 가능하다.
디폴트 PropertyEditor - 스프링이 기본적으로 제공.
커스텀 PropertyEditor - PropertyEditorSupport를 상속해서 직접 구현 가능.
모든 컨트롤러 내에서의 변화 - WebBindingInitializer를 구현 후 등록.
특정 컨트롤러 내에서의 변환 - 컨트롤러에 @InitBinder가 붙은 메서드를 작성.
단방향 타입(타입A -> 타입B)으로 변환하므로,
PropertyEditor(stateful)의 단점을 개선(iv를 사용 -> 싱글톤 불가).
Converter(stateless)(iv 사용X -> 싱글톤으로 사용 가능).
양방향 타입 변환(String -> 타입, 타입 -> String)
바인딩할 필드에 적용 - @NumberFormat, @DateTimeFormat
@InitBinder를 붙인 메서드를 작성해서 PropertyEditor를 사용한다.
해당 컨트롤러 내에서만 사용할 수 있다.
@InitBinder
public void toDate(WebDataBinder binder) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
//커스텀에디터로 SimpleDateFormat의 날짜 형식을 Date클래스로 변환한다.
binder.registerCustomEditor(Date.class, new CustomDateEditor(df, false));
//커스텀에디터로 "#"구분자로 나눠서 String[]로 변환한다.
binder.registerCustomEditor(String[].class, "hobby", new StringArrayPropertyEditor("#"));
}
날짜와 숫자는 컨트롤러에 PropertyEditor 대신 Formatter를 iv에 붙여서 타입변환을 할 수 있다.
@DateTimeFormat, @NumberFormat
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birth;
Validator란?
객체를 검증하기 위한 인터페이스. 객체 검증기(validator) 구현에 사용.
public interface Validator {
//이 검증기로 검증가능한 객체인지 알려주는 메서드
boolean supports(Class<?> clazz);
//객체를 검증하는 메서드 - target: 검증할 객체, errors:검증시 발생한 에러저장소
void validate(Object target, Errors errors);
}
public class UserValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
//clazz가 User타입인지 확인
return User.class.equals(clazz);
//clazz가 User또는 그 자손인지 확인
//return User.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
User user = (User)target;
String id = user.getId();
//if(id==null || "".equals(id.trim())) {
// errors.rejectValue("id", "required");
//}
//위 주석 3줄을 1줄로 한 것.
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "pwd", "required");
if(id==null || id.length() < 5 || id.length() > 12) {
errors.rejectValue("id", "invalidLength");
}
}
}
컨트롤러에 UserValidator객체를 만들어서 validator로 검증.
에러가 있으면 registerForm으로 이동.
@PostMapping("/register/add")
public String save(User user, BindingResult result, Model model) {
UserValidator userValidator = new UserValidator();
userValidator.validate(user, result); //validator로 검증
if(result.hasErrors()) { //에러가 있으면
return "registerForm";
}
}
@InitBinder의 메서드에 setValidator()로 등록하고,
검증 할 객체 앞에 @Valid를 붙인다.
@Valid는 javax.validation을 사용하므로,
Maven Ropo에서 validation-api를 pom.xml에 추가한다.
@InitBinder
public void toDate(WebDataBinder binder) {
//1 - UserValidator를 WebDataBinder에 등록한다.
binder.setValidator(new UserValidator());
}
@PostMapping("register/add")
//2 - @Valid를 검증할 객체 앞에 붙이면 자동검증한다.
public String save(@Valid User user, BindingResult result, Model model) {
if(result.hasErrors()) {
return "registerForm";
}
}
pom.xml
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
하나의 Validator로 여러 객체를 검증할 때, 글로벌 Validator로 등록.
<!-- 아래 빈을 annotation-driven에 bean의 id를 등록 -->
<annotation-driven validator="globalValidator"/>
<!-- 빈으로 등록 -->
<beans:bean id="globalValidator" class="com.fastcampus.ch2.GlobalValidator"/>
@InitBinder
public void toDate(WebDataBinder binder) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(df, false));
// binder.setValidator(new UserValidator()); //UserValidator를 WebDataBinder의 로컬 validator로 등록
binder.addValidators(new UserValidator()); //글로벌Validator에 로컬Validator를 추가(add)
public interface MessageSource {
String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NuSuchMessageException;
}
<bean:bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basenames">
<beans:list>
<beans:value>error_message</beans:value> <!-- /src/main/resources/error_message.properties -->
</beans:list>
</beans:property>
<beans:property name="defaultEncoding" value="UTF-8"/>
</beans:bean>
required=필수 항목입니다.
required.user.pwd=사용자 비밀번호는 필수 항목입니다.
invalidLength.id=아이디의 길이는 {1}~{2}사이어야 합니다.
콘솔이 아닌 브라우저에 검증 메세지를 출력하려면,
스프링이 제공하는 커스텀 태그 라이브러리를 사용해야 한다.
1. JSP페이지에 임포트 해야한다.
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<form>
대신 <form:form>을 사용하면 자동변경된다.
<!-- user는 검증할 객체 -->
<form:form modelAttribute="user">
</form:form>
아래와 같이 변경된다.
<form id="user" action="/ch2/register/add" method="post">
</form>
2-2. form태그안에 <form:errors>
로 에러를 출력 할 수 있다.
path에 에러가 발생하는 필드를 지정한다.(*는 모든 필드의 에러)
즉 user객체의 id란 필드에서 발생하는 에러의 msg를 보여준다는 의미이다.
<form:errors path="id" cssClass="msg"/>
아래와 같이 변경된다.
<span id="id.errors" class="msg">필수 입력 항목입니다.</span>
<div id="msg" class="msg"><form:errors path="id"/></div>