Spring - перенаправление после POST (даже с ошибками проверки)

Я пытаюсь понять, как "сохранить" BindingResult, чтобы он мог использоваться в последующем GET с помощью тега Spring <form:errors>. Причина, по которой я хочу это сделать, связана с ограничениями SSL для Google App Engine. У меня есть форма, которая отображается через HTTP, а сообщение - URL-адрес HTTPS. Если я только переадресую, а не перенаправляю, тогда пользователь увидит https://whatever.appspot.com/my/form. Я пытаюсь избежать этого. Любые идеи, как подойти к этому?

Ниже я хотел бы сделать это, но я вижу только ошибки проверки, когда я использую return "create".

@RequestMapping(value = "/submit", method = RequestMethod.POST)
public final String submit(
    @ModelAttribute("register") @Valid final Register register,
    final BindingResult binding) {

    if (binding.hasErrors()) {
        return "redirect:/register/create";
    }

    return "redirect:/register/success";
}

Ответ 1

Так как Spring 3.1, вы можете использовать RedirectAttributes. Добавьте атрибуты, которые вы хотите иметь, перед выполнением перенаправления. Добавьте и BindingResult, и объект, который вы используете для проверки, в этом случае Register.

Для BindingResult вы будете использовать имя: "org.springframework.validation.BindingResult. [имя вашего ModelAttribute]".

Для объекта, который вы используете для проверки, вы будете использовать имя ModelAttribute.

Чтобы использовать RedirectAttributes, вы должны добавить это в свой файл конфигурации. Помимо прочего, вы говорите Spring, чтобы использовать несколько новых классов:

<mvc:annotation-driven />

Теперь ошибки будут отображаться везде, где вы перенаправляете

@RequestMapping(value = "/submit", method = RequestMethod.POST)
public final String submit(@ModelAttribute("register") @Valid final Register register, final BindingResult binding, RedirectAttributes attr, HttpSession session) {

if (binding.hasErrors()) {
    attr.addFlashAttribute("org.springframework.validation.BindingResult.register", binding);
    attr.addFlashAttribute("register", register);
    return "redirect:/register/create";
}

return "redirect:/register/success";
}

Ответ 2

В дополнение к приятному ответу Оскара, если вы следуете этому подходу RedirectAttributes, не забывайте, что вы фактически передаете modelAttribute на перенаправленную страницу. Это означает, что если вы создаете новый экземпляр этого атрибута modelAttribute для перенаправленной страницы (в контроллере), вы потеряете ошибки проверки. Итак, если ваш метод POST - это примерно так:

@RequestMapping(value = "/submit", method = RequestMethod.POST)
public final String submit(@ModelAttribute("register") @Valid final Register register, final BindingResult binding, RedirectAttributes attr, HttpSession session) {

if (binding.hasErrors()) {
    attr.addFlashAttribute("org.springframework.validation.BindingResult.register", binding);
    attr.addFlashAttribute("register", register);
    return "redirect:/register/create";
}

return "redirect:/register/success";
}

Тогда вам, вероятно, понадобится внести изменения в вашу страницу создания реестра GET. Из этого:

@RequestMapping(value = "/register/create", method = RequestMethod.GET)
public String registerCreatePage(Model model) {
    // some stuff
    model.addAttribute("register", new Register());
    // some more stuff
}

к

@RequestMapping(value = "/register/create", method = RequestMethod.GET)
public String registerCreatePage(Model model) {
    // some stuff
    if (!model.containsAttribute("register")) {
        model.addAttribute("register", new Register());
    }
    // some more stuff
}

Источник: http://gerrydevstory.com/2013/07/11/preserving-validation-error-messages-on-spring-mvc-form-post-redirect-get/

Ответ 3

Проблема в том, что вы перенаправляетесь на новый контроллер, вместо того, чтобы отображать представление и возвращать страницу обработанной формы. Вам нужно что-то сделать в соответствии с:

String FORM_VIEW = wherever_your_form_page_resides

...

if (binding.hasErrors())
    return FORM_VIEW;

Я бы сохранил пути вне любых методов из-за дублирования кода строк.

Ответ 4

Единственный способ сохранить объекты между запросами (т.е. перенаправление) - это сохранить объект в атрибуте сеанса. Таким образом, вы должны включить "запрос HttpServletRequest" в параметры метода для обоих методов (т.е. Получить и отправить) и получить объект через request.getAttribute( "привязка" ). Тем не менее, и не пробовав это самостоятельно, вам может понадобиться выяснить, как повторно привязать привязку к объекту в новом запросе.

Еще один "не лучший" способ - просто изменить URL-адрес браузера с помощью javascript

Ответ 5

Я бы поставил под вопрос, зачем вам нужно перенаправление. Почему бы просто не отправить на тот же URL-адрес и ответить на него по-разному? Тем не менее, если вы действительно хотите это сделать:

@RequestMapping(value = "/submit", method = RequestMethod.POST)
public final String submit(
    @ModelAttribute("register") @Valid final Register register,
    final BindingResult binding,
    HttpSession session) {

    if (binding.hasErrors()) {
        session.setAttribute("register",register);
        session.setAttribute("binding",binding);
        return "redirect:/register/create";
    }

    return "redirect:/register/success";
}

Затем в вашем методе "create":

model.put("register",session.getAttribute("register"));
model.put("org.springframework.validation.BindingResult.register",session.getAttribute("register"));

Ответ 6

Я не знаю точной проблемы с Google App Engine, но используя ForwardedHeaderFilter может помочь сохранить исходную схему, клиент используется. Этот фильтр был добавлен в Spring Framework 4.3, но некоторые контейнеры Servlet предоставляют аналогичные фильтры, и фильтр является самодостаточным, поэтому вы можете просто захватить источник, если это необходимо.

Ответ 7

Возможно, это немного упрощено, но попробовали ли вы добавить его в свою модель? I.e., включите модель в аргументы метода, затем добавьте BindingResult к ней, которая затем будет доступна в вашем представлении.

model.addAttribute("binding",binding);

Я думаю, вам, возможно, придется использовать переадресацию, а не перенаправление (в моей голове я не могу вспомнить, если перенаправление теряет сеанс или нет, я мог ошибаться в этом, так как у меня нет никакой документации, т.е. если вы не получаете BindingResult после добавления его в модель, попробуйте использовать forward вместо этого, чтобы подтвердить это).