Проверка с помощью Spring 3.2.0

Я использую HibernateValidator 4.3.1. Валидации выполняются по назначению во всем приложении.

Я зарегистрировал несколько пользовательских редакторов для выполнения глобальной проверки, например, для обеспечения числовых значений (double, int и т. д.) в текстовом поле, для обеспечения действительных дат относительно Joda-Time API и т. д.

В этом типе проверки я разрешаю нулевые/пустые значения, устанавливая для параметра allowEmpty значение false, как обычно, чтобы проверить его отдельно, особенно для отображения отдельных удобных для пользователя сообщений об ошибках, когда такие поля оставлены пустыми.

Поэтому в дополнение к проверке с помощью HibernateValidator и пользовательских редакторов я пытаюсь использовать следующую стратегию проверки. Опять же, этот тип проверки предназначен только для тех полей, которые зарегистрированы для пользовательских редакторов, если они оставлены пустыми.

Ниже приведен класс, реализующий org.springframework.validation.Validator.

package test;

import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import validatorbeans.TempBean;

@Component
public final class TempValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        System.out.println("supports() invoked.");
        return TempBean.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        TempBean tempBean = (TempBean) target;

        System.out.println("startDate = " + tempBean.getStartDate() + " validate() invoked.");
        System.out.println("doubleValue = " + tempBean.getDoubleValue() + " validate() invoked.");
        System.out.println("stringValue = " + tempBean.getStringValue() + " validate() invoked.");

        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "startDate", "java.util.date.nullOrEmpty.error");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "doubleValue", "java.lang.double.nullOrEmpty.error");
    }
}

Класс обозначен аннотацией @Component, чтобы его можно было автоматически связать с конкретным классом контроллера Spring. Операторы отладки отображаются точно на основе ввода, предоставленного пользователем.

Ниже приведен класс контроллера.

package controller;

import customizeValidation.CustomizeValidation;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.groups.Default;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import test.TempValidator;
import validatorbeans.TempBean;

@Controller
public final class TempController {

    @Autowired
    private TempService tempService;

    private TempValidator tempValidator;

    public TempValidator getTempValidator() {
        return tempValidator;
    }

    @Autowired
    public void setTempValidator(TempValidator tempValidator) {
        this.tempValidator = tempValidator;
    }

    @RequestMapping(method = {RequestMethod.GET}, value = {"admin_side/Temp"})
    public String showForm(@ModelAttribute("tempBean") @Valid TempBean tempBean, BindingResult error, Map model, HttpServletRequest request, HttpServletResponse response) {
        return "admin_side/Temp";
    }

    @RequestMapping(method = {RequestMethod.POST}, value = {"admin_side/Temp"})
    public String onSubmit(@ModelAttribute("tempBean") @Valid TempBean tempBean, BindingResult errors, Map model, HttpServletRequest request, HttpServletResponse response) {
        //tempValidator.supports(TempBean.class);
        //tempValidator.validate(tempBean, errors);

        DataBinder dataBinder = new DataBinder(tempBean);
        dataBinder.setValidator(tempValidator);
        dataBinder.validate();

        //errors=dataBinder.getBindingResult();
        if (CustomizeValidation.isValid(errors, tempBean, TempBean.ValidationGroup.class, Default.class) && !errors.hasErrors()) {
            System.out.println("Validated");
        }

        return "admin_side/Temp";
    }
}

Я вызываю валидатор из самого класса контроллера Spring (который я действительно хочу) с помощью

DataBinder dataBinder = new DataBinder(tempBean);
dataBinder.setValidator(tempValidator);
dataBinder.validate();

Валидатор вызывается, но ожидаемая проверка не выполняется.

Если только я вызову валидатор вручную, используя следующий оператор (который закомментирован выше),

tempValidator.validate(tempBean, errors);

затем выполняется проверка. Поэтому я не верю, что мой валидатор работает правильно. Почему это не работает с DataBinder?

В моем файле application-context.xml этот bean-компонент просто настроен следующим образом.

<bean id="tempValidator" class="test.TempValidator"/>

Это множество пакетов, как показано ниже, включая пакет test, в который заключен класс TempValidator, определяются автоматически.

<context:component-scan base-package="controller spring.databinder validatorbeans validatorcommands test" use-default-filters="false">
    <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    <context:include-filter expression="org.springframework.web.bind.annotation.ControllerAdvice" type="annotation"/>  
</context:component-scan>

Я даже пытался поставить

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

В моем файле dispatcher-servlet.xml.

Что я здесь упускаю?


person Tiny    schedule 20.02.2013    source источник
comment
Если вы поместите разрыв в строку dataBinder.validate(); и войдите в него, вы можете увидеть, какой валидатор в конечном итоге вызывается?   -  person CodeChimp    schedule 20.02.2013
comment
@CodeChimp — вызываемый валидатор указан в вопросе -tempValidator.   -  person Tiny    schedule 21.02.2013


Ответы (2)


Если я хорошо понимаю, чего вы пытаетесь достичь - различать пустые поля и введенные неверные значения - вы можете использовать НАМНОГО БОЛЕЕ ПРОСТОЙ подход:

public class MyBean {

    @NotNull
    @DateTimeFormat(pattern="dd.MM.yyyy HH:mm") 
    private DateTime date;

    @NotNull
    @Max(value=5)
    private Integer max;

    @NotNull
    @Size(max=20)
    private String name;

    // getters, setters ... 
} 

