Um guia para redirecionamentos de primavera

Um guia para redirecionamentos de primavera

1. Visão geral

Este artigo se concentrará emimplementing a Redirect in Springe discutirá o raciocínio por trás de cada estratégia.

Leitura adicional:

Redirecionar para páginas diferentes após o login com o Spring Security

Exemplo de como redirecionar para páginas diferentes após o login com o Spring Security.

Read more

Spring Security - Redirecionar para o URL anterior após o login

Um pequeno exemplo de redirecionamento após o login no Spring Security

Read more

Controlar a sessão com Spring Security

Configurar sessões com Spring Security - configure sessões simultâneas, ative a proteção de fixação de sessão e evite que URLs contenham informações da sessão.

Read more

2. Por que fazer um redirecionamento?

Vamos primeiro considerarthe reasons why you may need to do a redirect em um aplicativo Spring.

Existem muitos exemplos e razões possíveis, é claro. Um simples pode ser o POST de dados de formulário, contornar o problema de envio duplo ou apenas delegar o fluxo de execução para outro método do controlador.

Uma observação rápida aqui é que o padrão Post / Redirect / Get típico não aborda adequadamente os problemas de envio duplo - problemas como atualizar a página antes que o envio inicial seja concluído podem ainda resultar em um envio duplo.

3. Redirecione com oRedirectView

Vamos começar com esta abordagem simples - ego 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");
    }
}

Nos bastidores,RedirectView acionará umHttpServletResponse.sendRedirect() - que executará o redirecionamento real.

Observe aqui comowe’re injecting the redirect attributes into the method - o framework fará o trabalho pesado aqui e nos permitirá interagir com esses atributos.

Estamos adicionando o atributo de modeloattribute – que será exposto como parâmetro de consulta HTTP. O modelo deve conter apenas objetos - geralmente Strings ou objetos que podem ser convertidos em Strings.

Vamos agora testar nosso redirecionamento - com a ajuda de um comandocurl simples:

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

O resultado será:

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

4. Redirecionar com o prefixoredirect:

A abordagem anterior - usarRedirectView – não é ideal por alguns motivos.

Primeiro, agora estamos acoplados à API Spring porque estamos usandoRedirectView diretamente em nosso código.

Segundo - agora precisamos saber desde o início, ao implementar a operação do controlador - que o resultado sempre será um redirecionamento - o que nem sempre pode ser o caso.

Uma opção melhor é usar o prefixoredirect: - o nome da visualização de redirecionamento é injetado no controlador como qualquer outro nome de visualização lógico. The controller is not even aware that redirection is happening.

Isso é o que parece:

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

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

Quando um nome de visualização é retornado com o prefixoredirect:, oUrlBasedViewResolver (e todas as suas subclasses) reconhecerão isso como uma indicação especial de que um redirecionamento precisa acontecer. O restante do nome da visualização será usado como o URL de redirecionamento.

Uma nota rápida, mas importante, é que - quando usamos esse nome de visualização lógico aqui -redirect:/redirectedUrl - estamos fazendo um redirecionamentorelative to the current Servlet context.

Podemos usar um nome comoa redirect: http://localhost:8080/spring-redirect-and-forward/redirectedUrl se precisarmos redirecionar para uma URL absoluta.

Então, agora, quando executarmos o comandocurl:

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

Seremos redirecionados imediatamente:

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

5. Avançar com o prefixoforward:

Vamos agora ver como fazer algo um pouco diferente - um avanço.

Antes do código, vamos passar dea quick, high-level overview of the semantics of forward vs. redirect:

  • redirect responderá com um 302 e a nova URL no cabeçalhoLocation; o navegador / cliente fará outra solicitação para o novo URL

  • forward acontece inteiramente no lado do servidor; o contêiner Servlet encaminha a mesma solicitação para a URL de destino; o URL não muda no navegador

Agora vamos dar uma olhada no código:

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

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

Da mesma forma queredirect:, o prefixoforward: será resolvido porUrlBasedViewResolvere suas subclasses. Internamente, isso criará umInternalResourceView que faz umRequestDispatcher.forward() para a nova visualização.

Quando executamos o comando comcurl:

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

Obteremos o HTTP 405 (método não permitido):

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

Para finalizar, em comparação com as duas solicitações que tivemos no caso da solução de redirecionamento, neste caso, temos apenas uma solicitação saindo do navegador / cliente para o lado do servidor. O atributo que foi adicionado anteriormente pelo redirecionamento também está ausente.

6. Atributos comRedirectAttributes

A seguir - vamos olhar mais de perto empassing attributes in a redirect - fazendo uso total da estrutura comRedirectAttribures:

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

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

Como vimos anteriormente, podemos injetar diretamente o objeto de atributos no método - o que torna esse mecanismo muito fácil de usar.

Observe também quewe are adding a flash attribute as well - este é um atributo que não será incluído no URL. O que podemos alcançar com esse tipo de atributo é - podemos acessar posteriormente o atributo flash usando@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);
 }

Então, para finalizar - se testarmos a funcionalidade comcurl:

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

Seremos redirecionados para o novo local:

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

Dessa forma, usarRedirectAttribures em vez deModelMap nos dá a capacidade de compartilhar apenassome attributes between the two methods que estão envolvidos na operação de redirecionamento.

7. Uma configuração alternativa sem o prefixo

Vamos agora explorar uma configuração alternativa - um redirecionamento sem usar o prefixo.

Para conseguir isso, precisamos usar umorg.springframework.web.servlet.view.XmlViewResolver:


    
        /WEB-INF/spring-views.xml
    
    

Em vez deorg.springframework.web.servlet.view.InternalResourceViewResolver, usamos na configuração anterior:


Também precisamos definir um beanRedirectView na configuração:


    

Agora podemostrigger 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);
    }
}

E para testá-lo, usaremos novamente o comandocurl:

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

O resultado será:

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

8. Redirecionando uma solicitação HTTP POST

Para casos de uso como pagamentos bancários, talvez seja necessário redirecionar uma solicitação HTTP POST. Dependendo do código de status HTTP retornado, a solicitação POST pode ser redirecionada para um HTTP GET ou POST.

De acordo com o protocolo HTTP 1.1reference, os códigos de status 301 (Movido Permanentemente) e 302 (Encontrado) permitem que o método de solicitação seja alterado de POST para GET. A especificação também define os códigos de status 307 (Redirecionamento temporário) e 308 (Redirecionamento permanente) correspondentes que não permitem que o método de solicitação seja alterado de POST para GET.

Agora vamos ver o código para redirecionar uma solicitação de postagem para outra solicitação de postagem:

@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

Estamos sendo redirecionados para o local de destino:

> 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. Conclusão

Este artigo ilustrouthree different approaches to implementing a redirect in Spring, como lidar / passar atributos ao fazer esses redirecionamentos e também como lidar com redirecionamentos de solicitações HTTP POST.