Spring MVC: วิธีการตรวจสอบความถูกต้อง?


156

ฉันต้องการทราบว่าวิธีใดที่ดีที่สุดและดีที่สุดในการตรวจสอบความถูกต้องของแบบฟอร์มอินพุตของผู้ใช้ org.springframework.validation.Validatorฉันได้เห็นนักพัฒนาบางส่วนดำเนินการ คำถามเกี่ยวกับที่: ฉันเห็นมันตรวจสอบชั้นเรียน ชั้นต้องกรอกข้อมูลด้วยตนเองด้วยค่าจากอินพุตของผู้ใช้แล้วส่งต่อไปยังตัวตรวจสอบความถูกต้องหรือไม่?

ฉันสับสนเกี่ยวกับวิธีที่สะอาดและดีที่สุดในการตรวจสอบอินพุตของผู้ใช้ ฉันรู้เกี่ยวกับวิธีการแบบดั้งเดิมของการใช้request.getParameter()แล้วด้วยตนเองตรวจสอบแต่ฉันไม่ต้องการที่จะทำการตรวจสอบทั้งหมดที่อยู่ในของฉันnulls Controllerคำแนะนำที่ดีเกี่ยวกับเรื่องนี้จะได้รับการชื่นชมอย่างมาก ฉันไม่ได้ใช้ไฮเบอร์เนตในแอปพลิเคชันนี้


คำตอบ:


322

ด้วย 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;

    ...

}

คุณจะต้องใช้ผู้ให้บริการ JSR-303 ในไลบรารีของคุณเช่นHibernate Validatorซึ่งเป็นการนำไปใช้อ้างอิง (ไลบรารีนี้ไม่มีส่วนเกี่ยวข้องกับฐานข้อมูลและการทำแผนที่เชิงสัมพันธ์ แต่จะตรวจสอบความถูกต้อง :-)

จากนั้นในตัวควบคุมของคุณคุณจะมีสิ่งที่ชอบ:

@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 () จะเป็นจริง

วิธีที่ 2:ถ้าคุณมีการตรวจสอบที่ซับซ้อน (เช่นตรรกะการตรวจสอบความถูกต้องทางธุรกิจขนาดใหญ่การตรวจสอบความถูกต้องแบบมีเงื่อนไขในหลาย ๆ ฟิลด์) หรือด้วยเหตุผลบางอย่างที่คุณไม่สามารถใช้วิธีที่ 1 ให้ใช้การตรวจสอบด้วยตนเอง เป็นการดีที่จะแยกรหัสของคอนโทรลเลอร์ออกจากตรรกะการตรวจสอบความถูกต้อง อย่าสร้างคลาสการตรวจสอบความถูกต้องตั้งแต่เริ่มต้นสปริงมีorg.springframework.validation.Validatorอินเทอร์เฟซที่ใช้งานง่าย(ตั้งแต่สปริง 2)

สมมติว่าคุณมี

public class User {

    private String name;

    private Integer birthYear;
    private User responsibleUser;
    ...

}

และคุณต้องการทำการตรวจสอบที่ "ซับซ้อน" บางอย่างเช่น: หากอายุของผู้ใช้ต่ำกว่า 18 ผู้รับผิดชอบจะต้องไม่เป็นโมฆะและอายุของผู้รับผิดชอบจะต้องมีอายุมากกว่า 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 () จะเป็นจริง

หมายเหตุ: คุณยังสามารถตั้งค่าเครื่องมือตรวจสอบความถูกต้องในวิธีการ @InitBinder ของคอนโทรลเลอร์ด้วย "binder.setValidator (... )" (ซึ่งในกรณีนี้การผสมวิธี 1 และ 2 จะไม่สามารถทำได้เนื่องจากคุณแทนที่ค่าเริ่มต้น ตรวจสอบ) หรือคุณสามารถยกตัวอย่างในตัวสร้างเริ่มต้นของตัวควบคุม หรือมี @ Component / @ Service UserValidator ที่คุณฉีด (@Autowired) ในคอนโทรลเลอร์ของคุณ: มีประโยชน์มากเพราะเครื่องมือตรวจสอบส่วนใหญ่เป็น singletons + การเยาะเย้ยการทดสอบหน่วยกลายเป็นเรื่องง่ายขึ้น + เครื่องมือตรวจสอบของคุณสามารถเรียกคอมโพเนนต์สปริงอื่น ๆ

วิธีที่ 3: ทำไมไม่ใช้ทั้งสองวิธีร่วมกัน ตรวจสอบสิ่งที่ง่ายเช่นแอตทริบิวต์ "ชื่อ" ด้วยคำอธิบายประกอบ (มันรวดเร็วในการทำกระชับและอ่านง่ายขึ้น) เก็บการตรวจสอบความถูกต้องอย่างหนักสำหรับผู้ตรวจสอบความถูกต้อง (เมื่อต้องใช้เวลาหลายชั่วโมงในการเขียนบันทึกย่อการตรวจสอบความซับซ้อนที่กำหนดเองหรือเมื่อไม่สามารถใช้คำอธิบายประกอบได้) ฉันทำสิ่งนี้ในโครงการก่อนหน้ามันใช้งานได้อย่างมีเสน่ห์รวดเร็วและง่ายดาย

