Una guida ai reindirizzamenti di primavera

1. Panoramica

Questo articolo si concentrerà sull'implementazione di un reindirizzamento in primavera e discuterà il ragionamento alla base di ciascuna strategia.

2. Perché eseguire un reindirizzamento?

Consideriamo prima i motivi per cui potrebbe essere necessario eseguire un reindirizzamento in un'applicazione Spring.

Ci sono molti possibili esempi e ragioni ovviamente. Uno semplice potrebbe essere il POST dei dati del modulo, aggirare il problema del doppio invio o semplicemente delegare il flusso di esecuzione a un altro metodo del controller.

Una breve nota a margine qui è che il tipico schema Post / Reindirizzamento / Ottieni non risolve adeguatamente i problemi di doppio invio: problemi come l'aggiornamento della pagina prima che l'invio iniziale sia stato completato possono comunque comportare un doppio invio.

3. Reindirizzamento con RedirectView

Cominciamo con questo semplice approccio e andiamo direttamente a un esempio :

@Controller @RequestMapping("/") public class RedirectController { @GetMapping("/redirectWithRedirectView") public RedirectView redirectWithUsingRedirectView( RedirectAttributes attributes) { attributes.addFlashAttribute("flashAttribute", "redirectWithRedirectView"); attributes.addAttribute("attribute", "redirectWithRedirectView"); return new RedirectView("redirectedUrl"); } }

Dietro le quinte, RedirectView attiverà un HttpServletResponse.sendRedirect () , che eseguirà il reindirizzamento effettivo.

Nota qui come stiamo inserendo gli attributi di reindirizzamento nel metodo : il framework farà il lavoro pesante qui e ci consentirà di interagire con questi attributi.

Stiamo aggiungendo l'attributo di attributo del modello , che verrà esposto come parametro di query HTTP. Il modello deve contenere solo oggetti, generalmente stringhe o oggetti che possono essere convertiti in stringhe.

Proviamo ora il nostro reindirizzamento, con l'aiuto di un semplice comando curl :

curl -i //localhost:8080/spring-rest/redirectWithRedirectView

Il risultato sarà:

HTTP/1.1 302 Found Server: Apache-Coyote/1.1 Location: //localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectView

4. Reindirizzamento con il reindirizzamento del prefisso :

L'approccio precedente, utilizzando RedirectView, non è ottimale per alcuni motivi.

Primo: ora siamo collegati all'API Spring perché stiamo usando RedirectView direttamente nel nostro codice.

Secondo: ora dobbiamo sapere dall'inizio, quando si implementa l'operazione del controller, che il risultato sarà sempre un reindirizzamento, il che potrebbe non essere sempre il caso.

Un'opzione migliore è usare il reindirizzamento del prefisso : - il nome della vista di reindirizzamento viene inserito nel controller come qualsiasi altro nome di vista logica. Il controller non è nemmeno a conoscenza del fatto che il reindirizzamento sta avvenendo .

Ecco come appare:

@Controller @RequestMapping("/") public class RedirectController { @GetMapping("/redirectWithRedirectPrefix") public ModelAndView redirectWithUsingRedirectPrefix(ModelMap model) { model.addAttribute("attribute", "redirectWithRedirectPrefix"); return new ModelAndView("redirect:/redirectedUrl", model); } } 

Quando un nome di vista viene restituito con il prefisso redirect: - l'UrlBasedViewResolver (e tutte le sue sottoclassi) riconosceranno questo come un'indicazione speciale che ha bisogno un redirect per accadere. Il resto del nome della vista verrà utilizzato come URL di reindirizzamento.

Una nota veloce ma importante qui è che - quando usiamo questo nome di vista logica qui - redirect: / redirectedUrl - stiamo facendo un reindirizzamento relativo al contesto Servlet corrente .

Possiamo usare un nome come redirect: // localhost: 8080 / spring-redirect-and-forward / redirectedUrl se dobbiamo reindirizzare a un URL assoluto.

Quindi ora, quando eseguiamo il comando curl :

curl -i //localhost:8080/spring-rest/redirectWithRedirectPrefix

Verremo immediatamente reindirizzati:

HTTP/1.1 302 Found Server: Apache-Coyote/1.1 Location: //localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectPrefix

5. Avanti con il prefisso in avanti:

Vediamo ora come fare qualcosa di leggermente diverso: un attaccante.

Prima del codice, esaminiamo una rapida panoramica di alto livello della semantica di forward vs. redirect :

  • redirect risponderà con un 302 e il nuovo URL nell'intestazione Location ; il browser / client farà quindi un'altra richiesta al nuovo URL
  • forward avviene interamente su un lato server; il contenitore Servlet inoltra la stessa richiesta all'URL di destinazione; l'URL non cambierà nel browser

Ora guardiamo il codice:

