복붙노트

[SPRING] Spring 유효성 검사, PropertyEditor가 특정 오류 메시지를 생성하는 방법

SPRING

Spring 유효성 검사, PropertyEditor가 특정 오류 메시지를 생성하는 방법

폼 입력과 검증을 위해 Spring을 사용하고 있습니다. 양식 컨트롤러의 명령은 편집중인 모델을 포함합니다. 모델의 속성 중 일부는 사용자 정의 유형입니다. 예를 들어 개인의 사회 보장 번호는 맞춤형 SSN 유형입니다.

public class Person {
    public String getName() {...}
    public void setName(String name) {...}
    public SSN getSocialSecurtyNumber() {...}
    public void setSocialSecurtyNumber(SSN ssn) {...}
}

스프링 폼 편집자에서 래핑하기 :

public class EditPersonCommand {
    public Person getPerson() {...}
    public void setPerson(Person person) {...}
}

Spring은 텍스트를 SSN으로 변환하는 방법을 알지 못하기 때문에 양식 편집기의 바인더를 사용하여 고객 편집기를 등록합니다.

public class EditPersonController extends SimpleFormController {
    protected void initBinder(HttpServletRequest req, ServletRequestDataBinder binder) {
        super.initBinder(req, binder);
        binder.registerCustomEditor(SSN.class, "person.ssn", new SsnEditor());
    }
}

SsnEditor는 텍스트를 SSN 객체로 변환 할 수있는 사용자 정의 java.beans.PropertyEditor입니다.

public class SsnEditor extends PropertyEditorSupport {
    public String getAsText() {...} // converts SSN to text
    public void setAsText(String str) {
        // converts text to SSN
        // throws IllegalArgumentException for invalid text
    }
}

setAsText가 유효하지 않아 SSN으로 변환 할 수없는 텍스트를 발견하면 (PropertyEditor setAsText의 스펙에 따라) IllegalArgumentException을 발생시킵니다. 내가 가지고있는 문제는 Spring Validator가 호출되기 전에 PropertyEditor.setAsText ()를 통한 텍스트 변환이 발생한다는 것입니다. setAsText가 IllegalArgumentException을 throw하면 Spring은 errors.properties에 정의 된 일반 오류 메시지 만 표시합니다. 내가 원하는 것은 입력 된 SSN이 유효하지 않은 정확한 이유에 따라 달라지는 특정 오류 메시지입니다. PropertyEditor.setAsText ()는 이유를 판별합니다. 나는 오류 이유 텍스트를 IllegalArgumentException의 텍스트에 포함 시키려고 시도했지만 Spring은 일반 오류로 취급합니다.

이것에 대한 해결책이 있습니까? 반복하길 원한다면 PropertyEditor에 의해 생성 된 특정 오류 메시지가 Spring 폼의 오류 메시지에 나타납니다. 내가 생각할 수있는 유일한 대안은 명령에 SSN을 텍스트로 저장하고 유효성 검사기에서 유효성 검사를 수행하는 것입니다. SSN 객체 변환 텍스트는 양식의 onSubmit에서 발생합니다. 이것은 내 양식 (및 모델)이 많은 특성을 가지므로 그리 바람직하지 않습니다. 모든 모델 속성을 텍스트 필드로 갖는 명령을 작성하고 유지 관리하고 싶지는 않습니다.

위의 코드는 단지 예일뿐입니다. 실제 코드는 Person / SSN이 아니므로 "SSN을 텍스트로 저장하지 않는 이유는 무엇입니까?"라고 대답 할 필요가 없습니다.

