2009-03-27 26 views
13

Tôi đang sử dụng Spring để nhập và xác thực biểu mẫu. Lệnh của bộ điều khiển biểu mẫu chứa mô hình đang được chỉnh sửa. Một số thuộc tính của mô hình là một loại tùy chỉnh. Ví dụ, số an sinh xã hội của một người là một loại SSN tùy chỉnh.Xác thực mùa xuân, cách để PropertyEditor tạo thông báo lỗi cụ thể

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

và gói Person trong một hình thức mùa xuân chỉnh sửa lệnh:

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

Kể từ mùa xuân không biết làm thế nào để chuyển đổi văn bản thành một SSN, tôi đăng ký một biên tập viên của khách hàng với chất kết dính các bộ điều khiển hình thức của:

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

và SsnEditor chỉ là một tùy chỉnh java.beans.PropertyEditor mà có thể chuyển đổi văn bản đến một đối tượng SSN:

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 
    } 
} 

Nếu setAsText gặp văn bản không hợp lệ và không thể chuyển đổi thành SSN, thì nó sẽ ném IllegalArgumentException (theo thông số PropertyEditorsetAsText). Vấn đề tôi gặp phải là việc chuyển đổi văn bản thành đối tượng (thông qua PropertyEditor.setAsText()) diễn ra trước khi trình xác thực Spring của tôi được gọi. Khi setAsText ném IllegalArgumentException, Spring chỉ hiển thị thông báo lỗi chung được xác định trong errors.properties. Những gì tôi muốn là một thông báo lỗi cụ thể phụ thuộc vào lý do chính xác tại sao SSN đã nhập không hợp lệ. PropertyEditor.setAsText() sẽ xác định lý do. Tôi đã thử nhúng các lý do lỗi văn bản trong văn bản của IllegalArgumentException, nhưng mùa xuân chỉ xử lý nó như là một lỗi chung chung.

Có giải pháp cho điều này không? Để lặp lại, những gì tôi muốn là thông báo lỗi cụ thể được tạo bởi PropertyEditor để hiển thị thông báo lỗi trên biểu mẫu Mùa xuân. Cách thay thế duy nhất tôi có thể nghĩ là lưu trữ SSN dưới dạng văn bản trong lệnh và thực hiện xác thực trong trình xác nhận hợp lệ. Văn bản để chuyển đổi đối tượng SSN sẽ diễn ra trong biểu mẫu onSubmit. Điều này là ít mong muốn như hình thức của tôi (và mô hình) có nhiều thuộc tính và tôi không muốn phải tạo và duy trì một lệnh có mỗi và mọi thuộc tính mô hình như là một trường văn bản.

Trên đây chỉ là một ví dụ, mã thực tế của tôi không phải là Person/SSN, do đó không cần phải trả lời với "tại sao không lưu trữ SSN dưới dạng văn bản ..."

+0

@Steve Kuo Đã thêm vào câu trả lời gốc –

Trả lời

6

Bạn đang cố gắng thực hiện xác thực trong một chất kết dính. Đó không phải là mục đích của người đóng sách. Một chất kết dính là nghĩa vụ phải ràng buộc các tham số yêu cầu đối tượng sao lưu của bạn, không có gì hơn. Một trình soạn thảo thuộc tính chuyển đổi các chuỗi thành các đối tượng và ngược lại - nó không được thiết kế để làm bất cứ điều gì khác.

Nói cách khác, bạn cần xem xét việc phân tách mối quan tâm - bạn đang cố gắng đánh thức chức năng thành một đối tượng không bao giờ có ý định làm bất cứ điều gì hơn là chuyển đổi chuỗi thành đối tượng và ngược lại.

Bạn có thể xem xét chia nhỏ đối tượng SSN thành nhiều trường hợp hợp lệ có thể dễ dàng bị ràng buộc (đối tượng String, đối tượng cơ bản như Ngày tháng, v.v.). Bằng cách này, bạn có thể sử dụng trình xác thực sau khi ràng buộc để xác minh rằng SSN là chính xác hoặc bạn có thể đặt trực tiếp lỗi. Với trình soạn thảo thuộc tính, bạn ném một IllegalArgumentException, Spring chuyển đổi nó thành một lỗi không khớp kiểu vì đó là những gì nó - chuỗi không khớp với kiểu được mong đợi. Đó là tất cả những gì nó được. Mặt khác, trình xác nhận có thể thực hiện điều này. Bạn có thể sử dụng thẻ bind xuân để liên kết với các trường lồng nhau, miễn là cá thể SSN được điền - nó phải được khởi tạo bằng new() trước tiên. Ví dụ:

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