@Controller @RequestMapping("/") public class RedirectController { @GetMapping("/forwardWithForwardPrefix") public ModelAndView redirectWithUsingForwardPrefix(ModelMap model) { model.addAttribute("attribute", "forwardWithForwardPrefix"); return new ModelAndView("forward:/redirectedUrl", model); } } 

Come redirect : , il prefisso forward: verrà risolto da UrlBasedViewResolver e dalle sue sottoclassi. Internamente, questo creerà un InternalResourceView che esegue RequestDispatcher.forward () alla nuova vista.

Quando eseguiamo il comando con curl:

curl -I //localhost:8080/spring-rest/forwardWithForwardPrefix 

Otterremo HTTP 405 (metodo non consentito):

HTTP/1.1 405 Method Not Allowed Server: Apache-Coyote/1.1 Allow: GET Content-Type: text/html;charset=utf-8

Per concludere, rispetto alle due richieste che abbiamo avuto nel caso della soluzione di reindirizzamento, in questo caso abbiamo una sola richiesta in uscita dal browser / client lato server. Anche l'attributo precedentemente aggiunto dal reindirizzamento è, ovviamente, mancante.

6. Attributi con RedirectAttributes

Next – let's look closer at passing attributes in a redirect – making full use the framework with RedirectAttribures:

@GetMapping("/redirectWithRedirectAttributes") public RedirectView redirectWithRedirectAttributes(RedirectAttributes attributes) { attributes.addFlashAttribute("flashAttribute", "redirectWithRedirectAttributes"); attributes.addAttribute("attribute", "redirectWithRedirectAttributes"); return new RedirectView("redirectedUrl"); } 

As we saw before, we can inject the attributes object in the method directly – which makes this mechanism very easy to use.

Notice also that we are adding a flash attribute as well – this is an attribute that won't make it into the URL. What we can achieve with this kind of attribute is – we can later access the flash attribute using @ModelAttribute(“flashAttribute”)only in the method that is the final target of the redirect:

@GetMapping("/redirectedUrl") public ModelAndView redirection( ModelMap model, @ModelAttribute("flashAttribute") Object flashAttribute) { model.addAttribute("redirectionAttribute", flashAttribute); return new ModelAndView("redirection", model); } 

So, to wrap up – if we test the functionality with curl:

curl -i //localhost:8080/spring-rest/redirectWithRedirectAttributes

We will be redirected to the new location:

HTTP/1.1 302 Found Server: Apache-Coyote/1.1 Set-Cookie: JSESSIONID=4B70D8FADA2FD6C22E73312C2B57E381; Path=/spring-rest/; HttpOnly Location: //localhost:8080/spring-rest/redirectedUrl; jsessionid=4B70D8FADA2FD6C22E73312C2B57E381?attribute=redirectWithRedirectAttributes

That way, using RedirectAttribures instead of a ModelMap gives us the ability only to share some attributes between the two methods that are involved in the redirect operation.

7. An Alternative Configuration Without the Prefix

Let's now explore an alternative configuration – a redirect without using the prefix.

To achieve this, we need to use an org.springframework.web.servlet.view.XmlViewResolver:

  /WEB-INF/spring-views.xml    

Instead of org.springframework.web.servlet.view.InternalResourceViewResolver we used in the previous configuration:

We also need to define a RedirectView bean in the configuration:

Now we can trigger the redirect by referencing this new bean by id:

@Controller @RequestMapping("/") public class RedirectController { @GetMapping("/redirectWithXMLConfig") public ModelAndView redirectWithUsingXMLConfig(ModelMap model) { model.addAttribute("attribute", "redirectWithXMLConfig"); return new ModelAndView("RedirectedUrl", model); } } 

And to test it, we'll again use the curl command:

curl -i //localhost:8080/spring-rest/redirectWithRedirectView

The result will be:

HTTP/1.1 302 Found Server: Apache-Coyote/1.1 Location: //localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectView

8. Redirecting an HTTP POST Request

For use cases like bank payments, we might need to redirect an HTTP POST request. Depending upon the HTTP status code returned, POST request can be redirected to an HTTP GET or POST.

As per HTTP 1.1 protocol reference, status codes 301 (Moved Permanently) and 302 (Found) allow the request method to be changed from POST to GET. The specification also defines the corresponding 307 (Temporary Redirect) and 308 (Permanent Redirect) status codes that don't allow the request method to be changed from POST to GET.

Now let's look at the code for redirecting a post request to another post request:

@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"); }

Now, let's test the redirect of POST using the curl command:

curl -L --verbose -X POST //localhost:8080/spring-rest/redirectPostToPost

Stiamo per essere reindirizzati alla posizione prevista:

> 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. Conclusione

Questo articolo ha illustrato tre diversi approcci all'implementazione di un reindirizzamento in Spring , come gestire / passare attributi durante questi reindirizzamenti e come gestire i reindirizzamenti delle richieste HTTP POST.