해결법

  1. ==============================

    1.바인더에서 검증을하려고합니다. 그것은 바인더의 목적이 아닙니다. 바인더는 요청 매개 변수를 뒷받침 객체에 바인딩하기로되어 있습니다. 속성 편집기는 문자열을 객체로 변환하고 그 반대로 문자열을 다른 것으로 변환합니다.

    바인더에서 검증을하려고합니다. 그것은 바인더의 목적이 아닙니다. 바인더는 요청 매개 변수를 뒷받침 객체에 바인딩하기로되어 있습니다. 속성 편집기는 문자열을 객체로 변환하고 그 반대로 문자열을 다른 것으로 변환합니다.

    즉, 문자열을 객체로 또는 그 반대로 변환하는 것 이상의 기능을 수행하지 않는 객체에 기능을 구형 화하려고 시도하는 우려의 분리를 고려해야합니다.

    SSN 객체를 쉽게 바인딩 할 수있는 유효성 검사 가능한 여러 필드 (String 객체, Dates와 같은 기본 객체 등)로 분리하는 것이 좋습니다. 이렇게하면 바인딩 후 유효성 검사기를 사용하여 SSN이 올바른지 확인하거나 직접 오류를 설정할 수 있습니다. 속성 편집기를 사용하면 IllegalArgumentException이 발생하고, Spring은 그 문자열이 예상 유형과 일치하지 않으므로이를 형식 불일치 오류로 변환합니다. 그것이 전부입니다. 유효성 검사기는 다른 한편으로는 이것을 할 수 있습니다. 스프링 바인딩 태그를 사용하여 SSN 인스턴스가 채워지는 한 중첩 된 필드에 바인딩 할 수 있습니다. 먼저 new ()를 사용하여 초기화해야합니다. 예를 들면 :

    <spring:bind path="ssn.firstNestedField">...</spring:bind>
    

    그러나이 경로에서 계속 유지하려면 속성 편집기에 오류 목록을 보관하십시오. IllegalArgumentException을 발생시키고 목록에 추가 한 다음 IllegalArgumentException을 throw합니다 (필요한 경우 catch 및 다시 throw). 바인딩과 동일한 스레드에서 속성 편집기를 구성 할 수 있으므로 속성 편집기 기본 동작을 재정의하면 스레드 세이프가됩니다. 바인딩을 수행하는 데 사용하는 후크를 찾아서 재정의해야합니다. 동일한 속성 편집기를 사용하십시오 (같은 방법을 제외하고 편집기에 대한 참조를 유지할 수있는 것을 제외하고) 이제 바인딩을 끝내고 공용 접근자를 제공하면 편집기에서 목록을 검색하여 등록 할 수 있습니다 . 목록이 검색되면이를 처리하고 이에 따라 오류를 추가 할 수 있습니다.

  2. ==============================

    2.말한대로 :

    말한대로 :

    배후에서 Spring MVC는 BindingErrorProcessor 전략을 사용하여 누락 된 필드 오류를 처리하고 PropertyAccessException을 FieldError로 변환합니다. 따라서 기본 스프링 MVC BindingErrorProcessor 전략을 재정의하려면 다음에 따라 BindingErrorProcessor 전략을 제공해야합니다.

    public class CustomBindingErrorProcessor implements DefaultBindingErrorProcessor {
    
        public void processMissingFieldError(String missingField, BindException errors) {
            super.processMissingFieldError(missingField, errors);
        }
    
        public void processPropertyAccessException(PropertyAccessException accessException, BindException errors) {
            if(accessException.getCause() instanceof IllegalArgumentException)
                errors.rejectValue(accessException.getPropertyChangeEvent().getPropertyName(), "<SOME_SPECIFIC_CODE_IF_YOU_WANT>", accessException.getCause().getMessage());
            else
                defaultSpringBindingErrorProcessor.processPropertyAccessException(accessException, errors);
        }
    
    }
    

    테스트를 위해서 다음과 같이 해보 죠.

    protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
        binder.registerCustomEditor(SSN.class, new PropertyEditorSupport() {
    
            public String getAsText() {
                if(getValue() == null)
                    return null;
    
                return ((SSN) getValue()).toString();
            }
    
            public void setAsText(String value) throws IllegalArgumentException {
                if(StringUtils.isBlank(value))
                    return;
    
                boolean somethingGoesWrong = true;
                if(somethingGoesWrong)
                    throw new IllegalArgumentException("Something goes wrong!");
            }
    
        });
    }
    

    이제 우리의 Test 클래스

    public class PersonControllerTest {
    
        private PersonController personController;
        private MockHttpServletRequest request;
    
        @BeforeMethod
        public void setUp() {
            personController = new PersonController();
            personController.setCommandName("command");
            personController.setCommandClass(Person.class);
            personController.setBindingErrorProcessor(new CustomBindingErrorProcessor());
    
            request = new MockHttpServletRequest();
            request.setMethod("POST");
            request.addParameter("ssn", "somethingGoesWrong");
        }
    
        @Test
        public void done() {
            ModelAndView mav = personController.handleRequest(request, new MockHttpServletResponse());
    
            BindingResult bindingResult = (BindingResult) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + "command");
    
            FieldError fieldError = bindingResult.getFieldError("ssn");
    
            Assert.assertEquals(fieldError.getMessage(), "Something goes wrong!");
        }
    
    }
    

    문안 인사,

  3. ==============================

    3.@Arthur Ronald의 답변에 따라 다음과 같이 구현했습니다.

    @Arthur Ronald의 답변에 따라 다음과 같이 구현했습니다.

    컨트롤러에서 :

    setBindingErrorProcessor(new CustomBindingErrorProcessor());
    

    그리고 바인딩 오류 처리기 클래스 :

    public class CustomBindingErrorProcessor extends DefaultBindingErrorProcessor {
    
        public void processPropertyAccessException(PropertyAccessException accessException, 
                                                   BindingResult bindingResult) {
    
            if(accessException.getCause() instanceof IllegalArgumentException){
    
                String fieldName = accessException.getPropertyChangeEvent().getPropertyName();
                String exceptionError = accessException.getCause().getMessage();
    
                FieldError fieldError = new FieldError(fieldName,
                                                       "BINDING_ERROR", 
                                                       fieldName + ": " + exceptionError);
    
                bindingResult.addError(fieldError);
            }else{
                super.processPropertyAccessException(accessException, bindingResult);
            }
    
        }
    
    }        
    

    따라서 프로세서 메서드의 서명은이 버전에서 BindException 대신 BindingResult를 사용합니다.

  4. ==============================

    4.이 말은 폼에 String을 입력하면 정수 속성 값을 바인딩 할 수 없을 때 NumberFormatExceptions와 관련된 문제와 유사하게 들립니다. 양식의 오류 메시지는 해당 예외에 대한 일반 메시지입니다.

    이 말은 폼에 String을 입력하면 정수 속성 값을 바인딩 할 수 없을 때 NumberFormatExceptions와 관련된 문제와 유사하게 들립니다. 양식의 오류 메시지는 해당 예외에 대한 일반 메시지입니다.

    솔루션은 내 응용 프로그램 컨텍스트에 내 자신의 메시지 리소스 번들을 추가하고 해당 속성의 형식 불일치에 대한 자체 오류 메시지를 추가하는 것이 었습니다. 아마도 특정 필드에서 IllegalArgumentExceptions에 대해 비슷한 작업을 수행 할 수 있습니다.

  5. ==============================

    5.나는 당신이 당신의 메시지 소스에 이것을 넣으려고 노력할 수 있다고 믿는다.

    나는 당신이 당신의 메시지 소스에 이것을 넣으려고 노력할 수 있다고 믿는다.

    typeMismatch.person.ssn = 잘못된 SSN 형식

  6. from https://stackoverflow.com/questions/691790/spring-validation-how-to-have-propertyeditor-generate-specific-error-message by cc-by-sa and MIT license