>source

사용자가 이름과 연도를 업데이트 할 수있는 다음 코드가 있습니다.

모델

@Entity
public class Person implements Serializable{
    private static final long serialVersionUID = 1L;
    private String name;
    private int year;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getYear() {
        return year;
    }
    public void setYear(int year) {
        this.year = year;
    }
}

제어 장치

@RequestMapping(value = "/person", method = RequestMethod.POST)
public ModelAndView editPerson(@ModelAttribute Person person)
{
    //perform operations
    //display results
    ModelAndView modelAndView = new ModelAndView("Person.html");
    modelAndView.addObject("personBind", person);
        
    return modelAndView;
}

전망

<form action="person" method="post" th:object="${personBind}">
Name:
<input type="text" th:field="*{name}" />
Year:   
<input type="text" th:field="*{year}" />

이제 연도 필드에서 몇 가지 유효성 검사를 수행하고 싶습니다. 예를 들어 사용자가 해당 필드에 숫자 대신 문자열을 입력하면 정수 속성에 문자열을 설정할 수 없기 때문에 현재 코드에서 예외가 발생합니다.

그렇다면 입력을 검증하는 방법은 무엇입니까? @Valid를 사용하고 싶지 않습니다. 사용자 지정 유효성 검사를 수행하고 싶습니다.

이 작업을 수행하는 방법은 모델 (getter/setter)에서 연도 필드의 문자열 버전을 만드는 것입니다. 그런 다음보기에서 해당 strYear를 사용하고 컨트롤러에서 유효성 검사를 수행합니다. 아래의 업데이트 된 코드와 같습니다. 이것이 올바른 접근 방식입니까, 아니면 더 나은 방법이 있습니까? 유효성을 검사해야하는 모든 숫자 속성에 대해 getter/setter의 문자열 버전을 만드는 것이 올바른지 확실하지 않기 때문에 묻습니다. 중복이 많은 것 같습니다.

모델

@Entity
public class Person implements Serializable{
    private static final long serialVersionUID = 1L;
    private String name;
    private int year;
    @Transient
    private String strYear;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getYear() {
        return year;
    }
    public void setYear(int year) {
        this.year = year;
    }    
    
    public String getStrYear() {
        return strYear;
    }
    public void setStrYear(String strYear) {
        this.strYear = strYear;
    }
}

제어 장치

