Un guide pour les redirections de printemps

Un guide pour les redirections de printemps

1. Vue d'ensemble

Cet article se concentrera surimplementing a Redirect in Spring et discutera du raisonnement derrière chaque stratégie.

Lectures complémentaires:

Redirection vers différentes pages après la connexion avec Spring Security

Exemple de redirection vers différentes pages après la connexion avec Spring Security.

Read more

Spring Security - Rediriger vers l'URL précédente après la connexion

Petit exemple de redirection après la connexion à Spring Security

Read more

Contrôler la session avec Spring Security

Configurez les sessions avec la sécurité de printemps - configurez des sessions simultanées, activez la protection de la fixation de session et empêchez les URL de contenir des informations de session.

Read more

2. Pourquoi faire une redirection?

Considérons d'abordthe reasons why you may need to do a redirect dans une application Spring.

Il y a beaucoup d'exemples possibles et de raisons bien sûr. Une solution simple peut être la publication de données de formulaire, le contournement du problème de double soumission ou simplement la délégation du flux d’exécution à une autre méthode de contrôleur.

Une remarque rapide ici est que le modèle typique Post / Redirection / Get ne résout pas correctement les problèmes de double soumission - des problèmes tels que l'actualisation de la page avant la fin de la soumission initiale peuvent toujours entraîner une double soumission.

3. Rediriger avec lesRedirectView

Commençons par cette approche simple - etgo straight to an example:

@Controller
@RequestMapping("/")
public class RedirectController {

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

Dans les coulisses,RedirectView déclenchera unHttpServletResponse.sendRedirect() - qui effectuera la redirection réelle.

Remarquez ici commentwe’re injecting the redirect attributes into the method - le framework fera le gros du travail ici et nous permettra d'interagir avec ces attributs.

Nous ajoutons l'attribut de modèleattribute – qui sera exposé en tant que paramètre de requête HTTP. Le modèle ne doit contenir que des objets - généralement des chaînes ou des objets pouvant être convertis en chaînes.

Testons maintenant notre redirection - à l’aide d’une simple commandecurl:

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

Le résultat sera:

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

4. Rediriger avec le préfixeredirect:

L'approche précédente - utiliserRedirectView – est sous-optimale pour plusieurs raisons.

Premièrement, nous sommes maintenant couplés à l'API Spring car nous utilisons lesRedirectView directement dans notre code.

Deuxièmement - nous devons maintenant savoir dès le début, lors de la mise en œuvre de cette opération de contrôleur - que le résultat sera toujours une redirection - ce qui peut ne pas toujours être le cas.

Une meilleure option consiste à utiliser le préfixeredirect: - le nom de la vue de redirection est injecté dans le contrôleur comme tout autre nom de vue logique. The controller is not even aware that redirection is happening.

Voici à quoi cela ressemble:

@Controller
@RequestMapping("/")
public class RedirectController {

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

Lorsqu'un nom de vue est renvoyé avec le préfixeredirect:, leUrlBasedViewResolver (et toutes ses sous-classes) reconnaîtra cela comme une indication spéciale qu'une redirection doit avoir lieu. Le reste du nom de la vue sera utilisé comme URL de redirection.

Une remarque rapide mais importante ici est que - lorsque nous utilisons ce nom de vue logique ici -redirect:/redirectedUrl - nous faisons une redirectionrelative to the current Servlet context.

Nous pouvons utiliser un nom tel quea redirect: http://localhost:8080/spring-redirect-and-forward/redirectedUrl si nous devons rediriger vers une URL absolue.

Alors maintenant, lorsque nous exécutons la commandecurl:

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

Nous serons immédiatement redirigés:

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

5. Transférer avec le préfixeforward:

Voyons maintenant comment faire quelque chose de légèrement différent - un avant.

Avant le code, passons en revuea quick, high-level overview of the semantics of forward vs. redirect:

  • redirect répondra avec un 302 et la nouvelle URL dans l'en-têteLocation; le navigateur / client fera alors une autre demande à la nouvelle URL

