Классы с аннотациями @Controller или @ControllerAdvice могут иметь методы, помеченные аннотацией @InitBinder, которые инициализируют экземпляры WebDataBinder, а те, в свою очередь, могут:

  • Привязывать параметры запроса (то есть данные формы или запроса) к объекту модели.

  • Преобразование строковых значений запроса (таких как параметры запроса, переменные пути, заголовки, данные cookie и др.) в целевой тип аргументов метода контроллера.

  • Форматировать значения объектов модели как String значения при отображении HTML-форм.

Методы с аннотацией @InitBinder могут регистрировать специфичные для контроллера компоненты java.beans.PropertyEditor или Converter и Formatter из Spring. Кроме того, можно использовать конфигурацию MVC для регистрации типов Converter и Formatter в глобально разделяемой FormattingConversionService.

Методы, помеченные аннотацией @InitBinder, поддерживают многие из тех же аргументов, что и методы с аннотацией @RequestMapping, за исключением аргументов, аннотированных @ModelAttribute (объект команды). Как правило, они объявляются с аргументом WebDataBinder (для процедур регистрации) и возвращаемым значениемvoid . В следующем листинге приведен пример:

Java
@Controller
public class FormController {
    @InitBinder 
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    }
    // ...
}
  1. Определение метода с аннотацией @InitBinder.
Kotlin
@Controller
class FormController {
    @InitBinder 
    fun initBinder(binder: WebDataBinder) {
        val dateFormat = SimpleDateFormat("yyyy-MM-dd")
        dateFormat.isLenient = false
        binder.registerCustomEditor(Date::class.java, CustomDateEditor(dateFormat, false))
    }
    // ...
}
  1. Определение метода с аннотацией @InitBinder.

Кроме того, если вы используете настройку на основе Formatter через общую службу FormattingConversionService, то можете повторно использовать тот же подход и зарегистрировать специфичные для контроллера реализации Formatter, как показано в следующем примере:

Java
@Controller
public class FormController {
    @InitBinder 
    protected void initBinder(WebDataBinder binder) {
        binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
    }
    // ...
}
  1. Определение метода с аннотацией @InitBinder для кастомного форматтера.
Kotlin
@Controller
class FormController {
    @InitBinder 
    protected fun initBinder(binder: WebDataBinder) {
        binder.addCustomFormatter(DateFormatter("yyyy-MM-dd"))
    }
    // ...
}
  1. Определение метода с аннотацией @InitBinder для кастомного форматтера.

Структура модели

В контексте веб-приложений привязывание данных подразумевает привязку параметров HTTP-запроса (то есть данных формы или параметров запроса) к свойствам объекта модели и его вложенных объектов.

Для привязки данных открываются только public свойства, соответствующие соглашениям об именовании JavaBeans – например, методы public String getFirstName() и public void setFirstName(String) для свойства firstName.

Объект модели и его вложенный граф объектов также иногда называют объектом команды, базовым объектом формы или POJO ("старый добрый Java-объект").

По умолчанию Spring разрешает привязку ко всем публичным свойствам в графе объектов модели. Это означает, что необходимо тщательно продумывать, какие публичные свойства есть у модели, поскольку клиент может сделать целью любое публичное свойство, даже те, которые не предполагаются для данного случая использования.

Например, при наличии конечной точки данных HTTP-формы, вредоносный клиент может передать значения для свойств, которые существуют в графе объектов модели, но не являются частью HTML-формы, представленной в браузере. Это может привести к тому, что в объект модели и любой из его вложенных объектов будут установлены данные, обновление которых не ожидается.

Рекомендуемый подход заключается в использовании специализированного объекта модели, который открывает только те свойства, которые имеют отношение к отправке формы. Например, в форме для изменения адреса электронной почты пользователя объект модели должен объявить минимальный набор свойств, как в следующем ChangeEmailForm.

public class ChangeEmailForm {
    private String oldEmailAddress;
    private String newEmailAddress;
    public void setOldEmailAddress(String oldEmailAddress) {
        this.oldEmailAddress = oldEmailAddress;
    }
    public String getOldEmailAddress() {
        return this.oldEmailAddress;
    }
    public void setNewEmailAddress(String newEmailAddress) {
        this.newEmailAddress = newEmailAddress;
    }
    public String getNewEmailAddress() {
        return this.newEmailAddress;
    }
}

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

Например, чтобы зарегистрировать допустимые шаблоны полей в вашем приложении, можно реализовать метод, помеченный аннотацией @InitBinder, в компоненте с аннотацией @Controller или @ControllerAdvice, как показано ниже:

@Controller
public class ChangeEmailController {
    @InitBinder
    void initBinder(WebDataBinder binder) {
        binder.setAllowedFields("oldEmailAddress", "newEmailAddress");
    }
    // методы, аннотированные @RequestMapping и т.д.
}

В дополнение к регистрации шаблонов допустимых полей, можно также зарегистрировать шаблоны недопустимых полей с помощью метода setDisallowedFields() в DataBinder и его подклассах. Однако обратите внимание, что "допускающий список" безопаснее, чем "запрещающий список". Следовательно, использование setAllowedFields() следует предпочесть вместо использования setDisallowedFields().

Обратите внимание, что сопоставление с шаблонами допустимых полей чувствительно к регистру; в то время как сопоставление с запрещенными шаблонами полей – нет. Кроме того, поле, соответствующее запрещенному шаблону, не будет принято, даже если оно также соответствует шаблону из списка допустимых.

Очень важно правильно сконфигурировать шаблоны допустимых и недопустимых полей при непосредственном открытии модели предметной области для привязки данных. В противном случае это будет большой риск для безопасности.

Кроме того, настоятельно рекомендуется не использовать типы из вашей модели предметной области, такие как сущности JPA или Hibernate, в качестве объекта модели в сценариях привязки данных.