Spring redirect tutorial
last modified October 18, 2023
Spring redirect tutorial shows how to redirect a request in a Spring web application.
Spring is a popular Java application framework for creating enterprise applications.
Redirect
The special redirect:
prefix in a view name performs a redirect to
different URL. The net effect is the same as if the controller had returned a
RedirectView
. Redirects are applied in Post/Redirect/Get (PRG)
scenarios; PGR is a web development design pattern that prevents duplicate form
submissions. By default status code 302 is sent. If we want to change that we can
annotate the return type of the handler method with @ResponseStatus
.
RedirectAttributes
is a specialization of the Model interface that
controllers can use to select attributes for a redirect scenario.
Redirect vs Forward
A request can be basically processed in three ways: a) resolved by Spring in a controller action, b) forwarded to a different controller action, c) redirected to client to fetch another URL.
Forward:
- performed internally by Spring
- the browser is completely unaware of forward, so its original URL remains intact
- a browser reload of the resulting page repeats the original request, with the original URL
- data sent in the request is available to the forwarded action
Redirect:
- is a two step process
- Spring instructs the browser to fetch a second URL, which differs from the original
- a browser reload of the second URL will not repeat the original request, but will rather fetch the second URL
- data sent in the original request scope is not available to the second request
Spring Redirect example
The following application uses redirects to a different URL after a form submission.
It performs a redirect with the redirect:
prefix.
pom.xml src ├───main │ ├───java │ │ └───com │ │ └───zetcode │ │ ├───bean │ │ │ User.java │ │ ├───config │ │ │ MyWebInitializer.java │ │ │ WebConfig.java │ │ └───controller │ │ MyController.java │ └───resources │ │ logback.xml │ │ │ └───templates │ addUser.html │ showUserAdded.html └───test └───java
This is the project structure.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zetcode</groupId> <artifactId>springredirectex</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <spring-version>5.3.23</spring-version> </properties> <dependencies> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.23</version> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.11.RELEASE</version> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.0.11.RELEASE</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.3.2</version> </plugin> <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.4.49.v20220914</version> </plugin> </plugins> </build> </project>
In the pom.xml
we have the necessary dependencies.
<?xml version="1.0" encoding="UTF-8"?> <configuration> <logger name="org.springframework" level="ERROR"/> <logger name="com.zetcode" level="INFO"/> <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <Pattern>%d{HH:mm:ss.SSS} %blue(%-5level) %magenta(%logger{36}) - %msg %n </Pattern> </encoder> </appender> <root> <level value="INFO" /> <appender-ref ref="consoleAppender" /> </root> </configuration>
The logback.xml
is a configuration file for the Logback logging library.
package com.zetcode.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; @Configuration public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return null; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{WebConfig.class}; } @Override protected String[] getServletMappings() { return new String[]{"/"}; } }
MyWebInitializer
registers the Spring DispatcherServlet
, which
is a front controller for a Spring web application.
@Override protected Class<?>[] getServletConfigClasses() { return new Class[]{WebConfig.class}; }
The getServletConfigClasses
returns a web configuration class.
package com.zetcode.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ViewResolverRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.thymeleaf.spring5.SpringTemplateEngine; import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; import org.thymeleaf.spring5.view.ThymeleafViewResolver; @Configuration @EnableWebMvc @ComponentScan(basePackages = {"com.zetcode"}) public class WebConfig implements WebMvcConfigurer { @Autowired private ApplicationContext applicationContext; @Bean public SpringResourceTemplateResolver templateResolver() { var templateResolver = new SpringResourceTemplateResolver(); templateResolver.setApplicationContext(applicationContext); templateResolver.setPrefix("classpath:/templates/"); templateResolver.setSuffix(".html"); return templateResolver; } @Bean public SpringTemplateEngine templateEngine() { var templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver()); templateEngine.setEnableSpringELCompiler(true); return templateEngine; } @Bean public ViewResolver viewResolver() { var resolver = new ThymeleafViewResolver(); var registry = new ViewResolverRegistry(null, applicationContext); resolver.setTemplateEngine(templateEngine()); registry.viewResolver(resolver); return resolver; } }
WebConfig
configures Thymeleaf template engine. We set the
template files location to templates
directory on the classpath.
(The resources
is on the classpath.)
package com.zetcode.bean; public class User { private String name; private String occupation; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getOccupation() { return occupation; } public void setOccupation(String occupation) { this.occupation = occupation; } @Override public String toString() { final StringBuilder sb = new StringBuilder("User{"); sb.append("name='").append(name).append('\''); sb.append(", occupation='").append(occupation).append('\''); sb.append('}'); return sb.toString(); } }
This is the User
bean. It is filled with data from the form.
package com.zetcode.controller; import com.zetcode.bean.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.servlet.mvc.support.RedirectAttributes; @Controller public class MyController { private static final Logger logger = LoggerFactory.getLogger(MyController.class); @GetMapping("/addUser") public String sendForm(User user) { return "addUser"; } @PostMapping("/addUser") public String processForm(User user, RedirectAttributes redirectAttrs) { logger.info("User {} has been saved", user.getName()); redirectAttrs.addAttribute("name", user.getName()); return "redirect:userAdded"; } @GetMapping("/userAdded") public String userAdded() { return "showUserAdded"; } }
MyController
provides mappings between request paths and handler methods.
@GetMapping("/addUser") public String sendForm(User user) { return "addUser"; }
This mapping sends the form to the user.
@PostMapping("/addUser") public String processForm(User user, RedirectAttributes redirectAttrs) { logger.info("User {} has been saved", user.getName()); redirectAttrs.addAttribute("name", user.getName()); return "redirect:userAdded"; }
This mapping processes the form. A new user is "saved" and a redirect is performed.
User's name is added to the redirect attributes with addAttribute
.
<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Add user</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>Add User</h1> <form action="#" th:action="@{/addUser}" th:object="${user}" method="post"> <p> Name: <input type="text" th:field="*{name}"> </p> <p> Occupation: <input type="text" th:field="*{occupation}"> </p> <p> <input type="submit" value="Submit"/> <input type="reset" value="Reset"> </p> </form> </body> </html>
The addUser.html
template provides a form to the user. The entered fields
are automatically inserted into the User's
attributes.
<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>User saved</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h2>User has been saved</h2> <p th:text="'Name: ' + ${#request.getParameter('name')}"></p> <a href="/addUser">Add another user</a> </body> </html>
This templates shows a message to the user after the form submission.
It reads the redirect attribute with ${#request.getParameter('name')}
.
$ mvn jetty:run
We run the server and locate to localhost:8080/addUser
.
In this article we have performed a redirect in a Spring controller.
Author
List all Spring tutorials.