คำเตือน:คุณจะต้องไม่ผิดพลาดในการตรวจสอบการจัดการสำหรับการจัดการข้อยกเว้น อ่านโพสต์นี้เพื่อทราบเมื่อใช้

การอ้างอิง:


คุณบอกได้ไหมว่า servlet.xml ของฉันควรมีอะไรสำหรับการกำหนดค่านี้ ฉันต้องการส่งข้อผิดพลาดกลับไปที่มุมมอง
devdar

@dev_darin คุณหมายถึง config สำหรับการตรวจสอบ JSR-303 หรือไม่
เจอโรม Dalbert

2
@dev_marin สำหรับการตรวจสอบความถูกต้องใน Spring 3.x + ไม่มีอะไรพิเศษใน "servlet.xml" หรือ "[servlet-name] -servlet.xml คุณเพียงแค่ต้องการ jar hibernate-validator ในไลบรารีโครงการของคุณ (หรือผ่าน Maven) ทั้งหมดนี้ควรใช้งานได้คำเตือนหากคุณใช้วิธีที่ 3: โดยค่าเริ่มต้นตัวควบคุมแต่ละตัวจะสามารถเข้าถึงตัวตรวจสอบความถูกต้อง JSR-303 ได้ดังนั้นโปรดระวังอย่าเขียนทับด้วย "setValidator" หากคุณต้องการเพิ่มตัวตรวจสอบความถูกต้องเอง เพียงแค่ยกตัวอย่างและใช้งานหรือฉีด (ถ้าเป็นส่วนประกอบของ Spring) หากคุณยังมีปัญหาหลังจากตรวจสอบ Google และ Spring doc คุณควรโพสต์คำถามใหม่
Jerome Dalbert

2
สำหรับการใช้วิธีผสม 1 และ 2 จะมีวิธีใช้ @InitBinder แทนที่จะเป็น "binder.setValidator (... )" สามารถใช้ "binder.addValidators (... )"
jasonfungsing

1
แก้ไขให้ฉันถ้าฉันผิด แต่คุณสามารถผสมการตรวจสอบผ่านบันทึกย่อ JSR-303 (วิธีที่ 1) และการตรวจสอบที่กำหนดเอง (วิธีที่ 2) เมื่อใช้คำอธิบายประกอบ @InitBinder เพียงใช้ binder.addValidators (userValidator) แทน binder.setValidator (userValidator) และทั้งสองวิธีการตรวจสอบจะมีผล
SebastianRiemer

31

มีสองวิธีในการตรวจสอบอินพุตของผู้ใช้: คำอธิบายประกอบและโดยการสืบทอดคลาส 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คำอธิบายประกอบ คุณสามารถใช้ Hibernate ได้ org.hibernate.validator.constraintsแต่ดูเหมือนคุณจะไม่ได้ใช้ Hibernate

อีกวิธีหนึ่งถ้าคุณใช้เครื่องมือตรวจสอบความถูกต้องของ Spring คุณจะต้องสร้างคลาสดังนี้

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());
}

ยังเห็นเอกสารฤดูใบไม้ผลิ

หวังว่าจะช่วย


เมื่อใช้ตัวตรวจสอบความถูกต้องของ Spring ฉันต้องตั้งค่า pojo จากคอนโทรลเลอร์แล้วตรวจสอบความถูกต้องหรือไม่
devdar

ฉันไม่แน่ใจว่าฉันเข้าใจคำถามของคุณ หากคุณเห็นข้อมูลโค้ดคอนโทรลเลอร์ Spring จะเชื่อมโยงแบบฟอร์มที่ส่งไปยังFooพารามิเตอร์ในวิธีการจัดการโดยอัตโนมัติ คุณช่วยอธิบายได้ไหม
stephen.hanson

ตกลงสิ่งที่ฉันพูดคือเมื่อผู้ใช้ส่งผู้ใช้อินพุตคอนโทรลเลอร์รับคำขอ http จากนั้นจะเกิดอะไรขึ้นคือคุณใช้ request.getParameter () เพื่อรับพารามิเตอร์ผู้ใช้ทั้งหมดจากนั้นตั้งค่าใน POJO แล้วส่งผ่าน ชั้นเรียนเพื่อวัตถุการตรวจสอบ คลาสการตรวจสอบจะส่งข้อผิดพลาดกลับไปที่มุมมองพร้อมข้อผิดพลาดหากพบ นี่เป็นวิธีที่มันจะเกิดขึ้น?
devdar

1
มันจะเกิดขึ้นเช่นนี้ แต่มีวิธีที่ง่ายกว่า ... ถ้าคุณใช้ JSP และการส่ง <form: form commandName = "user"> ข้อมูลจะถูกใส่โดยอัตโนมัติในผู้ใช้ @ModelAttribute ("ผู้ใช้") ในคอนโทรลเลอร์ วิธี. ดู doc: static.springsource.org/spring/docs/3.0.x/…
Jerome Dalbert