  • forward se produit entièrement du côté serveur; le conteneur Servlet transmet la même requête à l'URL cible; l'URL ne change pas dans le navigateur

Examinons maintenant le code:

@Controller
@RequestMapping("/")
public class RedirectController {

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

Identique àredirect:, le préfixeforward: sera résolu parUrlBasedViewResolver et ses sous-classes. En interne, cela créera unInternalResourceView qui fera unRequestDispatcher.forward() sur la nouvelle vue.

Lorsque nous exécutons la commande aveccurl:

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

Nous aurons HTTP 405 (méthode non autorisée):

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

Pour conclure, comparé aux deux demandes que nous avions dans le cas de la solution de redirection, dans ce cas, nous n’avons qu’une seule demande sortant du navigateur / client vers le côté serveur. Bien entendu, l'attribut précédemment ajouté par la redirection est également manquant.

6. Attributs avec lesRedirectAttributes

Ensuite - regardons de plus prèspassing attributes in a redirect - en utilisant pleinement le framework avecRedirectAttribures:

@GetMapping("/redirectWithRedirectAttributes")
public RedirectView redirectWithRedirectAttributes(RedirectAttributes attributes) {

    attributes.addFlashAttribute("flashAttribute", "redirectWithRedirectAttributes");
    attributes.addAttribute("attribute", "redirectWithRedirectAttributes");
    return new RedirectView("redirectedUrl");
}

Comme nous l'avons vu précédemment, nous pouvons directement injecter l'objet d'attributs dans la méthode, ce qui rend ce mécanisme très facile à utiliser.

Notez également quewe are adding a flash attribute as well - c'est un attribut qui ne sera pas intégré à l'URL. Ce que nous pouvons réaliser avec ce type d'attribut est - nous pouvons accéder ultérieurement à l'attribut flash en utilisant@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);
 }

Donc, pour conclure - si nous testons la fonctionnalité aveccurl:

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

Nous serons redirigés vers le nouvel emplacement:

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

De cette façon, utiliserRedirectAttribures au lieu d'unModelMap nous donne la possibilité de partager uniquement lessome attributes between the two methods impliqués dans l'opération de redirection.

7. Une configuration alternative sans le préfixe

Explorons maintenant une configuration alternative - une redirection sans utiliser le préfixe.

Pour y parvenir, nous devons utiliser unorg.springframework.web.servlet.view.XmlViewResolver:


    
        /WEB-INF/spring-views.xml
    
    

Au lieu deorg.springframework.web.servlet.view.InternalResourceViewResolver, nous avons utilisé dans la configuration précédente:


Nous devons également définir un beanRedirectView dans la configuration:


    

Maintenant, nous pouvonstrigger 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);
    }
}

Et pour le tester, nous utiliserons à nouveau la commandecurl:

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

Le résultat sera:

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

8. Rediriger une requête HTTP POST

Pour les cas d'utilisation tels que les paiements bancaires, il peut être nécessaire de rediriger une demande HTTP POST. En fonction du code d'état HTTP renvoyé, la demande POST peut être redirigée vers un HTTP GET ou un POST.

Conformément au protocole HTTP 1.1reference, les codes d'état 301 (déplacé de façon permanente) et 302 (trouvé) permettent de changer la méthode de demande de POST à ​​GET. La spécification définit également les codes d'état correspondants 307 (redirection temporaire) et 308 (redirection permanente) qui ne permettent pas de changer la méthode de demande de POST à ​​GET.

Examinons maintenant le code permettant de rediriger une demande de publication vers une autre demande de publication:

@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 http://localhost:8080/spring-rest/redirectPostToPost

Nous sommes redirigés vers l'emplacement prévu:

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

Cet article a illustréthree different approaches to implementing a redirect in Spring, comment gérer / passer des attributs lors de ces redirections ainsi que comment gérer les redirections de requêtes HTTP POST.