Отображение контроллера:

public void submitForm(@ModelAttribute @Valid MyBean myBean, BindingResult result) {

    if (result.hasErrors){
       // do something}
    else{
       // do something else
    }        
}

Сообщения проверки:

NotNull=Required field.
NotNull.date=Date is required field.
NotNull.max=Max is required field.
Size=Must be between {2} and {1} letters.
Max=Must be lower than {1}.
typeMismatch.java.lang.Integer=Must be number.
typeMismatch.org.joda.time.DateTime=Required format dd.mm.yyyy HH:mm

Конфигурация пружины:

@Configuration
public class BaseValidatorConfig {

    @Bean
    public LocalValidatorFactoryBean getValidator() {

        LocalValidatorFactoryBean lvfb = new LocalValidatorFactoryBean();
        lvfb.setValidationMessageSource(getValidationMessageSource());
        return lvfb;
    }

    protected MessageSource getValidationMessageSource() {// return you validation messages ...}
}

Я могу предоставить более подробную информацию и пояснения, если это необходимо.

person Pavla Nováková    schedule 27.02.2013
comment
Ах! Я никогда не использовал это LocalValidatorFactoryBean. Такие аннотации, как @NotEmpty, @NotBlank, @NotNull и т. д., работают сами по себе (в Spring 3.2.2, который я использую в настоящее время). Для чего это LocalValidatorFactoryBean? Более того, @Configuration лучше, чем конфигурация XML? Спасибо. - person Tiny; 13.06.2013
comment
LocalValidatorFactoryBean обеспечивает мост между Spring MVC Validator и реализацией проверки компонентов JSR-303 — он позволяет, например, использовать аннотацию @Valid в методах контроллера, которая обеспечивает привязку готовых ошибок проверки к результату привязки, как показано в примере кода) и также используйте сообщения проверки в стиле Spring (преобразователи сообщений с кодами ошибок). @Configuration обеспечивает большую гибкость и менее подвержен ошибкам, но вы можете добиться одинаковых результатов как в конфигурации Java, так и в конфигурации XML. - person Pavla Nováková; 16.06.2013

Я не знаю, почему подход, упомянутый в вопросе, не сработал. Я не заставил это работать, но прошел через этот document, я нашел другой подход, который работал для меня в соответствии с моими требованиями.

Я установил валидатор внутри метода, который был обозначен аннотацией @InitBinder.

Из документов< /а>

Экземпляр Validator, вызываемый при обнаружении аргумента метода @Valid, можно настроить двумя способами. Во-первых, вы можете вызвать binder.setValidator(Validator) в обратном вызове @Controller @InitBinder. Это позволяет настроить экземпляр Validator для каждого класса @Controller:

В частности, в моих требованиях проверка должна выполняться только при обновлении или вставке данных в базу данных, то есть при нажатии соответствующей кнопки отправки для этих операций (в моем приложении есть общая кнопка для обеих этих задач (вставка и обновление). чье имя btnSubmit).

Проверка должна быть отключена в любом другом случае (например, при нажатии кнопки удаления). Чтобы выполнить это требование, я зарегистрировал валидатор следующим образом.

@InitBinder
protected void initBinder(WebDataBinder binder, WebRequest webRequest) {
    if (webRequest.getParameter("btnSubmit") != null) {
        binder.setValidator(new TempValidator());
    } else {
        binder.setValidator(null);
    }
}

В этой ситуации валидатор - TempValidator будет установлен только тогда, когда клиент нажмет кнопку отправки, атрибут имени которой равен btnSubmit.

Нет необходимости в конфигурации xml в любом месте, а также в автоматическом подключении.

Примерный класс контроллера теперь выглядит следующим образом.

@Controller
public final class TempController {

    @Autowired
    private TempService tempService;

    @InitBinder
    protected void initBinder(WebDataBinder binder, WebRequest webRequest) {
        if (webRequest.getParameter("btnSubmit") != null) {
            binder.setValidator(new TempValidator());
        } else {
            binder.setValidator(null);
        }
    }

    //Removed the @Valid annotation before TempBean, since validation is unnecessary on page load.
    @RequestMapping(method = {RequestMethod.GET}, value = {"admin_side/Temp"})
    public String showForm(@ModelAttribute("tempBean") TempBean tempBean, BindingResult error, Map model, HttpServletRequest request, HttpServletResponse response) {
        return "admin_side/Temp";
    }

    @RequestMapping(method = {RequestMethod.POST}, value = {"admin_side/Temp"})
    public String onSubmit(@ModelAttribute("tempBean") @Valid TempBean tempBean, BindingResult errors, Map model, HttpServletRequest request, HttpServletResponse response) {
        if (CustomizeValidation.isValid(errors, tempBean, TempBean.ValidationGroup.class, Default.class) && !errors.hasErrors()) {
            System.out.println("Validated");
        }
        return "admin_side/Temp";
    }
}

Параметр WebRequest в методе initBinder() не предназначен для обработки всего HTTP-запроса, как это очевидно. Это просто для использования метаданных запроса общего назначения.

Javadocs о WebRequest.

Общий интерфейс для веб-запроса. В основном предназначен для общих перехватчиков веб-запросов, предоставляя им доступ к общим метаданным запроса, а не для фактической обработки запроса.

Если есть что-то не так, за чем я мог бы следить, пожалуйста, уточните это или добавьте другой ответ.

person Tiny    schedule 21.02.2013