봄 MVC: 검증 실행 방법
사용자 입력 폼 검증을 수행하는 가장 깨끗하고 최선의 방법이 무엇인지 알고 싶습니다.몇몇 개발자들이 이를 구현하는 것을 본 적이 있습니다. 이에 대한 질문:수업 검증하는 거 봤어요.클래스를 사용자 입력 값으로 수동으로 채운 다음 검증자에게 전달해야 합니까?
사용자 입력을 검증하는 가장 깨끗하고 최선의 방법이 무엇인지 헷갈립니다.는 전통적인 있습니다.request.getParameter()
하겠습니다.nulls
제 M&A에서 Controller
이 부분에 대해 좋은 조언을 해주시면 감사하겠습니다.이 어플리케이션에서는 하이버네이트를 사용하지 않습니다.
Spring MVC에서는 주석 사용, 수동 또는 두 가지 조합의 3가지 검증 방법이 있습니다.검증에 있어서 독자적인 「가장 깨끗하고 최선의 방법」은 없지만, 고객의 프로젝트/문제/콘텍스트에 보다 적합한 방법이 있을 수 있습니다.
사용자:
public class User {
private String name;
...
}
방법 1 : Spring 3.x+ 및 간단한 검증이 필요한 경우javax.validation.constraints
(JSR-303 )
public class User {
@NotNull
private String name;
...
}
라이브러리에는 참조 구현인 Hibernate Validator와 같은 JSR-303 공급자가 필요합니다(이 라이브러리는 데이터베이스 및 관계 매핑과는 무관하며 검증 :-만 수행합니다).
컨트롤러에는 다음과 같은 기능이 있습니다.
@RequestMapping(value="/user", method=RequestMethod.POST)
public createUser(Model model, @Valid @ModelAttribute("user") User user, BindingResult result){
if (result.hasErrors()){
// do something
}
else {
// do something else
}
}
@Valid :사용자의 이름이 늘일 경우 result.hasErrors()가 true가 됩니다.
방법 2 : 복잡한 검증(대기업 검증 로직, 여러 필드에 걸친 조건부 검증 등)이 있는 경우 또는 어떤 이유로 방법 1을 사용할 수 없는 경우 수동 검증을 사용하십시오.컨트롤러의 코드를 검증 로직에서 분리하는 것이 좋습니다.검증 클래스를 처음부터 만들지 마십시오.스프링에는 편리한 기능이 준비되어 있습니다.org.springframework.validation.Validator
【2】
예를 들어 다음과 같이 합시다.
public class User {
private String name;
private Integer birthYear;
private User responsibleUser;
...
}
또, 다음과 같은 「복잡한」검증을 실시할 필요가 있습니다.사용자의 연령이 18세 미만인 경우, consible User는 null이 되어서는 안 됩니다.사용자의 나이는 21세 이상이어야 합니다.
이런 걸 하게 될 거야
public class UserValidator implements Validator {
@Override
public boolean supports(Class clazz) {
return User.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors) {
User user = (User) target;
if(user.getName() == null) {
errors.rejectValue("name", "your_error_code");
}
// do "complex" validation here
}
}
컨트롤러에는 다음과 같은 기능이 있습니다.
@RequestMapping(value="/user", method=RequestMethod.POST)
public createUser(Model model, @ModelAttribute("user") User user, BindingResult result){
UserValidator userValidator = new UserValidator();
userValidator.validate(user, result);
if (result.hasErrors()){
// do something
}
else {
// do something else
}
}
검증 오류가 있는 경우 result.hasErrors()가 true가 됩니다.
주의: 컨트롤러의 @InitBinder 메서드에서 "binder"를 사용하여 검증자를 설정할 수도 있습니다.setValidator(...") (이 경우 기본 검증자를 대체하기 때문에 메서드1과 2를 혼재시킬 수 없습니다).또는 컨트롤러의 기본 컨스트럭터에서 인스턴스화할 수도 있습니다.또는 컨트롤러에 @Component/@Service UserValidator를 삽입(@Autowired): 매우 편리합니다.대부분의 검증자는 싱글톤이고 유닛테스트 모킹이 쉬워지며 검증자는 다른 스프링 컴포넌트를 호출할 수 있기 때문입니다.
방법 3 : 두 가지 방법을 조합하여 사용하면 어떨까요?"이름" 속성과 같은 간단한 내용을 주석으로 검증합니다(빠르고 간결하며 읽기 쉬움).검증자에 대한 무거운 검증(커스텀 복잡한 검증 주석을 코드화하는 데 몇 시간이 걸리는 경우 또는 주석을 사용할 수 없는 경우)을 유지합니다.이전 프로젝트에서 이 작업을 수행했는데, 매우 쉽고 빠르게 작동했습니다.
경고: 유효성 검사 처리를 예외 처리로 오인해서는 안 됩니다.사용 방법에 대해서는, 이 투고를 참조해 주세요.
참고 자료:
- bean validation에 관한 매우 흥미로운 블로그 투고(원래 링크가 끊어졌습니다)
- 검증에 관한 또 하나의 좋은 블로그 투고(원래 링크가 불통)
- 검증에 관한 최신 스프링 문서
사용자 입력을 검증하는 방법에는 주석과 Spring의 Validator 클래스를 상속하는 두 가지가 있습니다.간단한 경우에는 주석이 좋습니다.복잡한 검증이 필요한 경우(예를 들어 크로스 필드 검증 등)."이메일 주소 확인" 필드) 또는 다른 규칙을 사용하여 응용 프로그램의 여러 위치에서 모델을 검증하거나 모델 개체에 주석을 달아 모델 개체를 수정할 수 없는 경우 Spring의 상속 기반 검증기를 사용하는 것이 좋습니다.둘 다 예를 들어 보겠습니다.
실제 검증 부분은 사용하고 있는 검증 유형에 관계없이 동일합니다.
RequestMapping(value="fooPage", method = RequestMethod.POST)
public String processSubmit(@Valid @ModelAttribute("foo") Foo foo, BindingResult result, ModelMap m) {
if(result.hasErrors()) {
return "fooPage";
}
...
return "successPage";
}
하고 있는 는, 「주석」을 해 주세요.Foo
츠키다
public class Foo {
@NotNull
@Size(min = 1, max = 20)
private String name;
@NotNull
@Min(1)
@Max(110)
private Integer age;
// getters, setters
}
위의 주석은javax.validation.constraints
주석입니다.하이버네이트를 사용할 수도 있습니다.org.hibernate.validator.constraints
단, Hibernate를 사용하고 있는 것 같지는 않습니다.
또는 Spring's Validator를 구현하는 경우 다음과 같이 클래스를 만듭니다.
public class FooValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return Foo.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors) {
Foo foo = (Foo) target;
if(foo.getName() == null) {
errors.rejectValue("name", "name[emptyMessage]");
}
else if(foo.getName().length() < 1 || foo.getName().length() > 20){
errors.rejectValue("name", "name[invalidLength]");
}
if(foo.getAge() == null) {
errors.rejectValue("age", "age[emptyMessage]");
}
else if(foo.getAge() < 1 || foo.getAge() > 110){
errors.rejectValue("age", "age[invalidAge]");
}
}
}
위의 검증기를 사용하는 경우 검증기를 스프링 컨트롤러에 바인딩해야 합니다(주석을 사용하는 경우 필요하지 않음).
@InitBinder("foo")
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new FooValidator());
}
도움이 됐으면 좋겠다.
나는 제롬 달버트의 좋은 답변을 연장하고 싶다.JSR-303 방식으로 주석 검증기를 작성하는 것은 매우 간단했습니다.「1 필드」의 검증에 한정되는 것은 아닙니다.유형 수준에서 고유한 주석을 만들고 복잡한 검증을 수행할 수 있습니다(아래 예 참조).Jerome처럼 다른 유형의 검증(Spring 및 JSR-303)을 혼합할 필요가 없기 때문에 이 방법을 선호합니다.또, 이 검증자는 「스프링 대응」이기 때문에, @Inject/@ 를 사용할 수 있습니다.자동 배선을 개봉합니다.
커스텀 오브젝트 검증의 예:
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { YourCustomObjectValidator.class })
public @interface YourCustomObjectValid {
String message() default "{YourCustomObjectValid.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class YourCustomObjectValidator implements ConstraintValidator<YourCustomObjectValid, YourCustomObject> {
@Override
public void initialize(YourCustomObjectValid constraintAnnotation) { }
@Override
public boolean isValid(YourCustomObject value, ConstraintValidatorContext context) {
// Validate your complex logic
// Mark field with error
ConstraintViolationBuilder cvb = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate());
cvb.addNode(someField).addConstraintViolation();
return true;
}
}
@YourCustomObjectValid
public YourCustomObject {
}
일반 필드 동일성의 예:
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { FieldsEqualityValidator.class })
public @interface FieldsEquality {
String message() default "{FieldsEquality.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Name of the first field that will be compared.
*
* @return name
*/
String firstFieldName();
/**
* Name of the second field that will be compared.
*
* @return name
*/
String secondFieldName();
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface List {
FieldsEquality[] value();
}
}
import java.lang.reflect.Field;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
public class FieldsEqualityValidator implements ConstraintValidator<FieldsEquality, Object> {
private static final Logger log = LoggerFactory.getLogger(FieldsEqualityValidator.class);
private String firstFieldName;
private String secondFieldName;
@Override
public void initialize(FieldsEquality constraintAnnotation) {
firstFieldName = constraintAnnotation.firstFieldName();
secondFieldName = constraintAnnotation.secondFieldName();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null)
return true;
try {
Class<?> clazz = value.getClass();
Field firstField = ReflectionUtils.findField(clazz, firstFieldName);
firstField.setAccessible(true);
Object first = firstField.get(value);
Field secondField = ReflectionUtils.findField(clazz, secondFieldName);
secondField.setAccessible(true);
Object second = secondField.get(value);
if (first != null && second != null && !first.equals(second)) {
ConstraintViolationBuilder cvb = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate());
cvb.addNode(firstFieldName).addConstraintViolation();
ConstraintViolationBuilder cvb = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate());
cvb.addNode(someField).addConstraintViolation(secondFieldName);
return false;
}
} catch (Exception e) {
log.error("Cannot validate fileds equality in '" + value + "'!", e);
return false;
}
return true;
}
}
@FieldsEquality(firstFieldName = "password", secondFieldName = "confirmPassword")
public class NewUserForm {
private String password;
private String confirmPassword;
}
다른 메서드 핸들러의 에러 처리 로직이 같은 경우는, 다음의 코드 패턴을 가지는 다수의 핸들러가 됩니다.
if (validation.hasErrors()) {
// do error handling
}
else {
// do the actual business logic
}
RESTful 서비스를 만들고 있으며 반환하고 싶다고 가정합니다.400 Bad Request
모든 검증 오류 사례에 대한 오류 메시지와 함께 표시됩니다.그러면 검증이 필요한 모든 REST 엔드포인트에 대해 오류 처리 부분이 동일합니다.모든 핸들러에서 동일한 논리를 반복하는 것은 그다지 건조하지 않습니다.
이 문제를 해결할 수 있는 한 가지 방법은 즉시 폐기하는 것입니다.BindingResult
To-Be-Validated bean이 끝날 때마다.핸들러는 다음과 같습니다.
@RequestMapping(...)
public Something doStuff(@Valid Somebean bean) {
// do the actual business logic
// Just the else part!
}
이 방법으로 바인드된 콩이 유효하지 않은 경우,MethodArgumentNotValidException
봄에 의해 던져질 것이다.를 정의할 수 있습니다.ControllerAdvice
다음 에러 처리 로직을 사용하여 이 예외를 처리합니다.
@ControllerAdvice
public class ErrorHandlingControllerAdvice {
@ExceptionHandler(MethodArgumentNotValidException.class)
public SomeErrorBean handleValidationError(MethodArgumentNotValidException ex) {
// do error handling
// Just the if part!
}
}
아직 기초가 되는 것을 조사할 수 있습니다.BindingResult
사용.getBindingResult
의 방법MethodArgumentNotValidException
.
Spring MVC 검증의 전체 예 찾기
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.technicalkeeda.bean.Login;
public class LoginValidator implements Validator {
public boolean supports(Class aClass) {
return Login.class.equals(aClass);
}
public void validate(Object obj, Errors errors) {
Login login = (Login) obj;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName",
"username.required", "Required field");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userPassword",
"userpassword.required", "Required field");
}
}
public class LoginController extends SimpleFormController {
private LoginService loginService;
public LoginController() {
setCommandClass(Login.class);
setCommandName("login");
}
public void setLoginService(LoginService loginService) {
this.loginService = loginService;
}
@Override
protected ModelAndView onSubmit(Object command) throws Exception {
Login login = (Login) command;
loginService.add(login);
return new ModelAndView("loginsucess", "login", login);
}
}
이 콩을 컨피규레이션클래스에 넣습니다.
@Bean
public Validator localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
그리고 나서
<T> BindingResult validate(T t) {
DataBinder binder = new DataBinder(t);
binder.setValidator(validator);
binder.validate();
return binder.getBindingResult();
}
수동으로 콩을 검증할 수 있습니다.그러면 BindingResult에서 모든 결과를 얻을 수 있으며 BindingResult에서 결과를 가져올 수 있습니다.
언급URL : https://stackoverflow.com/questions/12146298/spring-mvc-how-to-perform-validation
'programing' 카테고리의 다른 글
fork() 브랜치가 예상보다 많습니까? (0) | 2022.07.11 |
---|---|
부팅 시 지정된 브라우저 URL로 리디렉션 (0) | 2022.07.11 |
vuejs2/vuex: 돌연변이에 대한 다중 구독을 방지하는 방법 (0) | 2022.07.06 |
Vue 전환을 사용하여 div를 확장 및 축소하는 방법 (0) | 2022.07.06 |
사용자가 Vue JS에서 탐색 컨테이너 외부를 클릭할 때 탐색을 닫거나 전환하는 방법 (0) | 2022.07.06 |