Автор оригинала: Eugen Paraschiv.
1. Обзор
В этой статье основное внимание будет уделено реализации перенаправления весной и будет обсуждаться обоснование каждой стратегии.
Дальнейшее чтение:
Перенаправление на разные страницы после входа в систему с помощью Spring Security
Spring Security – Перенаправление на предыдущий URL-адрес после входа в систему
Управление сеансом с помощью Spring Security
2. Зачем делать перенаправление?
Давайте сначала рассмотрим причины, по которым вам может потребоваться выполнить перенаправление в приложении Spring.
Конечно, есть много возможных примеров и причин. Одним из простых способов может быть публикация данных формы, решение проблемы двойной отправки или просто делегирование потока выполнения другому методу контроллера.
Краткое примечание здесь заключается в том, что типичный шаблон 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() – который будет выполнять фактическое перенаправление.
Обратите внимание здесь, как мы вводим атрибуты перенаправления в метод – фреймворк выполнит здесь тяжелую работу и позволит нам взаимодействовать с этими атрибутами.
Мы добавляем атрибут модели attribute– , который будет представлен в качестве параметра HTTP-запроса. Модель должна содержать только объекты – обычно строки или объекты, которые могут быть преобразованы в строки.
Давайте теперь протестируем наш редирект – с помощью простого локон команда:
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. Перенаправление с префиксом redirect:
Предыдущий подход – использование RedirectView – является неоптимальным по нескольким причинам.
Во – первых, теперь мы подключены к API Spring, потому что мы используем представление Redirect непосредственно в вашем коде.
Во – вторых, теперь нам нужно знать с самого начала, при реализации этой операции контроллера, что результатом всегда будет перенаправление, что может быть не всегда так.
Лучшим вариантом является использование префикса redirect: – имя представления перенаправления вводится в контроллер, как и любое другое логическое имя представления. Контроллер даже не знает, что происходит перенаправление .
Вот как это выглядит:
@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 -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 -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. Атрибуты с атрибутами перенаправления
Далее – давайте подробнее рассмотрим передачу атрибутов в перенаправлении – полное использование фреймворка с RedirectAttribures :
@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 -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 вместо Карты модели дает нам возможность только совместно использовать некоторые атрибуты между двумя методами , участвующими в операции перенаправления.
7. Альтернативная Конфигурация Без Префикса
Давайте теперь рассмотрим альтернативную конфигурацию – перенаправление без использования префикса.
Для этого нам нужно использовать org.springframework.web.servlet.view.XmlViewResolver:
/WEB-INF/spring-views.xml
Вместо org.springframework.web.servlet.view.InternalResourceViewResolver мы использовали в предыдущей конфигурации:
Нам также необходимо определить представление Redirect bean в конфигурации:
Теперь мы можем инициировать перенаправление, ссылаясь на этот новый компонент по идентификатору :
@Controller
@RequestMapping("/")
public class RedirectController {
@GetMapping("/redirectWithXMLConfig")
public ModelAndView redirectWithUsingXMLConfig(ModelMap model) {
model.addAttribute("attribute", "redirectWithXMLConfig");
return new ModelAndView("RedirectedUrl", model);
}
}
И чтобы проверить это, мы снова воспользуемся локон команда:
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 reference , коды состояния 301 (Перемещено навсегда) и 302 (Найдено) позволяют изменять метод запроса с POST на GET. Спецификация также определяет соответствующие коды состояния 307 (Временное перенаправление) и 308 (Постоянное перенаправление), которые не позволяют изменять метод запроса с POST на GET.
Теперь давайте рассмотрим код для перенаправления запроса post на другой запрос post:
@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 с префиксом forward .
В этом случае мы можем использовать HttpServletRequest для передачи параметров между вызовами .
Вот метод forward С парами , который должен отправить param1 и param2 на другое отображение forward С парами :
@RequestMapping(value="/forwardWithParams", method = RequestMethod.GET)
public ModelAndView forwardWithParams(HttpServletRequest request) {
request.setAttribute("param1", "one");
request.setAttribute("param2", "two");
return new ModelAndView("forward:/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 , на которое перенаправляется с помощью параметров указывает.
10. Заключение
В этой статье проиллюстрированы три различных подхода к реализации перенаправления весной , как обрабатывать/передавать атрибуты при выполнении этих перенаправлений, а также как обрабатывать перенаправления запросов HTTP POST.