Lets use a command bean for Todo
Add Validations
The JSR 303 and JSR 349 defines specification for the Bean Validation API (version 1.0 and 1.1, respectively), and Hibernate Validator is the reference implementation.
org.hibernate:hibernate-validator
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<form:form method="post" commandName="todo">
<fieldset class="form-group">
<form:label path="desc">Description</form:label>
<form:input path="desc" type="text" class="form-control" required="required"/>
</fieldset>
</form:form>
@Size(min = 10, message = "Enter atleast 10 Characters.")
@Valid Todo todo, BindingResult result
if (result.hasErrors())
return "todo";
<form:errors path="desc" cssClass="text-warning" />
<?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.in28minutes.springboot.web</groupId >
<artifactId >spring-boot-first-web-application</artifactId >
<version >0.0.1-SNAPSHOT</version >
<packaging >jar</packaging >
<name >spring-boot-first-web-application</name >
<description >Demo project for Spring Boot</description >
<parent >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-parent</artifactId >
<version >1.4.3.RELEASE</version >
<relativePath /> <!-- lookup parent from repository -->
</parent >
<properties >
<project .build.sourceEncoding>UTF-8</project .build.sourceEncoding>
<project .reporting.outputEncoding>UTF-8</project .reporting.outputEncoding>
<java .version>1.8</java .version>
</properties >
<dependencies >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-web</artifactId >
</dependency >
<dependency >
<groupId >javax.servlet</groupId >
<artifactId >jstl</artifactId >
</dependency >
<dependency >
<groupId >org.webjars</groupId >
<artifactId >bootstrap</artifactId >
<version >3.3.6</version >
</dependency >
<dependency >
<groupId >org.webjars</groupId >
<artifactId >jquery</artifactId >
<version >1.9.1</version >
</dependency >
<dependency >
<groupId >org.apache.tomcat.embed</groupId >
<artifactId >tomcat-embed-jasper</artifactId >
<scope >provided</scope >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-devtools</artifactId >
<scope >runtime</scope >
</dependency >
<dependency >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-starter-test</artifactId >
<scope >test</scope >
</dependency >
</dependencies >
<build >
<plugins >
<plugin >
<groupId >org.springframework.boot</groupId >
<artifactId >spring-boot-maven-plugin</artifactId >
</plugin >
</plugins >
</build >
</project >
src/main/java/com/in28minutes/springboot/web/controller/LoginController.java
package com .in28minutes .springboot .web .controller ;
import org .springframework .beans .factory .annotation .Autowired ;
import org .springframework .stereotype .Controller ;
import org .springframework .ui .ModelMap ;
import org .springframework .web .bind .annotation .RequestMapping ;
import org .springframework .web .bind .annotation .RequestMethod ;
import org .springframework .web .bind .annotation .RequestParam ;
import org .springframework .web .bind .annotation .SessionAttributes ;
import com .in28minutes .springboot .web .service .LoginService ;
@ Controller
@ SessionAttributes ("name" )
public class LoginController {
@ Autowired
LoginService service ;
@ RequestMapping (value ="/login" , method = RequestMethod .GET )
public String showLoginPage (ModelMap model ){
return "login" ;
}
@ RequestMapping (value ="/login" , method = RequestMethod .POST )
public String showWelcomePage (ModelMap model , @ RequestParam String name , @ RequestParam String password ){
boolean isValidUser = service .validateUser (name , password );
if (!isValidUser ) {
model .put ("errorMessage" , "Invalid Credentials" );
return "login" ;
}
model .put ("name" , name );
model .put ("password" , password );
return "welcome" ;
}
}
src/main/java/com/in28minutes/springboot/web/controller/TodoController.java
package com .in28minutes .springboot .web .controller ;
import java .util .Date ;
import javax .validation .Valid ;
import org .springframework .beans .factory .annotation .Autowired ;
import org .springframework .stereotype .Controller ;
import org .springframework .ui .ModelMap ;
import org .springframework .validation .BindingResult ;
import org .springframework .web .bind .annotation .RequestMapping ;
import org .springframework .web .bind .annotation .RequestMethod ;
import org .springframework .web .bind .annotation .RequestParam ;
import org .springframework .web .bind .annotation .SessionAttributes ;
import com .in28minutes .springboot .web .model .Todo ;
import com .in28minutes .springboot .web .service .LoginService ;
import com .in28minutes .springboot .web .service .TodoService ;
@ Controller
@ SessionAttributes ("name" )
public class TodoController {
@ Autowired
TodoService service ;
@ RequestMapping (value = "/list-todos" , method = RequestMethod .GET )
public String showTodos (ModelMap model ) {
String name = (String ) model .get ("name" );
model .put ("todos" , service .retrieveTodos (name ));
return "list-todos" ;
}
@ RequestMapping (value = "/add-todo" , method = RequestMethod .GET )
public String showAddTodoPage (ModelMap model ) {
model .addAttribute ("todo" , new Todo (0 , (String ) model .get ("name" ), "Default Desc" ,
new Date (), false ));
return "todo" ;
}
@ RequestMapping (value = "/delete-todo" , method = RequestMethod .GET )
public String deleteTodo (@ RequestParam int id ) {
service .deleteTodo (id );
return "redirect:/list-todos" ;
}
@ RequestMapping (value = "/add-todo" , method = RequestMethod .POST )
public String addTodo (ModelMap model , @ Valid Todo todo , BindingResult result ) {
if (result .hasErrors ()){
return "todo" ;
}
service .addTodo ((String ) model .get ("name" ), todo .getDesc (), new Date (),
false );
return "redirect:/list-todos" ;
}
}
src/main/java/com/in28minutes/springboot/web/model/Todo.java
package com .in28minutes .springboot .web .model ;
import java .util .Date ;
import javax .validation .constraints .Size ;
public class Todo {
private int id ;
private String user ;
@ Size (min =10 , message ="Enter at least 10 Characters..." )
private String desc ;
private Date targetDate ;
private boolean isDone ;
public Todo () {
super ();
}
public Todo (int id , String user , String desc , Date targetDate ,
boolean isDone ) {
super ();
this .id = id ;
this .user = user ;
this .desc = desc ;
this .targetDate = targetDate ;
this .isDone = isDone ;
}
public int getId () {
return id ;
}
public void setId (int id ) {
this .id = id ;
}
public String getUser () {
return user ;
}
public void setUser (String user ) {
this .user = user ;
}
public String getDesc () {
return desc ;
}
public void setDesc (String desc ) {
this .desc = desc ;
}
public Date getTargetDate () {
return targetDate ;
}
public void setTargetDate (Date targetDate ) {
this .targetDate = targetDate ;
}
public boolean isDone () {
return isDone ;
}
public void setDone (boolean isDone ) {
this .isDone = isDone ;
}
@ Override
public int hashCode () {
final int prime = 31 ;
int result = 1 ;
result = prime * result + id ;
return result ;
}
@ Override
public boolean equals (Object obj ) {
if (this == obj ) {
return true ;
}
if (obj == null ) {
return false ;
}
if (getClass () != obj .getClass ()) {
return false ;
}
Todo other = (Todo ) obj ;
if (id != other .id ) {
return false ;
}
return true ;
}
@ Override
public String toString () {
return String .format (
"Todo [id=%s, user=%s, desc=%s, targetDate=%s, isDone=%s]" , id ,
user , desc , targetDate , isDone );
}
}
src/main/java/com/in28minutes/springboot/web/service/LoginService.java
package com .in28minutes .springboot .web .service ;
import org .springframework .stereotype .Component ;
import org .springframework .stereotype .Service ;
@ Service
public class LoginService {
public boolean validateUser (String userid , String password ) {
// in28minutes, dummy
return userid .equalsIgnoreCase ("in28minutes" )
&& password .equalsIgnoreCase ("dummy" );
}
}
src/main/java/com/in28minutes/springboot/web/service/TodoService.java
package com .in28minutes .springboot .web .service ;
import java .util .ArrayList ;
import java .util .Date ;
import java .util .Iterator ;
import java .util .List ;
import org .springframework .stereotype .Service ;
import com .in28minutes .springboot .web .model .Todo ;
@ Service
public class TodoService {
private static List <Todo > todos = new ArrayList <Todo >();
private static int todoCount = 3 ;
static {
todos .add (new Todo (1 , "in28Minutes" , "Learn Spring MVC" , new Date (),
false ));
todos .add (new Todo (2 , "in28Minutes" , "Learn Struts" , new Date (), false ));
todos .add (new Todo (3 , "in28Minutes" , "Learn Hibernate" , new Date (),
false ));
}
public List <Todo > retrieveTodos (String user ) {
List <Todo > filteredTodos = new ArrayList <Todo >();
for (Todo todo : todos ) {
if (todo .getUser ().equals (user )) {
filteredTodos .add (todo );
}
}
return filteredTodos ;
}
public void addTodo (String name , String desc , Date targetDate ,
boolean isDone ) {
todos .add (new Todo (++todoCount , name , desc , targetDate , isDone ));
}
public void deleteTodo (int id ) {
Iterator <Todo > iterator = todos .iterator ();
while (iterator .hasNext ()) {
Todo todo = iterator .next ();
if (todo .getId () == id ) {
iterator .remove ();
}
}
}
}
src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java
package com .in28minutes .springboot .web ;
import org .springframework .boot .SpringApplication ;
import org .springframework .boot .autoconfigure .SpringBootApplication ;
import org .springframework .context .annotation .ComponentScan ;
@ SpringBootApplication
@ ComponentScan ("com.in28minutes.springboot.web" )
public class SpringBootFirstWebApplication {
public static void main (String [] args ) {
SpringApplication .run (SpringBootFirstWebApplication .class , args );
}
}
src/main/resources/application.properties
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
logging.level.org.springframework.web=INFO
src/main/webapp/WEB-INF/jsp/list-todos.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>Todo's for ${name}</title>
<link href="webjars/bootstrap/3.3.6/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body>
<div class="container">
<table class="table table-striped">
<caption>Your todos are</caption>
<thead>
<tr>
<th>Description</th>
<th>Target Date</th>
<th>Is it Done?</th>
<th></th>
</tr>
</thead>
<tbody>
<c:forEach items="${todos}" var="todo">
<tr>
<td>${todo.desc}</td>
<td>${todo.targetDate}</td>
<td>${todo.done}</td>
<td><a type="button" class="btn btn-warning"
href="/delete-todo?id=${todo.id}">Delete</a></td>
</tr>
</c:forEach>
</tbody>
</table>
<div>
<a class="button" href="/add-todo">Add a Todo</a>
</div>
</div>
<script src="webjars/jquery/1.9.1/jquery.min.js"></script>
<script src="webjars/bootstrap/3.3.6/js/bootstrap.min.js"></script>
</body>
</html>
src/main/webapp/WEB-INF/jsp/login.jsp
<html>
<head>
<title>First Web Application</title>
</head>
<body>
<font color="red">${errorMessage}</font>
<form method="post">
Name : <input type="text" name="name" />
Password : <input type="password" name="password" />
<input type="submit" />
</form>
</body>
</html>
src/main/webapp/WEB-INF/jsp/todo.jsp
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<html>
<head>
<title>First Web Application</title>
<link href="webjars/bootstrap/3.3.6/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body>
<div class="container">
<form:form method="post" modelAttribute="todo">
<fieldset class="form-group">
<form:label path="desc">Description</form:label>
<form:input path="desc" type="text"
class="form-control" required="required"/>
<form:errors path="desc" cssClass="text-warning"/>
</fieldset>
<button type="submit" class="btn btn-success">Add</button>
</form:form>
</div>
<script src="webjars/jquery/1.9.1/jquery.min.js"></script>
<script src="webjars/bootstrap/3.3.6/js/bootstrap.min.js"></script>
</body>
</html>
src/main/webapp/WEB-INF/jsp/welcome.jsp
<html>
<head>
<title>First Web Application</title>
</head>
<body>
Welcome ${name}!! <a href="/list-todos">Click here</a> to manage your todo's.
</body>
</html>
src/test/java/com/in28minutes/springboot/web/SpringBootFirstWebApplicationTests.java
package com .in28minutes .springboot .web ;
import org .junit .Test ;
import org .junit .runner .RunWith ;
import org .springframework .boot .test .context .SpringBootTest ;
import org .springframework .test .context .junit4 .SpringRunner ;
@ RunWith (SpringRunner .class )
@ SpringBootTest
public class SpringBootFirstWebApplicationTests {
@ Test
public void contextLoads () {
}
}
Implementing Server Side Validation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Command Bean or Form Backing Bean
Add Validation
Use Validation on Controller
Display Errors in View
Command Bean
~~~~~~~~~~~~
Controller
View - Spring Form Tags
LoginController -> adds name to model
welcome.jsp -> shows ${name}
TodoController -> redirects to list-todos.jsp
${name} is empty
Component, Service, Repository, Controller
Autowired
ComponentScan
Field dummyService in com.in28minutes.springboot.web.controller.LoginController
required a bean of type 'com.in28minutes.dummy.DummyService'
that could not be found.
Spring Boot Starter Parent
Spring Boot Starter Web
@SpringBootApplication
Auto Configuration
Dispatcher Servlet
/login => "login"
"login" => src/main/webapp/WEB-INF/jsp/login.jsp
Search for a view named "login"
/login => LoginController