+1 เพราะนั่นคือตัวอย่างแรกที่ฉันพบซึ่งใช้ @ModelAttribute; โดยไม่ต้องมันไม่มีการกวดวิชาที่ฉันพบว่าทำงานได้
Riccardo Cossu

12

ฉันต้องการขยายคำตอบที่ดีของ Jerome Dalbert ฉันพบว่าง่ายมากในการเขียนเครื่องมือตรวจสอบหมายเหตุประกอบของคุณเองในแบบ JSR-303 คุณไม่ถูก จำกัด ให้มีการตรวจสอบ "หนึ่งช่อง" คุณสามารถสร้างคำอธิบายประกอบของคุณเองในระดับประเภทและมีการตรวจสอบที่ซับซ้อน (ดูตัวอย่างด้านล่าง) ฉันชอบวิธีนี้เพราะฉันไม่ต้องการการตรวจสอบที่แตกต่างหลากหลาย (สปริงและ JSR-303) เหมือนกับเจอโรมทำ ตัวตรวจสอบนี้คือ "การรับรู้ในฤดูใบไม้ผลิ" เพื่อให้คุณสามารถใช้ @ Inject / @ Autowire ได้

ตัวอย่างของการตรวจสอบวัตถุที่กำหนดเอง:

@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;

}

1
ฉันยังสงสัยว่าตัวควบคุมมักจะมีตัวตรวจสอบความถูกต้องหนึ่งตัวและฉันเห็นว่าคุณสามารถใช้ตัวตรวจสอบความถูกต้องหลายตัวได้ แต่ถ้าคุณมีชุดของการตรวจสอบความถูกต้องที่กำหนดไว้สำหรับวัตถุหนึ่ง บันทึกชุดการตรวจสอบความถูกต้องบางอย่างและจำเป็นต้องมีการปรับปรุงการตรวจสอบชุดอื่น มีวิธีการกำหนดค่าคลาส validator เพื่อเก็บการตรวจสอบทั้งหมดขึ้นอยู่กับการดำเนินการหรือคุณจะต้องใช้ตัวตรวจสอบหลายตัว?
devdar

1
คุณยังสามารถใช้วิธีการตรวจสอบคำอธิบายประกอบได้ ดังนั้นคุณสามารถสร้าง "การตรวจสอบโดเมน" ของคุณเองได้ถ้าฉันเข้าใจคำถามของคุณ สำหรับนี้คุณจะต้องระบุในElementType.METHOD @Target
michal.kreuzman

ฉันเข้าใจสิ่งที่คุณพูดคุณสามารถชี้ให้ฉันเป็นตัวอย่างเพื่อภาพที่ชัดเจนยิ่งขึ้น
devdar

4

หากคุณมีข้อผิดพลาดการจัดการตรรกะเดียวกันสำหรับตัวจัดการวิธีการที่แตกต่างกันแล้วคุณจะจบลงด้วยการจัดการจำนวนมากด้วยรูปแบบรหัสต่อไปนี้

if (validation.hasErrors()) {
  // do error handling
}
else {
  // do the actual business logic
}

สมมติว่าคุณกำลังสร้างบริการ RESTful และต้องการกลับมา400 Bad Requestพร้อมกับข้อความแสดงข้อผิดพลาดสำหรับทุกกรณีการตรวจสอบความถูกต้อง จากนั้นส่วนจัดการข้อผิดพลาดจะเหมือนกันสำหรับจุดปลาย REST เดียวที่ต้องการการตรวจสอบความถูกต้อง การทำซ้ำตรรกะที่เหมือนกันมากในตัวจัดการทุก ๆ ตัวนั้นไม่ใช่DRY ish!

วิธีหนึ่งในการแก้ไขปัญหานี้คือการดร็อปทันทีBindingResultหลังจากถั่วTo-Be-Validated แต่ละถั่ว ตอนนี้ผู้จัดการของคุณจะเป็นเช่นนี้:

@RequestMapping(...)
public Something doStuff(@Valid Somebean bean) { 
    // do the actual business logic
    // Just the else part!
}

ด้วยวิธีนี้หากถั่วที่ถูกผูกไว้ไม่ถูกต้องMethodArgumentNotValidExceptionจะถูกโยนโดยสปริง คุณสามารถกำหนด a ControllerAdviceที่จัดการข้อยกเว้นนี้ด้วยตรรกะการจัดการข้อผิดพลาดเดียวกัน:

@ControllerAdvice
public class ErrorHandlingControllerAdvice {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public SomeErrorBean handleValidationError(MethodArgumentNotValidException ex) {
        // do error handling
        // Just the if part!
    }
}

คุณยังสามารถตรวจสอบที่อยู่ภายใต้BindingResultการใช้วิธีการของgetBindingResultMethodArgumentNotValidException


1

ค้นหาตัวอย่างที่สมบูรณ์ของการตรวจสอบความถูกต้องของ 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);
    }
}

0

ใส่ bean นี้ในคลาสการกำหนดค่าของคุณ

 @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 และคุณสามารถดึงข้อมูลได้จากที่นั่น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.