Controller Basics
Request Mapping
project_add.jsp
Model Basics
Autowiring Controller Services
ProjectController.java(only code to be added given)
URI Templates
Method Arguments
Data Binding
Advanced Concepts
Project.java
Data Binding Lists
Model Attributes
Session Attributes
Session Status
Response Body
Request Body Annotation
Redirect Request
RedirectAttributes Interface
REST Controller
- Controller handles urls
- Dispatcher Servlet receives the url and then forwards processing to a controller method.
- We annotate a normal class with a controller annotation to mark it as a controller.
- We need to make sure that this controller gets picked up by component scanning so we define a component scan inside Dispatcher Servlet context.
- <context:component-scan base package="com.springimplant.mvc.controllers" />
- We define the controller package inside dispatcher servlet.
- When IOC container starts up it creates the controller as a bean and makes it available for request processing.
- To map the controller methods that will be processing request we use the request mapping annotation
- This annotation can be applied at the type or method level.
- When applied at type level we do not perform actual mapping.Everything that matches particular path will be handled by the controller.
- We can further provide refinement by providing this annotation at controller methods.
- These methods are called as handler methods because they service the request provided by the dispatcher servlet.
- Next we need to look at a way to share information between controller and view
- We do this using a model
- In our method in controller class we are supplying an argument of type Model with name model.
- This is used to share information between view and controller
- Model is basically a map which takes key value pairs which are needed while adding an attribute to the model.
- These key value pairs are referred to as an attribute of Model.
- Since we are using the MVC annotation driven tag each attribute in the model will be termed into an attribute in the request.
- This allows us to access that attribute via jspel or jsp expression language within our view.
- See our jsp file we are using a attribute msg which we passed in our method
- To get the information passed inside a model we rely on service
- In order to use a service within Spring we need to autowire the same in our controller.
- This allows us to perform logic or persistence of data using services.
- You can find an example service being autowired in our controller(commented for now).
URI Templates
- We can also pass data into our request url
- Here we have a method "test" with data test id in our Request Mapping.
- The testid is a variable. To extract the value of this variable we use an annotation called as "@Path Variable".
- See how we have used this annotation in our method test.
- This value is then automatically converted by Spring to appropriate datatype.
- There are 30 different types of arguments that we can pass into controller.
- We can even extract a Request Parameter value and assign it to our arguments in methods.
- We use the "@RequestParam("action")" annotation to perform the same.
- The Request parameters are in the form of name value pairs.
- Here in method test we are extracting the Request Parameter called as "action".
- Again we are typecasting the same into a String.
- We are also able to pass Http Servelet Request Object and Http Session in our methods.
- Data binding can also be performed in a controller method using annotation @ModelAttribute
- When a request comes into a server it contains different parameters.
- Parameters are basically name value pairs.
- The "Model attribute" annotation maps these parameters to the corresponding names into the Model Attribute class.
- Here we are using an example class for model attributes.
- Save method is handling this request we have defined an argument of type example in method and we have annotated the same with @ModelAttribute annotation.
- Spring will look at example type which has an attribute "name" of type String.
- It will compare the names of fields in example class with names of Request Parameters.
- If the key matches it will take the value of Request Parameter and set it to the field name in example class.
package com.gaurav.mvc.controllers;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class HomeController {
// private ExampleService service;
@RequestMapping("/home")
public String goHome()
{
return "home";
}
@RequestMapping("/add")
public String add(Model model)
{
model.addAttribute("msg","Spring Mvc");
return "home";
}
@RequestMapping("/test/{testId}")
public String test(@PathVariable("testId") Long testId,Model model)
{
model.addAttribute("msg",testId);
return "home";
}
@RequestMapping("/example")
public String test(@RequestParam("action") String action,Model model)
{
model.addAttribute("msg",action);
return "home";
}
@RequestMapping("/example2")
public String test2(HttpServletRequest request,HttpSession session)
{
return "home";
}
@RequestMapping("/save")
public String save(@ModelAttribute Example example,Model model)
{
model.addAttribute("msg",example.getName());
return "home";
}
}
- Controllers are used to provide access to an application's behavior.
- A url is serviced by Controller which interacts with service of our application.
- Controllers also interpret user input and transform it into model.
- The model is then shown to user as view.
- Other logic's such as data persistence is performed within a controller.
- As of Spring 2.5 the main approach of using controllers has been annotation driven.
- Spring 2.5 provides annotation such as
- Controller Annotation
- Request Mapping Annotation
- Model Attribute Annotation
- These annotations are used within a controller to determine how they are going to process a request.
- There is no dependencies on J2EE specific classes such as servelets.
- There are no classes that you need to extend using a controller.
- We have a file home.jsp which includes file header.jsp
- header.jsp includes header navigation links
- When we click on Add Project navigation link it goes to project "project_add.jsp".
- Lets create a new class Project Controller which is defined with "@Controller" annotation.
- This class is now available as controller and allow us for request processing.
- The dispatcher servlet IOC container is indicated that this class will serve as one of our controllers.
- When dispatcher servlet will try to route a url from a request this class will be a candidate for routing if it matches url of the request.
- We need to enable component scanning for this package in dispatcher-servlet.xml
- This project controller will be available as a bean in IOC container and will be available for request processing.
- We create a method addProject()
- This method returns a String which is the name of our view i.e. "project_add".
- We will use this method for request handling
- We will add request Mapping annotation to our Project Controller("/project").
- This makes the class available for request mapping for a particular path.
- We will also provide a request mapping annotation to our method i.e("/add")
- When we run our project and click on add it displays the form in project_add.jsp.
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">Project Management</a>
</div>
<ul class="nav navbar-nav">
<li><a href="#">Home</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Projects
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="<spring:url value="/project/add"/>">Add</a></li>
<li><a href="#">Find</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Resources
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="#">Add</a></li>
<li><a href="#">Find</a></li>
</ul>
</li>
</ul>
</div>
</nav>
package com.gaurav.mvc.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/project")
public class ProjectController {
@RequestMapping("/add")
public String addProject()
{
return "project_add";
}
}
Request Mapping
- Till now we have used request mapping annotation above Project controller and methods to determine which controller and action needs to be selected on a url hit.Lets look further in this
- We add a new function saveProject() with some Request Mapping value i.e(value="/add")
- If we receive a request with "/project/add" the dispatcher servelet will not be able to predict which method to invoke since both have similer parameters.
- In such a case let us add our first parameter to RequestMapping annotation ("method=RequestMethod.GET" and "method=RequestMethod.POST") to methods addProject and saveProject respectively.
- In the project_add.jsp file we add "action value=" parameter to form element and pass url "/project/add/" and method = "post".So this form will post to url "/project/add".
- In menu we already have a GET request to "/project/add".
- "addProject" and "invokeProject" will be shown in console output when we visit the form and when we submit the form.
- Next we add another function "saveMultiProject" with same url parameters value="/add" ,method=RequestMethod.POST,params={"type=multi"})
- Here we have a new attribute with name params this will make sure any url that comes with "type=multi" parameter invokes this method.
- Now let us add parameters multi and special to our another function "saveSpecial".
- @RequestMapping(value="/add",method=RequestMethod.POST,params={"type=multi","special"})
- This function is invoked when we get method params as "type=multi" & special as value of another parameter.
- We have marked console output in each of the functions so that we know when each one has been invoked.
package com.gaurav.mvc.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/project")
public class ProjectController {
@RequestMapping(value="/add",method=RequestMethod.GET)
public String addProject()
{
System.out.println("Invoking addProject");
return "project_add";
}
@RequestMapping(value="/add",method=RequestMethod.POST)
public String saveProject()
{
System.out.println("Invoking saveProject");
return "project_add";
}
@RequestMapping(value="/add",method=RequestMethod.POST,params={"type=multi"})
public String saveMulti()
{
System.out.println("Invoking saveMulti");
return "project_add";
}
@RequestMapping(value="/add",method=RequestMethod.POST,params={"type=multi","special"})
public String saveSpecial()
{
System.out.println("Invoking saveSpecial");
return "project_add";
}
}
project_add.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Project Manager</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<link rel="stylesheet" href="<spring:url value="/resources/css/home.css"/>" type="text/css"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
</head>
<body>
<jsp:include page="../views/fragments/header.jsp"></jsp:include>
<div class="container">
<div class="row">
<form action="<spring:url value="/project/add"/>" method="post" class="col-md-8 col-md-offset-2">
<div class="form-group">
<label for="project-name">Name</label>
<input type="text" id="project-name" class="form-control" name="name"/>
</div>
<div class="form-group">
<label for="project_type">Type</label>
<select name="type" class="selectpicker">
<option></option>
<option value="single">Single Year</option>
<option value="multi">Multi-Year</option>
</select>
</div>
<div class="form-group">
<label for="sponsor">Sponsor</label>
<input id="sponsor" type="text"
class="form-control" name="sponsor"/>
</div>
<div class="form-group">
<label for="funds">Authorized Funds</label>
<input id="funds" type="text"
class="form-control" name="authorized_funds"/>
</div>
<div class="form-group">
<label for="hours">Authorized Hours</label>
<input id="hours" type="text"
class="form-control" name="authorized_hours"/>
</div>
<div class="form-group">
<label for="project-name">Description</label>
<textarea class="form-control" rows="3"></textarea>
</div>
<div class="form-group">
<label for="special">Special</label>
<input id="special" name="special" type="checkbox"/>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</body>
</html>
Model Basics
- Model is used to abstract all of our data or information in the system from view.
- Domain objects or entities are not available in jsp files or any other views.
- Let us create a project class with attributes ("projectId","name","description","sponsor","authorizedHours","authorizedFunds","year","special","type").(see project.java file below)
- We will display its object on our "home.jsp" page.
- In our controllers we will collect information related to our entity or domain object and pass it into our model and then this model will be available in our view.
- Let us pass the "object of Model" as argument in Controller method.
- Create and initialize project object.
- Place this object into model using "addAttribute" method.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Project Manager</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<link rel="stylesheet" href="<spring:url value="/resources/css/home.css"/>" type="text/css"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
</head>
<body>
<jsp:include page="../views/fragments/header.jsp"></jsp:include>
<div class="container">
<h2>Current Project</h2>
<ul class="list-group">
<li class="list-group-item"><label>Project Name:</label><span>${currentProject.name}</span></li>
<li class="list-group-item"><label>Sponsor:</label><span>${currentProject.sponser}</span></li>
<li class="list-group-item"><label>Description:</label><br/><span>${currentProject.description}</span></li>
</ul>
<h1>This page is called using a view resolver</h1>
<span>${msg}</span>
</div>
</body>
</html>
Project.java
package com.springimplant.mvc.data.entities;
import java.math.BigDecimal;
public class Project
{
private Long projectId;
private String name;
private String description;
private String sponser;
private BigDecimal authorizedHours;
private BigDecimal authorizedFunds;
private String year;
private boolean special;
private String type;
public Long getProjectId() {
return projectId;
}
public void setProjectId(Long projectId) {
this.projectId = projectId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getSponser() {
return sponser;
}
public void setSponser(String sponser) {
this.sponser = sponser;
}
public BigDecimal getAuthorizedHours() {
return authorizedHours;
}
public void setAuthorizedHours(BigDecimal authorizedHours) {
this.authorizedHours = authorizedHours;
}
public BigDecimal getAuthorizedFunds() {
return authorizedFunds;
}
public void setAuthorizedFunds(BigDecimal authorizedFunds) {
this.authorizedFunds = authorizedFunds;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
public boolean isSpecial() {
return special;
}
public void setSpecial(boolean special) {
this.special = special;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
HomeController.java
package com.gaurav.mvc.controllers;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.springimplant.mvc.data.entities.Project;
@Controller
public class HomeController {
// private ExampleService service;
@RequestMapping("/")
public String goHome(Model model)
{
Project project=new Project();
project.setName("Java Tutorial");
project.setSponser("Spring Implant");
project.setDescription("A simple project initiated to Learn Java with Fun");
model.addAttribute("currentProject",project);
return "home";
}
@RequestMapping("/add")
public String add(Model model)
{
model.addAttribute("msg","Spring Mvc");
return "home";
}
@RequestMapping("/test/{testId}")
public String test(@PathVariable("testId") Long testId,Model model)
{
model.addAttribute("msg",testId);
return "home";
}
@RequestMapping("/example")
public String test(@RequestParam("action") String action,Model model)
{
model.addAttribute("msg",action);
return "home";
}
@RequestMapping("/example2")
public String test2(HttpServletRequest request,HttpSession session)
{
return "home";
}
@RequestMapping("/save")
public String save(@ModelAttribute Example example,Model model)
{
model.addAttribute("msg",example.getName());
return "home";
}
}
Autowiring Controller Services
- Our controller may invoke different methods on services within our application.
- Controller may use different beans within the "rootApplicationContext"
- For this we need to autowire our beans and services in controller so that they can be used in our application and perform business logic.
- We will create a search service which will look up for projects.
- We will create a page called as "projects.jsp" inside our views which will list all projects.
- Next we will create a Project Service with name "ProjectService.java".
- This service returns a list of projects that we have initialized inside the constructor of the service.
- This service needs to be established as a bean in "rootApplicationContext".For this we use a bean tag give it an "id" a "class"(see updated file of "applicationContext.xml").
- We create a new method "find()" inside our Projects Controller file that will return view "projects".(see updated file Project Controller.java)
- To call our service bean we use @Autowired annotation
- Create a private field of type ProjectService.
- Inside find method we add projectservice field to model and invoke findAll() method.
- This will place the list of our projects under attribute name "projects".
- projectservice is a bean included in our applicationContext.
- In the jsp we will use jstl library and use its for each to iterate through each of our projects as follows.
<c:forEach items="${projects}" var="project">
<tr>
<td>${project.name}</td><td>${project.sponser}</td><td>${project.description}</td>
</tr>
</c:forEach>
ApplicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="projectService" class="com.springimplant.mvc.data.services.ProjectService"/>
</beans>
@Autowired
private ProjectService projectService;
@RequestMapping(value="/find")
public String find(Model model)
{
model.addAttribute("projects",this.projectService.findAll());
return "projects";
}
Projects.java <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Project Manager</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<link rel="stylesheet" href="<spring:url value="/resources/css/home.css"/>" type="text/css"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
</head>
<body>
<jsp:include page="../views/fragments/header.jsp"></jsp:include>
<div class="container">
<h2>Projects</h2>
<table class="table table-hover">
<tbody>
<tr>
<th>Name</th><th>Sponsor</th><th>Description</th>
</tr>
<c:forEach items="${projects}" var="project">
<tr>
<td>${project.name}</td><td>${project.sponser}</td><td>${project.description}</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</body>
</html>
ProjectService.java package com.springimplant.mvc.data.services;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import com.springimplant.mvc.data.entities.Project;
public class ProjectService {
private List<Project> projects = new LinkedList<>();
public ProjectService(){
Project p1 = this.createProject("Project 1", "This is description for Project 1" );
Project p2 = this.createProject("Project 2", "This is overview of Project 2");
Project p3 = this.createProject("Project 3", "This explains Project 3");
this.projects.addAll(Arrays.asList(new Project[]{p1,p2,p3}));
}
public List<Project> findAll(){
return this.projects;
}
private Project createProject(String title, String description) {
Project project = new Project();
project.setName(title);
project.setAuthorizedFunds(new BigDecimal("100000"));
project.setAuthorizedHours(new BigDecimal("1000"));
project.setSponser("Spring MVC Implant");
project.setProjectId(1L);
project.setSpecial(false);
project.setType("multi");
project.setYear("2015");
project.setDescription(description);
return project;
}
}
URI Templates
- We can pass data back to controller via URI template.
- A URI template inputs a variable into the URL that our dispatcher servlet will be serving request for.
- Restful urls,Restful Api's use Restful URI Templates to get data for example "/book/{isbn}" will fetch data for a book by ISBN.
- Taking forward our example let us create each project name on our "find" page as a hyperlink which links to a project description page.
- We will insert a URI template within the url.
- This URI template will have a project id which controller will use to fetch information regarding the project.
- We will display this information in "project.jsp" file.
- In href attribute of anchor tag we use "<spring:url value="/project/${project.projectId}" />"
- This is called as a URI template.
- Next we will create a method in project controller with name "findProject()"
- This will return a string "project" to display "project.jsp".
- "project.jsp" will be our new view which will show information related to a project.
- We will fetch the project object from the "find" method which we have defined in our Project Service and add it to the model passed into the method of controller.
- To this method add request mapping to value "/{projectId}" since we already have "/project" as controller request mapping.
- This will call this method when ever we hit "/project/{projectId}"
- To assign url template variable to our variable passed as argument in method use
- @PathVariable("projectId") Long projectId.(see updated controller file)
- Run the application and click on the project name on find page you will see the information of Project.
Method added to Project Controller
Method added to Project Service
Changes to projects.jsp
project.jsp @RequestMapping(value="/{projectId}")
public String findProject(Model model, @PathVariable("projectId") Long projectId) {
model.addAttribute("project",this.projectService.find(projectId));
return "project";
}
Method added to Project Service
public Project find(Long projectId){
return this.projects.stream().filter(p -> {
return p.getProjectId().equals(projectId);
}).collect(Collectors.toList()).get(0);
}
Changes to projects.jsp
<td><a href='<spring:url value="/project/${project.projectId}"/>'>${project.name}</a></td>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Project Manager</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<link rel="stylesheet" href="<spring:url value="/resources/css/home.css"/>" type="text/css"/>
<link rel="stylesheet" href="<spring:url value="/resources/css/bootstrap-select.min.css"/>" type="text/css"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script src="<spring:url value="/resources/js/bootstrap-select.min.js"/>"></script>
</head>
<body>
<jsp:include page="../views/fragments/header.jsp"></jsp:include>
<div class="container">
<div class="row">
<div class="form-group">
<label for="project-name">Name</label>
<span>${project.name}</span>
</div>
<div class="form-group">
<label for="project_type">Type</label>
<span>${project.type }</span>
</div>
<div class="form-group">
<label for="sponsor">Sponsor</label>
<span>${project.sponser}</span>
</div>
<div class="form-group">
<label for="funds">Authorized Funds</label>
<span>${project.authorizedFunds}</span>
</div>
<div class="form-group">
<label for="hours">Authorized Hours</label>
<span>${project.authorizedHours}</span>
</div>
<div class="form-group">
<label for="project-name">Description</label>
<span>${project.description}</span>
</div>
<div class="form-group">
<label for="special">Special</label>
<span>${project.special == true ? 'Yes':'No'}</span>
</div>
</div>
</div>
</body>
</html>
Method Arguments
- The controller handler methods have very flexible signatures.
- For example in "findProject" method we are passing Model as well as ProjectId
- In "find" method we are passing only model.
- The entire list of these arguments is available in Spring documentation
- Some of the commonly used arguments are as follows.
- HttpSession argument
- All the session attributes can be set or get using this argument.
- HttpServletRequest argument
- We can get any Http request parameter using this argument.
- @RequestParam("name") String name
- Used to get a specified request parameter.
- We can also typecast Request Parameters into our required type using this argument.
- Example @RequestParam("name") Long name
@RequestMapping(value="/add",method=RequestMethod.GET)
public String addProject(HttpSession session)
{
session.setAttribute("token","12345");
System.out.println("Invoking addProject");
return "project_add";
}
@RequestMapping(value="/add",method=RequestMethod.POST)
public String saveProject(HttpServletRequest request,@RequestParam("name") String name,HttpSession session)
{
System.out.println(session.getAttribute("token"));
System.out.println(request.getParameter("name"));
System.out.println(name);
System.out.println("Invoking saveProject");
return "project_add";
}
Data Binding
- Data Binding simplifies how we handle form processing.
- Data in a class properties is bound to the "name" attribute in fields of a form.
- The parameters from an HTML form are sent over in a request.
- The request comes to the controller side and is processed by the server handler method.
- In our case it will hit the "saveproject" method with request type as post.
- We will pull out each of the parameter values from the request and create a project object.
- Using data binding we can map the entire request object to the corresponding class object(Project class in this case)
- We pass an attribute called as project of type Project Class in the method.
- Before this attribute add "@ModelAttribute" annotation.
- Spring will match each attribute in request to the properties in Project Class.
- It assigns the value from the request to the field with corresponding name in Project Class.
- We have added Console output to "setName" method.
- Added a toString() method to the Project Class.
- Corrected/Added the "name" values in jsp in the HTML form so that they match Project properties so that we can print the updated object.
- When we now print "Project" object in saveProject() we find that values are mapped.
@RequestMapping(value="/add",method=RequestMethod.POST)
public String saveProject(@ModelAttribute Project project,HttpServletRequest request,@RequestParam("name") String name,HttpSession session)
{
System.out.println(session.getAttribute("token"));
System.out.println(request.getParameter("name"));
System.out.println(name);
System.out.println(project);
System.out.println("Invoking saveProject");
return "project_add";
}
Advanced Concepts
- Advanced concepts in controllers include following
- More complex data bindings.
- Includes mapping to Collection objects and Composite Objects.
- Present additional methods using annotations for storing model attributes.
- Model attribute annotation to store attributes on model.
- This is alternative way to store objects in our model
- One context allows data binding and other allows storing objects within the model.
- Discover how to store session attributes.
- Used to persist data in each request.
- Learn how to work with the request and response bodies.
- Response body is used to directly output message via controllers without involving jsp pages.
- Request body allows data coming in from a page to be mapped directly to one of our controller handler methods arguments.
- Helps to make manipulations on request object.
- Let us create a Sponsor class to represent details of a sponsor.
- Let us change the property sponsor in our project controller to an object of class Sponsor type.
- Generate new Getter and Setter methods for this property.
- Next we change our project_add.jsp file and add form tags to it replacing the current HTML tags.
- We have form tags for all the types except the sponsor type which is an object of Sponsor class.
- We will be adding details of the Sponsor i.e. the Sponsor Name,email,phone as defined in Sponsor class along with the Project.
- We have added 3 new input fields for this which are "Sponsor Name","Sponsor Email","Sponsor Phone" replacing the original Sponsor field.
- Now we need to convert these to Form Tag Library Fields.
- The main issue we will face is for the path property.
- We will define the path of these fields as "sponsor.name","sponsor.email","sponsor.phone" as shown in file.
- This accesses the project object's sponsor field's name,email and phone field respectively.
- This will automatically add Sponsor data to the respective object.
- In actual these values are represented as "project.sponsor.name","project.sponsor.email","project.sponsor.phone". But since we already have form's model attribute property as "project" we define path from "sponsor" only.
- The changes made to the files are as follows
Sponsor.java
package com.springimplant.mvc.data.entities;
public class Sponsor {
private String name;
private String phone;
private String email;
public Sponsor() {
}
public Sponsor(String name, String phone, String email) {
super();
this.name = name;
this.phone = phone;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
@Override
public String toString() {
return "Sponsor [name=" + name + ", phone=" + phone + ", email=" + email + "]";
}
public void setEmail(String email) {
this.email = email;
}
}
package com.springimplant.mvc.data.entities;
import java.math.BigDecimal;
public class Project {
private Long projectId;
private String name;
private String description;
private Sponsor sponsor;
private BigDecimal authorizedHours;
private BigDecimal authorizedFunds;
private String year;
private boolean special;
private String type;
public Long getProjectId() {
return projectId;
}
public void setProjectId(Long projectId) {
this.projectId = projectId;
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("Field Name Binded to object");
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Sponsor getSponsor() {
return sponsor;
}
public void setSponsor(Sponsor sponsor) {
this.sponsor = sponsor;
}
public BigDecimal getAuthorizedHours() {
return authorizedHours;
}
public void setAuthorizedHours(BigDecimal authorizedHours) {
this.authorizedHours = authorizedHours;
}
public BigDecimal getAuthorizedFunds() {
return authorizedFunds;
}
public void setAuthorizedFunds(BigDecimal authorizedFunds) {
this.authorizedFunds = authorizedFunds;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
public boolean isSpecial() {
return special;
}
public void setSpecial(boolean special) {
this.special = special;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "Project [projectId=" + projectId + ", name=" + name + ", description=" + description + ", sponser="
+ sponsor + ", authorizedHours=" + authorizedHours + ", authorizedFunds=" + authorizedFunds + ", year="
+ year + ", special=" + special + ", type=" + type + "]";
}
}
project_add.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Project Manager</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<link rel="stylesheet" href="<spring:url value="/resources/css/home.css"/>" type="text/css"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
</head>
<body>
<jsp:include page="../views/fragments/header.jsp"></jsp:include>
<div class="container">
<div class="row">
<spring:url value="/project/add" var="formUrl" htmlEscape="true"/>
<form:form method="POST" action="${formUrl}" modelAttribute="project">
<div class="form-group">
<label for="project-name">Name</label>
<form:input path="name" id="project-name" cssClass="form-control"/>
</div>
<div class="form-group">
<label for="project_type">Type</label>
<form:select path="type" cssClass="selectpicker" items="${types}"></form:select>
</div>
<div class="form-group">
<label for="sponsor-name">Sponsor Name</label>
<form:input id="sponsor-name" path="sponsor.name" cssClass="form-control"/>
</div>
<div class="form-group">
<label for="sponsor-phone">Sponsor Phone</label>
<form:input id="sponsor-phone" path="sponsor.phone" cssClass="form-control"/>
</div>
<div class="form-group">
<label for="sponsor-email">Sponsor Email</label>
<form:input id="sponsor-email" path="sponsor.email" cssClass="form-control"/>
</div>
<div class="form-group">
<label for="funds">Authorized Funds</label>
<form:input path="authorizedFunds" id="funds" cssClass="form-control"/>
</div>
<div class="form-group">
<label for="hours">Authorized Hours</label>
<form:input path="authorizedHours" id="hours" cssClass="form-control"/>
</div>
<div class="form-group">
<label for="project-name">Description</label>
<form:textarea path="description" id="description" cssClass="form-control" rows="3"/>
</div>
<div class="form-group">
<label for="special">Special</label>
<form:checkbox path="special" id="special" />
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form:form>
</div>
</div>
</body>
</html>
Data Binding Lists
- Let's add field of type List<String> to Project class called as pointsOfContact in Project Entity.
- Add setters and getters for this field.
- Update the toString() method.
- Now Let us add a form input field for this in project_add.jsp
- We will add 3 such fields and label them as POC,POC2,POC3.
- Next set the path property of these fields as "pointsOfContact[0]", "pointsOfContact[2]", "pointsOfContact[2]" respectively.
- This points to the list pointsOfContact and binds them to the first Item,second Item and Third Item in the list.
- The changed files are shown below
Project.java
private List<String> pointsOfContact;
public List<String> getPointsOfContact() {
return pointsOfContact;
}
public void setPointsOfContact(List<String> pointsOfContact) {
this.pointsOfContact = pointsOfContact;
}
@Override
public String toString() {
return "Project [projectId=" + projectId + ", name=" + name + ", description=" + description + ", sponsor="
+ sponsor + ", authorizedHours=" + authorizedHours + ", authorizedFunds=" + authorizedFunds + ", year="
+ year + ", special=" + special + ", type=" + type + ", pointsOfContact=" + pointsOfContact + "]";
}
project_add.jsp <div class="form-group">
<label for="poc">POC</label>
<form:input id="poc" path="pointsOfContact[0]" cssClass="form-control"/>
</div>
<div class="form-group">
<label for="poc2">POC 2</label>
<form:input id="poc2" path="pointsOfContact[1]" cssClass="form-control"/>
</div>
<div class="form-group">
<label for="poc3">POC 3</label>
<form:input id="poc3" path="pointsOfContact[2]" cssClass="form-control"/>
</div>
Model Attributes
- Model attribute annotation has two contexts
- When we annotate a method argument.
- We retrieve this argument from Model
- We check if there is a class of type Resource available to be provided as argument of this method.
- If we can't find class in Model we will create a new class using default constructor and we will check Request Object or Request Params passed in and map fields by name on fields of Resource Object.
- This is called as Data Binding
- The other context is to annotate method of controller
- Makes sure returned object is placed in model as an attribute.
- For example in below annotation the returned object from method will be placed in model under "checkOptions" attribute
- @ModelAttribute("checkOptions")
- We can prepopulate radio buttons and check boxes using these attributes.
- We can also prepopulate form's "modelAttribute" using this.
- Command/Hidden objects in form used to backup data.
@RequestMapping("/add")
public String add(Model model)
{
return "resource_add";
}
@ModelAttribute("resource")
public Resource getResource()
{
return new Resource();
}
@ModelAttribute("checkOptions")
public List<String> getChecks()
{
return new LinkedList<>(Arrays.asList(new String[]{"Lead Time","Special Rate","Requires Approval"}));
}
@ModelAttribute("radioOptions")
public List<String> getRadios()
{
return new LinkedList<>(Arrays.asList(new String[]{"Hours","Piece","Tons"}));
}
@ModelAttribute("itemOptions")
public List<String> getItems()
{
return new LinkedList<>(Arrays.asList(new String[]{"Lead Time","Special Rate","Requires Approval"}));
}
Session Attributes
- Used to store model attributes between requests.
- We need to have persistent data between different requests.
- We store this information using a session attribute using in a servelet api.
- We use session attribute annotation to achieve this functionality.
- We store our Model variables into the Session.
- We have added another review page before final submission of data to check this.
- So our Add Page will post to review page which will in turn post to Save Page
- The syntax for session attribute annotation is as follows.
- @SessionAttribute("resource")
- Session Attribute is persistent only to a controller so we cannot use it to persist objects between controllers.
package com.springimplant.mvc.controllers;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import com.springimplant.mvc.data.entities.Resource;
@Controller
@RequestMapping("/resource")
@SessionAttributes("resource")
public class ResourceController {
@RequestMapping("/review")
public String review(@ModelAttribute Resource resource)
{
System.out.println("Invoking Review() method");
return "resource_review";
}
@RequestMapping("/add")
public String add(Model model)
{
System.out.println("Invoking add() method");
return "resource_add";
}
@RequestMapping("/save")
public String save(@ModelAttribute Resource resource,Model model) throws ClassNotFoundException
{
System.out.println("Invoking save() method");
System.out.println(resource);
return "redirect:/resource/add";
}
@ModelAttribute("resource")
public Resource getResource()
{
System.out.println("Adding a new Resource to the model");
return new Resource();
}
@ModelAttribute("checkOptions")
public List<String> getChecks()
{
System.out.println("Adding CheckOptions");
return new LinkedList<>(Arrays.asList(new String[]{"Lead Time","Special Rate","Requires Approval"}));
}
@ModelAttribute("radioOptions")
public List<String> getRadios()
{
return new LinkedList<>(Arrays.asList(new String[]{"Hours","Piece","Tons"}));
}
@ModelAttribute("itemOptions")
public List<String> getItems()
{
return new LinkedList<>(Arrays.asList(new String[]{"Lead Time","Special Rate","Requires Approval"}));
}
}
resource_review.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Project Manager</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<link rel="stylesheet"
href="<spring:url value="/resources/css/home.css"/>" type="text/css" />
<link rel="stylesheet"
href="<spring:url value="/resources/css/bootstrap-select.min.css"/>"
type="text/css" />
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script
src="<spring:url value="/resources/js/bootstrap-select.min.js"/>"></script>
</head>
<body>
<jsp:include page="../views/fragments/header.jsp"></jsp:include>
<div class="container">
<div class="row">
<h2>Please review the resource for accuracy</h2>
<div class="form-group">
<label for="project-name">Name</label> <span>${resource.name}</span>
</div>
<div class="form-group">
<label for="project_type">Type</label> <span>${resource.type }</span>
</div>
<div class="form-group">
<label>Cost</label> <span>${resource.cost}</span>
</div>
<div class="form-group">
<label>Unit Of Measure</label> <span>${resource.unitOfMeasure}</span>
</div>
<div class="form-group">
<label>Indicators</label>
<c:forEach var="indicator" items="${resource.indicators}">
<span>${indicator}</span>
</c:forEach>
</div>
<a href="<spring:url value="/resource/add"/>" class="btn btn-default">Edit</a>
<a href="<spring:url value="/resource/save"/>"
class="btn btn-default">Save</a>
</div>
</div>
</body>
</html>
resource_add.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Project Manager</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<link rel="stylesheet" href="<spring:url value="/resources/css/bootstrap-select.min.css"/>" type="text/css" />
<link rel="stylesheet" href="<spring:url value="/resources/css/global.css"/>" type="text/css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script src="<spring:url value="/resources/js/bootstrap-select.min.js"/>"></script>
</head>
<body>
<jsp:include page="../views/fragments/header.jsp"></jsp:include>
<div class="container">
<div class="row">
<h1>Resource</h1>
</div>
<spring:url value="/resource/review" var="formUrl" htmlEscape="true"/>
<form:form action="${formUrl}" method="POST" modelAttribute="resource">
<div class="row">
<div class="form-group">
<label for="resource-name">Name</label>
<form:input path="name" cssClass="form-control" id="resource-name" />
</div>
<div class="form-group">
<label for="resource-type">Type</label>
<form:select path="type" items="${itemOptions}" cssClass="form-control"></form:select>
</div>
<div class="form-group">
<label for="cost">Cost</label>
<form:input path="cost" cssClass="form-control" id="cost" />
</div>
<div class="form-group">
<label for="unit">Unit of Measure</label>
<form:radiobuttons path="unitOfMeasure" items="${radioOptions}"/>
</div>
<div class="form-group">
<label for="indicators">Indicators</label>
<form:checkboxes id="indicators" path="indicators" items="${checkOptions}"/>
</div>
<div class="form-group">
<label for="notes">Notes</label>
<form:textarea id="notes" path="notes" cssClass="form-control" rows="3"/>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</div>
</form:form>
</div>
</body>
</html>
Session Status
- The session status interface can be injected in our controller Handler Methods.
- Once injected we can invoke different methods of this interface to remove session attributes so that they are unavailable for request Processing.
- Just Pass an argument of type SessionStatus in controller handler method to use this interface
- An example scenario of using this interface is when data is persisted in database we may need to clear the session.
@RequestMapping("/save")
public String save(@ModelAttribute Resource resource,SessionStatus status) throws ClassNotFoundException
{
System.out.println("Invoking save() method");
System.out.println(resource);
status.setComplete();
return "redirect:/resource/add";
}
Response Body
- Allows us to write a response from our controller handler methods directly to the client.
- Anything returned from controller will be directly written to the response body.
- It won't be placed in model and we will not use jsp to render our view.
- Used to handle data for java-script requests.
- The value returned from handler method is used as HTTP response directly.
- We can return json, xml response directly using this.
- Also used in scenario where we need to change a snippet of information on the same page.
- For Example to check weather a user name exists on signup page and show message and proceed accordingly.
- For a given below method handler if an ajax request is made or is directly called we will get response in ajax request as returned by method itself.
Changes to ResourceController
@RequestMapping("/request")
@ResponseBody
public String request(@ModelAttribute Resource resource)
{
//Send an Email for Request
//Format this into an json array to be handled in js
return "The request has been sent for Approval";
}
Request Body Annotation
- Allows us to take the http request body and map it to controller method parameters on one of our Controller Handler methods.
- We do this by annotating the method parameter with @RequestBody
- Spring uses Http Message converters to make this possible
- Helps to bind json/xml response to an object and parse it out.
- Spring provides an HTTP message converter that can convert json to an object.
- So if we have Resource as type of our @RequestBody argument and we have a json String sent in it will automatically map it to the argument.
- Spring can find a message converter for type resource or for json and then it is able to databind or desearilize json String to the Resource type automatically.
- Since in the below method we are sending String(serialized form of Form) from ajax call we are binding it to argument of type String.
@RequestMapping(value="/request",method=RequestMethod.POST)
@ResponseBody
public String request(@RequestBody String resource)
{
//Send an Email for Request
//Format this into an json array to be handled in js
System.out.println(resource);
return "The request has been sent for Approval";
}
Redirect Request
- Reposting of a form can be easily done if user is not redirected.
- In Spring we have a built in functionality to redirect which allows us to stop a form from being reposted.
- We return a string url with "redirect:" as prefix
- For example after add here we are redirecting to "/project/find" by returning a string "redirect:/project/find"
@RequestMapping(value="/add",method=RequestMethod.POST)
public String saveProject(@Valid @ModelAttribute Project project,Errors errors,HttpServletRequest request,@RequestParam("name") String name,HttpSession session,Model model)
{
if(errors.hasErrors())
{
System.out.println("The Project is not Validated");
return "project_add";
}
else
{
System.out.println("The Project is Validated");
}
System.out.println(session.getAttribute("token"));
System.out.println(request.getParameter("name"));
System.out.println(name);
System.out.println(project);
model.addAttribute("project",project);
System.out.println("Invoking saveProject");
return "redirect:/project/find";
}
RedirectAttributes Interface
- It is always useful to send some data to the next url to which we are redirecting too.
- for example after saving an entity we may need to display that entity using that id in such a case we will send id of new entity to the redirected method that displays that entity.
- To accomplish this Spring provides us with RedirectAttribute interface.
- This can be added as an object to our controller handler methods as an argument.
- Next we can add attributes to this object key value pairs in String which are then appended to the request and act as basically a request parameter.
- We then need to handle the parameters in the method to which it is redirected too.
RedirectAttribute sent via Controller method
@RequestMapping(value="/add",method=RequestMethod.POST)
public String saveProject(@Valid @ModelAttribute Project project,Errors errors,HttpServletRequest request,@RequestParam("name") String name,HttpSession session,Model model,RedirectAttributes attributes)
{
if(errors.hasErrors())
{
System.out.println("The Project is not Validated");
return "project_add";
}
else
{
System.out.println("The Project is Validated");
}
System.out.println(session.getAttribute("token"));
System.out.println(request.getParameter("name"));
System.out.println(name);
System.out.println(project);
project.setProjectId(55L);
this.projectService.save(project);
attributes.addAttribute("projectId", project.getProjectId().toString());
model.addAttribute("project",project);
System.out.println("Invoking saveProject");
return "redirect:/";
}
Request Parameters handled in redirected method
@RequestMapping(value="/", params="projectId")
public String goProjectHome(Model model,@RequestParam("projectId") Long projectId)
{
model.addAttribute("currentProject",this.service.find(projectId));
return "home";
}
Flash Attributes
- Used to handle the Post-Redirect-Get pattern
- In this attributes are not sent as request parameters but instead stored in a session.
- On the redirected method we fetch this using @ModelAttribute.
- We can pass entire objects to the redirected method.
- We add the attributes using the addFlashAttribute method which is defined in RedirectAttributes interface.
- Once the attribute is used it is automatically deleted from session that is why we call it as a Flash attribute.
@RequestMapping(value="/add",method=RequestMethod.POST,params={"type=Multi Year"})
public String saveMulti(@Valid @ModelAttribute Project project,Errors errors,@RequestParam("name") String name,HttpSession session,HttpServletRequest request,RedirectAttributes attributes)
{
System.out.println("Invoking saveMulti");
if(errors.hasErrors())
{
System.out.println("The Project is not Validated");
return "project_add";
}
else
{
System.out.println("The Project is Validated");
}
System.out.println(session.getAttribute("token"));
System.out.println(request.getParameter("name"));
System.out.println(name);
System.out.println(project);
project.setProjectId(66L);
this.projectService.save(project);
attributes.addFlashAttribute("project",project);
return "redirect:/showproject";
}
In the Redirected Method @RequestMapping(value="/showproject")
public String goHome(Model model,@ModelAttribute("project") Project project)
{
model.addAttribute("currentProject",project);
return "project";
}
REST Controller
- A REST controller returns the Response Body directly.
- If its a String it returns a String if an object is returned it is converted into JSON/XML format as specified in content type.
- We annotate the controller with @RestController annotation.
- This controller will return REST response from all its methods.
No comments:
Post a Comment