Nếu bạn thực sự muốn kiên trì trên con đường này, tuy nhiên, có biên tập tài sản của bạn giữ một danh sách các lỗi - nếu nó là để ném một IllegalArgumentException, thêm nó vào danh sách và sau đó ném IllegalArgumentException (bắt và rethrow nếu cần thiết). Vì bạn có thể xây dựng trình soạn thảo thuộc tính của bạn trong cùng một luồng như ràng buộc, nó sẽ là luồng an toàn nếu bạn chỉ cần ghi đè hành vi mặc định của trình soạn thảo thuộc tính - bạn cần tìm móc nó sử dụng để làm ràng buộc và ghi đè nó - làm cùng trình soạn thảo thuộc tính đăng ký bạn đang làm bây giờ (ngoại trừ trong cùng một phương pháp, để bạn có thể giữ tham chiếu đến trình soạn thảo của mình) và sau đó ở cuối ràng buộc, bạn có thể đăng ký lỗi bằng cách truy xuất danh sách từ trình soạn thảo của bạn nếu bạn cung cấp truy cập công khai . Một khi danh sách được lấy ra, bạn có thể xử lý nó và thêm các lỗi của bạn cho phù hợp.

+3

Tôi sợ điều này và tôi hơi thất vọng với hình thức xử lý của Spring. Từ những gì tôi nghe "phương pháp tiếp cận mùa xuân" chính xác là để thực hiện xác nhận trước khi ràng buộc. Điều này có nghĩa là tạo một lệnh biểu mẫu với các trường chuỗi. –

+0

Tiếp tục từ bình luận trước: Trình xác thực hợp lệ hóa các trường này. Sau đó, onSubmit của bộ điều khiển sẽ chuyển đổi các trường chuỗi thành kiểu chính xác của chúng và đặt giá trị trên mô hình sao lưu. Đây là một nỗi đau vì bây giờ tôi phải tạo lại mỗi trường có thể chỉnh sửa dưới dạng một chuỗi trong lệnh của biểu mẫu. –

+0

Không, bạn xác nhận sau khi bạn ràng buộc - ràng buộc không được xác nhận bất cứ điều gì - nếu bạn muốn điều này, một cách tiếp cận khác là tạo một số loại danh sách lỗi trong đối tượng cụ thể của bạn và lưu trữ các lỗi trong đó khi bạn liên kết và trình duyệt tính hợp lệ kiểm tra danh sách này ... – MetroidFan2002

0

này nghe có vẻ giống như một vấn đề tôi đã có với NumberFormatExceptions khi giá trị cho một thuộc tính số nguyên không thể bị ràng buộc nếu, nói, một String đã được nhập vào biểu mẫu. Thông báo lỗi trên biểu mẫu là một thông báo chung cho ngoại lệ đó.

Giải pháp là thêm gói tài nguyên thư của riêng tôi vào ngữ cảnh ứng dụng của tôi và thêm thông báo lỗi của riêng tôi cho loại không phù hợp trên thuộc tính đó. Có lẽ bạn có thể làm một cái gì đó tương tự cho IllegalArgumentExceptions trên một lĩnh vực cụ thể.

5

Như đã nói:

Những gì tôi muốn là thông điệp lỗi cụ thể được tạo ra bởi PropertyEditor bề mặt để thông báo lỗi về hình thức mùa xuân

Đằng sau hậu trường, sử dụng Spring MVC một chiến lược BindingErrorProcessor để xử lý các lỗi trường bị thiếu và để dịch một PropertyAccessException thành FieldError. Vì vậy, nếu bạn muốn ghi đè mùa xuân mặc định chiến lược MVC BindingErrorProcessor, bạn phải cung cấp một chiến lược BindingErrorProcessor theo:

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

} 

Để kiểm tra, hãy làm như sau

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!"); 
     } 

    }); 
} 

Bây giờ class Test của chúng tôi

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!"); 
    } 

} 

liên quan,

+0

Đối với nó để làm việc cho tôi Tôi đã có BindingResult trên chữ ký phương pháp quá trình của tôi thay vì BindException: 'public void processPropertyAccessException (PropertyAccessException accessException, BindingResult bindingResult)' – carlosHT

0

Tôi tin rằng bạn chỉ có thể tr y để đặt điều này trong nguồn thông điệp của bạn:

typeMismatch.person.ssn = Sai định dạng SSN

1

Là một theo dõi câu trả lời @Arthur Ronald, đây là cách tôi đã kết thúc thực hiện điều này:

On bộ điều khiển:

setBindingErrorProcessor(new CustomBindingErrorProcessor()); 

Và sau đó các lớp xử lý lỗi ràng buộc:

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

    } 

}   

Vì vậy, chữ ký của phương thức xử lý có một BindingResult thay vì một BindException trên phiên bản này.

Các vấn đề liên quan