1. Обзор
В этом руководстве основное внимание будет уделено реализации перенаправления в Spring и обсуждены причины, лежащие в основе каждой стратегии.
2. Зачем делать редирект?
Давайте сначала рассмотрим причины, по которым нам может понадобиться перенаправление в приложении Spring.
Есть много возможных примеров и причин, конечно. Например, нам может понадобиться отправить данные формы POST, обойти проблему двойной отправки или просто делегировать поток выполнения другому методу контроллера.
Небольшое примечание: типичный шаблон Post/Redirect/Get не решает должным образом проблемы с двойной отправкой, а такие проблемы, как обновление страницы до завершения первоначальной отправки, могут по-прежнему приводить к двойной отправке.
3. Перенаправление с помощью RedirectView
Давайте начнем с этого простого подхода и сразу перейдем к примеру :
@Controller
@RequestMapping("/")
public class RedirectController {
@GetMapping("/redirectWithRedirectView")
public RedirectView redirectWithUsingRedirectView(
RedirectAttributes attributes) {
attributes.addFlashAttribute("flashAttribute", "redirectWithRedirectView");
attributes.addAttribute("attribute", "redirectWithRedirectView");
return new RedirectView("redirectedUrl");
}
}
За кулисами RedirectView
вызовет HttpServletResponse.sendRedirect()
, который выполнит фактическое перенаправление.
Обратите внимание, как мы внедряем атрибуты перенаправления в метод. Фреймворк сделает тяжелую работу и позволит нам взаимодействовать с этими атрибутами.
Мы добавляем атрибут атрибута
модели , который будет отображаться как параметр HTTP-запроса. Модель должна содержать только объекты — обычно строки или объекты, которые можно преобразовать в строки.
Теперь давайте проверим наше перенаправление с помощью простой команды curl
:
curl -i http://localhost:8080/spring-rest/redirectWithRedirectView
И вот наш результат:
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location:
http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectView
4. Перенаправление с префиксом перенаправления:
Предыдущий подход — использование RedirectView
— неоптимален по нескольким причинам.
Во-первых, теперь мы связаны с Spring API, потому что мы используем RedirectView
непосредственно в нашем коде.
Во-вторых, теперь нам нужно знать с самого начала, при реализации этой операции контроллера, что результатом всегда будет перенаправление, что может не всегда иметь место.
Лучшим вариантом является использование префикса перенаправления:
. Имя представления перенаправления вводится в контроллер, как и любое другое имя логического представления. Контроллер даже не знает, что происходит перенаправление.
Вот как это выглядит:
@Controller
@RequestMapping("/")
public class RedirectController {
@GetMapping("/redirectWithRedirectPrefix")
public ModelAndView redirectWithUsingRedirectPrefix(ModelMap model) {
model.addAttribute("attribute", "redirectWithRedirectPrefix");
return new ModelAndView("redirect:/redirectedUrl", model);
}
}
Когда имя представления возвращается с префиксом redirect:
, UrlBasedViewResolver
(и все его подклассы) распознает это как особое указание на необходимость перенаправления. Остальная часть имени представления будет использоваться в качестве URL-адреса перенаправления.
Небольшое, но важное замечание: когда мы используем здесь это имя логического представления — redirect:/redirectedUrl
— мы делаем перенаправление относительно текущего контекста сервлета.
Мы можем использовать такое имя, как перенаправление: http://localhost:8080/spring-redirect-and-forward/redirectedUrl
, если нам нужно перенаправить на абсолютный URL-адрес.
Итак, теперь, когда мы выполняем команду curl
:
curl -i http://localhost:8080/spring-rest/redirectWithRedirectPrefix
мы будем немедленно перенаправлены:
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location:
http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectPrefix
5. Вперед с префиксом вперед:
Давайте теперь посмотрим, как сделать что-то немного другое: переадресацию.
Прежде чем приступить к коду, давайте кратко рассмотрим семантику перенаправления и перенаправления на высоком уровне :
перенаправление
ответит 302 и новым URL-адресом в заголовкеLocation ;
затем браузер/клиент сделает еще один запрос на новый URL.переадресация
происходит полностью на стороне сервера. Контейнер сервлета перенаправляет тот же запрос на целевой URL-адрес; URL-адрес не изменится в браузере.
Теперь давайте посмотрим на код:
@Controller
@RequestMapping("/")
public class RedirectController {
@GetMapping("/forwardWithForwardPrefix")
public ModelAndView redirectWithUsingForwardPrefix(ModelMap model) {
model.addAttribute("attribute", "forwardWithForwardPrefix");
return new ModelAndView("forward:/redirectedUrl", model);
}
}
Как и в случае с redirect:,
префикс forward:
будет разрешен с помощью UrlBasedViewResolver
и его подклассов. Внутри это создаст InternalResourceView
, который выполняет RequestDispatcher.forward()
для нового представления.
Когда мы выполняем команду с помощью curl
:
curl -I http://localhost:8080/spring-rest/forwardWithForwardPrefix
мы получим HTTP 405 (метод не разрешен):
HTTP/1.1 405 Method Not Allowed
Server: Apache-Coyote/1.1
Allow: GET
Content-Type: text/html;charset=utf-8
Подводя итог, по сравнению с двумя запросами, которые у нас были в случае решения перенаправления, в этом случае у нас есть только один запрос, исходящий от браузера/клиента на сторону сервера. Атрибут, ранее добавленный перенаправлением, конечно, тоже отсутствует.
6. Атрибуты с RedirectAttributes
Далее, давайте более подробно рассмотрим передачу атрибутов в перенаправлении , в полной мере используя фреймворк с RedirectAttributes
:
@GetMapping("/redirectWithRedirectAttributes")
public RedirectView redirectWithRedirectAttributes(RedirectAttributes attributes) {
attributes.addFlashAttribute("flashAttribute", "redirectWithRedirectAttributes");
attributes.addAttribute("attribute", "redirectWithRedirectAttributes");
return new RedirectView("redirectedUrl");
}
Как мы видели ранее, мы можем внедрить объект атрибутов напрямую в метод, что делает этот механизм очень простым в использовании.
Обратите также внимание, что мы также добавляем атрибут flash. Это атрибут, который не попадет в URL.
С таким атрибутом мы можем позже получить доступ к атрибуту flash, используя @ModelAttribute("flashAttribute")
только в методе, который является конечной целью перенаправления :
@GetMapping("/redirectedUrl")
public ModelAndView redirection(
ModelMap model,
@ModelAttribute("flashAttribute") Object flashAttribute) {
model.addAttribute("redirectionAttribute", flashAttribute);
return new ModelAndView("redirection", model);
}
Итак, в завершение, если мы проверим функциональность с помощью curl
:
curl -i http://localhost:8080/spring-rest/redirectWithRedirectAttributes
мы будем перенаправлены на новое место:
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=4B70D8FADA2FD6C22E73312C2B57E381; Path=/spring-rest/; HttpOnly
Location: http://localhost:8080/spring-rest/redirectedUrl;
jsessionid=4B70D8FADA2FD6C22E73312C2B57E381?attribute=redirectWithRedirectAttributes
Таким образом, использование RedirectAttributes
вместо ModelMap
дает нам возможность использовать только некоторые атрибуты между двумя методами , участвующими в операции перенаправления.
7. Альтернативная конфигурация без префикса
Давайте теперь рассмотрим альтернативную конфигурацию: перенаправление без использования префикса.
Для этого нам нужно использовать org.springframework.web.servlet.view.XmlViewResolver
:
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/spring-views.xml</value>
</property>
<property name="order" value="0" />
</bean>
Это вместо org.springframework.web.servlet.view.InternalResourceViewResolver
, который мы использовали в предыдущей конфигурации:
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
</bean>
Нам также нужно определить bean-компонент RedirectView
в конфигурации:
<bean id="RedirectedUrl" class="org.springframework.web.servlet.view.RedirectView">
<property name="url" value="redirectedUrl" />
</bean>
Теперь мы можем вызвать перенаправление, сославшись на этот новый компонент по идентификатору :
@Controller
@RequestMapping("/")
public class RedirectController {
@GetMapping("/redirectWithXMLConfig")
public ModelAndView redirectWithUsingXMLConfig(ModelMap model) {
model.addAttribute("attribute", "redirectWithXMLConfig");
return new ModelAndView("RedirectedUrl", model);
}
}
И чтобы проверить это, мы снова воспользуемся командой curl
:
curl -i http://localhost:8080/spring-rest/redirectWithRedirectView
И вот наш результат:
HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location:
http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectView
8. Перенаправление запроса HTTP POST
Для таких случаев использования, как банковские платежи, нам может потребоваться перенаправить HTTP-запрос POST. В зависимости от возвращенного кода состояния HTTP запрос POST может быть перенаправлен на HTTP GET или POST.
Согласно справочнику по протоколу HTTP 1.1 , коды состояния 301 (перемещено навсегда) и 302 (найдено) позволяют изменить метод запроса с POST на GET. Спецификация также определяет соответствующие коды состояния 307 (временное перенаправление) и 308 (постоянное перенаправление), которые не позволяют изменить метод запроса с POST на GET.
Давайте посмотрим на код для перенаправления почтового запроса на другой почтовый запрос:
@PostMapping("/redirectPostToPost")
public ModelAndView redirectPostToPost(HttpServletRequest request) {
request.setAttribute(
View.RESPONSE_STATUS_ATTRIBUTE, HttpStatus.TEMPORARY_REDIRECT);
return new ModelAndView("redirect:/redirectedPostToPost");
}
@PostMapping("/redirectedPostToPost")
public ModelAndView redirectedPostToPost() {
return new ModelAndView("redirection");
}
Теперь мы проверим перенаправление POST с помощью команды curl
:
curl -L --verbose -X POST http://localhost:8080/spring-rest/redirectPostToPost
Нас перенаправляют в нужное место:
> POST /redirectedPostToPost HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.49.0
> Accept: */*
>
< HTTP/1.1 200
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Tue, 08 Aug 2017 07:33:00 GMT
{"id":1,"content":"redirect completed"}
9. Вперед с параметрами
Теперь давайте рассмотрим сценарий, в котором мы хотели бы отправить некоторые параметры другому RequestMapping
с прямым
префиксом.
В этом случае мы можем использовать HttpServletRequest
для передачи параметров между вызовами.
Вот метод forwardWithParams
, который должен отправить param1
и param2
другому сопоставлению forwardedWithParams
:
@RequestMapping(value="/forwardWithParams", method = RequestMethod.GET)
public ModelAndView forwardWithParams(HttpServletRequest request) {
request.setAttribute("param1", "one");
request.setAttribute("param2", "two");
return new ModelAndView("forward:/forwardedWithParams");
}
На самом деле сопоставление forwardedWithParams
может существовать в совершенно новом контроллере и не обязательно должно быть в том же самом:
@RequestMapping(value="/forwardWithParams", method = RequestMethod.GET)
@Controller
@RequestMapping("/")
public class RedirectParamController {
@RequestMapping(value = "/forwardedWithParams", method = RequestMethod.GET)
public RedirectView forwardedWithParams(
final RedirectAttributes redirectAttributes, HttpServletRequest request) {
redirectAttributes.addAttribute("param1", request.getAttribute("param1"));
redirectAttributes.addAttribute("param2", request.getAttribute("param2"));
redirectAttributes.addAttribute("attribute", "forwardedWithParams");
return new RedirectView("redirectedUrl");
}
}
Чтобы проиллюстрировать, давайте попробуем эту команду curl :
curl -i http://localhost:8080/spring-rest/forwardWithParams
Вот результат:
HTTP/1.1 302 Found
Date: Fri, 19 Feb 2021 05:37:14 GMT
Content-Language: en-IN
Location: http://localhost:8080/spring-rest/redirectedUrl?param1=one¶m2=two&attribute=forwardedWithParams
Content-Length: 0
Как мы видим, param1
и param2
переместились с первого контроллера на второй. Наконец, они появились в перенаправлении с именем redirectedUrl
, на которое указывает forwardedWithParams
.
10. Заключение
В этой статье показаны три разных подхода к реализации перенаправления в Spring , как обрабатывать/передавать атрибуты при выполнении этих перенаправлений и как обрабатывать перенаправления HTTP-запросов POST.