@RequestMapping(value = "/person", method = RequestMethod.POST)
public ModelAndView editPerson(@ModelAttribute Person person)
{
    //validate
    boolean valid = Validate(Person.getStrYear());
    if(valid==true)
    {
      Person.setYear(Integer.ParseInt(Person.getStrYear()));
      //save edit
    }
    else
    {//display validation messages}
    //display results
    ModelAndView modelAndView = new ModelAndView("Person.html");
    modelAndView.addObject("personBind", person);
        
    return modelAndView;
}

전망

<form action="person" method="post" th:object="${personBind}">
Name:
<input type="text" th:field="*{name}" />
Year:   
<input type="text" th:field="*{strYear}" />

  • 답변 # 1

    유효성 검사는 까다 롭고 어려울 수 있으며 유효성 검사를 수행 할 때 고려해야 할 몇 가지 사항이 있습니다.

    유효성 검사 고려 사항

    MVC의 모델 (모델,보기, 컨트롤러)은 도메인 모델과 동일하지 않으며 일반적으로 동일하지 않아야합니다. @ wim-deblauwe의 의견과 아래 Q&A 섹션을 참조하세요.

    종종 사용자 인터페이스에 표시되는 내용은 도메인 모델 내에서 사용할 수있는 내용과 다릅니다.

    @Valid 주석을 도메인 모델에 배치하면 도메인 모델이 사용되는 모든 형식에서 동일한 @Valid 규칙이 적용됩니다. 이것은 항상 사실이 아닙니다. 참고 : 이것은 매우 간단한 CRUD (생성, 읽기, 업데이트, 삭제) 애플리케이션에는 적용되지 않을 수 있지만 일반적으로 대부분의 애플리케이션은 순수한 CRUD보다 더 정교합니다.

    Spring이 양식 제출 중에 값을 자동 설정하는 방식으로 인해 실제 도메인 모델 객체를 양식 백업 객체로 사용하는 데 심각한 보안 문제가 있습니다. 예를 들어, 암호 필드가있는 사용자 개체를 양식 백업 개체로 사용하는 경우 브라우저 개발자 도구에서 양식을 조작하여 암호 필드에 대한 새 값을 보낼 수 있으며 이제 새 값이 유지됩니다.

    html 양식을 통해 입력 된 모든 데이터는 나중에 실제 데이터 유형 (Integer, Double, Enumeration 등)으로 변환되어야하는 실제로 문자열 데이터입니다.

    제 생각에는 다른 시간적 순서로 발생해야하는 여러 유형의 유효성 검사가 있습니다.

    유형 검사 (Integer, Double, Enumeration 등 ...), 유효한 값 범위, 마지막으로 지속성 검사 (고유성, 이전 지속 값 등) 전에 필요한 검사가 수행됩니다.

    시간 수준에 오류가 있으면 나중에 아무것도 확인하지 마십시오.

    이렇게하면 최종 사용자가 동일한 오류 메시지에서 전화 번호가 필요함, 전화 번호가 숫자가 아님, 전화 번호 형식이 올바르지 않음 등의 오류가 발생하지 않습니다.

    유효성 검사기간에 시간적 결합이 없어야합니다. 즉, 필드가 선택 사항이면 '데이터 유형'유효성 검사기가 값이없는 경우 유효성 검사에 실패해서는 안됩니다. 아래 유효성 검사기를 참조하십시오.

    도메인 개체/비즈니스 개체 :

    @Entity
    public class Person {
        private String identifier;
        private String name;
        private int year;
        public String getIdentifier() {
            return identifier;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getYear() {
            return year;
        }
        public void setYear(int year) {
            this.year = year;
        }    
    }
    
    

    Spring MVC 컨트롤러를 통해 html 양식을 채우기 위해 해당 양식을 나타내는 특정 객체를 만듭니다. 여기에는 모든 유효성 검사 규칙도 포함됩니다.

    @GroupSequence({Required.class, Type.class, Data.class, Persistence.class, CreateOrUpdatePersonForm.class})
    public class CreateOrUpdatePersonForm {
        @NotBlank(groups = Required.class, message = "Name is required.")
        private String name;
        @NotBlank(groups = Required.class, message = "Year is required.")
        @ValidInteger(groups = Type.class, message = "Year must be a number.")
        @ValidDate(groups = Data.class, message = "Year must be formatted yyyy.")
        private String year;
        public CreateOrUpdatePersonForm(Person person) {
            this.name = person.getName();
            this.year = Integer.valueOf(person.getYear);
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getYearStr() {
            this.year;
        }
        public void setYearStr(String year) {
            this.year = year;
        }
        public int getYear() {
            return Integer.valueOf(this.year);
        }
    }
    
    

    그런 다음 컨트롤러에서 새 CreateOrUpdatePersonForm 개체를 사용합니다.

    @Controller
    public class PersonController {
        ...
        @ModelAttribute("command")
        public CreateOrUpdatePersonForm setupCommand(@RequestParam("identifier") Person person) {
            return new CreateOrUpdatePersonForm(person);
        }
        //@PreAuthorize("hasRole('ADMIN')")
        @RequestMapping(value = "/person/{person}/form.html", method = RequestMethod.GET)
        public ModelAndView getForm(@RequestParam("person") Person person) {
            return new ModelAndView("/form/person");
        }
        //@PreAuthorize("hasRole('ADMIN')")
        @RequestMapping(value = "/person/{person}/form.html", method = RequestMethod.POST)
        public ModelAndView postForm(@RequestParam("person") Person person, @ModelAttribute("command") @Valid CreateOrUpdatePersonForm form,
                                     BindingResult bindingResult, RedirectAttributes redirectAttributes) {
            ModelAndView modelAndView;
            if (bindingResult.hasErrors()) {
                modelAndView = new ModelAndView("/form/person");
            } else {
                this.personService.updatePerson(person.getIdentifier(), form);
                redirectAttributes.addFlashAttribute("successMessage", "Person updated.");
                modelAndView = new ModelAndView("redirect:/person/" + person.getIdentifier() + ".html");
            }
            return modelAndView;
        }
    }
    
    

    @ValidInteger 및 @ValidDate는 우리가 직접 작성한 유효성 검사기입니다.

    @ValidInteger :

    public class ValidIntegerValidator implements ConstraintValidator<ValidInteger, String> {
        @Override
        public void initialize(ValidInteger annotation) {
        }
        @Override
        public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
            boolean valid = true;
            if (StringUtils.hasText(value)) {
                try {
                    Integer.parseInteger
    (value);
                } catch (NumberFormatException e) {
                    valid = false;
                }
            }
            return valid;
        }
    }
    @Target({METHOD, FIELD})
    @Retention(RUNTIME)
    @Constraint(validatedBy = ValidIntegerValidator.class)
    @Documented
    public @interface ValidInteger {
        String message() default "{package.valid.integer}";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    
    

    헉헉

    public class ValidDateValidator implements ConstraintValidator<ValidDate, String> {
        private String format;
        @Override
        public void initialize(ValidDate annotation) {
            this.format = annotation.format();
        }
        @Override
        public boolean isValid(String inputDate, ConstraintValidatorContext constraintValidatorContext) {
            boolean valid = true;
            if (StringUtils.hasText(inputDate)) {
                SimpleDateFormat dateFormat = new SimpleDateFormat(format);
                dateFormat.setLenient(false);
                try {
                    dateFormat.parse(inputDate);
                } catch (ParseException e) {
                    valid = false;
                }
            }
            return valid;
        }
    }
    @Target({METHOD, FIELD})
    @Retention(RUNTIME)
    @Constraint(validatedBy = ValidDateValidator.class)
    @Documented
    public @interface ValidDate {
        String message() default "{package.dateformat}";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
        String format();
    }
    
    

    그런 다음보기 jsp 또는 템플릿에서 오류가있는 경우 표시해야합니다.

    <html>
        ...
        <body>
            <common:form-errors modelAttribute="command"/>
            ...
        </body>
    </html>
    
    

    두 필드를 함께 비교하거나 개인 이름이 고유한지 확인하기 위해 지속성 레이어에 액세스하는 것과 같이 유효성 검사를 처리해야 할 것이 훨씬 더 많지만, 더 많은 설명이 필요합니다.

    Q&A

    Q : 도메인 모델을 MVC 모델로 사용하지 않는 것에 대한 생각을 설명하는 링크를 제공 할 수 있습니까?

    A : 물론입니다. 엔터티 VS 도메인 모델 VS 뷰 모델

    요약 : 도메인 모델과 MVC 모델에 서로 다른 개체를 사용하는 것은 애플리케이션 계층 간의 결합을 줄이고 두 계층의 변경으로부터 UI와 도메인 모델을 보호하기 때문입니다.

    기타 고려 사항

    데이터 유효성 검사는 애플리케이션의 모든 진입 점 (UI, API 및 읽어 들인 외부 시스템 또는 파일)에서 발생해야합니다.

    API는 컴퓨터 용 UI 일 뿐이며 휴먼 UI와 동일한 규칙을 따라야합니다.

    인터넷에서 데이터를 받아들이는 것은 위험합니다. 덜 제한적인 것보다 더 제한적인 것이 좋습니다. 여기에는 이상한 문자가 없는지 확인하는 것도 포함됩니다.기침Microsoft의 1252 문자 인코딩기침, SQL 주입, JavaScript 주입. 데이터베이스가 유니 코드 용으로 설정되어 있는지 확인하고 언어에 따라 512 자로 설정된 열이 코드 포인트로 인해 실제로 256 자만 처리 할 수 ​​있음을 이해합니다.

  • 이전 web - Django 앱에 정적 파일을 업로드하는 데 문제가 있습니다 내가 뭘 놓치고 있는지 이해가 안 돼
  • 다음 wordpress - 주문 관리 패널에서 최소 수량 변경 방법