###### tags: `Java` `MVC` `SpringBoot`
# Spring Boot + MVC Startup
After creating a New Spring Project in Spring Boot (with version 2.7.10)
- [ ] 1. Add following dependencies to pom.xml
``` xml!
<!-- ADDED DEPENDENCIES -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>0.46</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- BOOTSTRAP DEPENDENCIES -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>5.2.3</version>
</dependency>
```
If using a database, be sure to also add
```xml!
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
```
If doing login and registration, be sure to also add
```xml!
<!-- BCRYPT -->
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<version>0.4</version>
</dependency>
```
:::info
If the pom.xml file is having an issue, try changing 2.7.10 to 2.7.9 or 2.7.8
:::
- [ ] 2. Add a new folder to **src/main/webapp** called **WEB-INF**

:::info
We'll be coming back to this folder later.
:::
- [ ] 3. In **src/main/resources**, add the following line to **application.properties**
```properties!
spring.mvc.view.prefix=/WEB-INF/
```
**If using a database**, also add the following lines
```properties!
spring.datasource.url=jdbc:mysql://localhost:3306/<YOUR_SCHEMA>
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update
spring.mvc.hiddenmethod.filter.enabled=true
```
:::danger
If using a database, be sure to create the database in MySQL workbench before adding any additional files. Be sure to have the same name for the database as <YOUR_SCHEMA> was replaced by.
:::
:::info
The hidden method filter enabler is so that you can use a put method in editing contents from the database
:::
- [ ] 4. *Right-click* on **src/main/java/com.*match.folder.name.here*** and create a *new > package*. Name this package com..controllers

Repeat this process for the following packages.
* .models
* .repositories
* .services
- [ ] 5. In the controllers folder, create a *new > class* for the Controller
:::danger
If using the code snippet, be sure not to get rid of any auto-generated packages
:::
```java!
@Controller
public class HomeController {
//@Autowired
//private <SERVICE_NAME> <service_name>;
@GetMapping("/") // reserve route
public String index() {
return "index.jsp"; // map route to jsp
}
}
```
This will be where the route paths are reserved and mapped to the appropriate .jsp file.
:::warning
If getting red lined errors, be sure to try Ctrl+Shift+o (windows) or ⌘+Shift+o (mac)
:::
- [ ] 6. In the WEB-INF folder created earlier, *right-click* on the folder, select **new > other...**, and search for *jsp*.

Create the .jsp file for the route path. This will be where your html and jsp code for the page go.
:::warning
If you copied the HomeController, be sure to name this file *index.jsp*
:::
- [ ] 7. Copy the following code into your .jsp file created.
```jsx!
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.min.css">
<!-- CSS only -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
<!-- My CSS -->
<link rel='stylesheet' href='/css/styles.css'>
<!-- JS for Bootstrap / jQuery -->
<script src='/webjars/jquery/jquery.min.js'> </script>
<script src='/webjars/bootstrap/js/boostrap.min.js'> </script>
<!-- JavaScript Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"> </script>
<!-- My JS -->
<script type = "text/javascript" src='/js/scripts.js'></script>
<meta charset="UTF-8">
<!-- Title -->
<title>Project Title</title>
</head>
<body>
<!-- HEADER -->
<header>
<h1>Testing</h1>
<nav>
</nav>
</header>
<!-- MAIN -->
<main>
</main>
<!-- FOOTER -->
<footer>
</footer>
</body>
</html>
```
- [ ] 8. The application is now good to launch with Spring Boot.
# Java MVC
## Trouble Shooting
- [Java Debugging Notes](https://hackmd.io/4dzTOgrRRnCOFWmZbyFfyQ?view)
## Comments
### Java Classes
- Attributes Label
```
// ==========================
// ATTRIBUTES
// ==========================
```
- Constructor Label
```
// ==========================
// CONSTRUCTOR
// ==========================
```
- Getter and Setter Label
```
// ==========================
// GETTERS / SETTERS
// ==========================
```
- Method Label
```
// ==========================
// METHODS
// ==========================
```
- Interface Label
```
// ==========================
// INTERFACE
// ==========================
```
- Relationship Label
```
// ==========================
// RELATIONSHIPS
// ==========================
```
## Quick Code
### Debugging
- Print out the Variables
```java!
System.out.println("=".repeat(20));
System.out.printf("\n\tVARIABLES:\t \n\n");
System.out.println("=".repeat(20));
```
- Print line number of cleared code
```java!
System.out.println("=".repeat(20));
System.out.println("\n\tCLEARED [Line: <LINE_NUMBER>]\t \n\n");
System.out.println("=".repeat(20));
```
### Models
The model is like the blueprint: The blueprint provides the detailed plan for how the house should be built. In Java Spring, the model represents the data structure and defines how data is stored in the database.
- Basic setup
:::info
To create a new file, add a *New > Class* to the .models package
:::
```java!
@Entity
@Table(name="<model_plural>")
public class <MODEL_NAME> {
// ==========================
// ATTRIBUTES
// ==========================
// create unique id
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// This will not allow the createdAt column to be updated after creation
@Column(updatable=false)
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date createdAt;
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date updatedAt;
// ==========================
// CONSTRUCTOR
// ==========================
public <MODEL_NAME>(){}
// ==========================
// GETTERS / SETTERS
// ==========================
@PrePersist
protected void onCreate(){ this.createdAt = new Date();}
@PreUpdate
protected void onUpdate(){ this.updatedAt = new Date();}
// add getters/setters for ALL attributes
// **HERE**
}
```
:::warning
When adding the imports (Ctrl+Shift+o / ⌘+Shift+o) for everything BUT Date, make sure to select the javax.persistence option. For Date, select the java.util option.
:::
- Relationships
:::danger
Be sure to add the Getter and Setter for the new relationship variable added.
:::
- One-to-One
```java!
// One-to-One Dominate Side
// <THIS_MODEL> ---- <ATTACHED_MODEL>
@OneToOne(mappedBy="<this_model>", cascade=CascadeType.ALL, fetch=FetchType.LAZY)
private <ATTACHED_MODEL> <ATTACHED_MODEL>;
```
```java!
// One-to-One Attached Side
// <ATTACHED_MODEL> ---- <THIS_MODEL>
@OneToOne(fetch=FetchType.LAZY)
@JoinColumn(name="<attached_model>_id")
private <ATTACHED_MODEL> <attached_model>;
```
- One-to-Many
```java!
// One-to-Many
// <THIS_MODEL> ---< <ATTACHED_MODEL>
@OneToMany(mappedBy="<this_model>", cascade=CascadeType.ALL, fetch = FetchType.LAZY)
private List<<ATTACHED_MODEL>> <attched_plural>;
```
```java!
// Many-to-One
// <THIS_MODEL> >--- <ATTACHED_MODEL>
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="<attached_model>_id")
private <ATTACHED_MODEL> <attached_model>;
```
- Many-to-Many
- In both models
```java!
// Many-to-Many
// <THIS_MODEL> >-- <TABLE_MODEL> --< <ATTACHED_MODEL>
@OneToMany(mappedBy="<this_model>", fetch=FetchType.LAZY)
private List<<TABLE_MODEL>> <attached_plural>;
```
- In the table model
```java!
// Many-to-Many
// <ATTACHED_MODEL_1> >-- <THIS_TABLE_MODEL> --< <ATTACHED_MODEL_2>
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="<attached_model_1>_id")
private <ATTACHED_MODEL_1> <attached_model_1>;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="<attached_model_2>_id")
private <ATTACHED_MODEL_2> <attached_model_2>;
```
### Repository
The repository is like the building materials: The building materials are the resources needed to construct the house, such as wood, concrete, and nails. In Java Spring, the repository is responsible for accessing and storing data in the database.
- Basic Setup
:::success
To create a new file, add a *New > Interface* to the .repositories package
:::
```java!
@Repository
public interface <MODEL_NAME>Repo extends CrudRepository<<MODEL_NAME>, Long> {
// Model gets imported here
List<<MODEL_NAME>> findAll();
// No need to add .save here because CrudRepository already has it
// Repo gets "exported" to model Service
}
```
### Services
The service is like the project manager: The project manager oversees the construction process, ensures that everything is on track, and communicates with different teams. In Java Spring, the service acts as an intermediary between the controller and the repository. It handles business logic and performs operations on the data.
- Basic Setup with CRUD
:::info
To create a new file, add a *New > Class* to the .services package
:::
```java!
@Service
public class <MODEL_NAME>Serv {
@Autowired
private <REPO_NAME> <repo_name>;
// ==========================
// CRUD METHODS
// ==========================
// create
public <MODEL_NAME> createOne(<MODEL_NAME> i) {
return <repo_name>.save(i);
}
// read all
public List<<MODEL_NAME>> getAll() {
return <repo_name>.findAll();
}
// read one
public <MODEL_NAME> getOne(Long id) {
return <repo_name>.findById(id).orElse(null);
}
// update
public <MODEL_NAME> updateOne(<MODEL_NAME> i) {
return <repo_name>.save(i);
}
// delete
public void deleteOne(Long id) {
<repo_name>.deleteById(id);
}
}
```
### Controllers
The controller is like the homeowner: The homeowner has the final say over how the house should look and function. In Java Spring, the controller receives requests from the user and communicates with the service to perform operations on the data. It then returns a response to the user.
- Basic setup
:::info
To create a new file, add a *New > Class* to the .controllers package
:::
```java!
@Controller
public class <MODEL_NAME>Controller {
@Autowired
private <SERVICE_NAME> <service_name>;
@GetMapping("/")
public String <PAGE_NAME>() {
return "<PAGE_NAME>.jsp";
}
}
```
- Get method with a form
```java!
@GetMapping("/<ROUTE>")
public String <PAGE_NAME>(Model model) {
// get attribute from server
// add attribute to model
return "<PAGE_NAME>.jsp";
}
```
- To check if user is logged in
```java!
if(session.getAttribute("userID") == null) {
return "redirect:/logReg";
}
```
- Post method for a form
```java!
@PostMapping("/<ROUTE>")
public String create<MODEL_NAME>(@Valid @ModelAttribute("modelForm") <MODEL_NAME> <model_name>, BindingResult result, Model model) {
if(result.hasErrors()) {
// get attribute from server
// add attribute to model
return "<PAGE_NAME>.jsp";
} else {
<service_name>.createOne(<model_name>);
return "redirect:/";
}
}
```
:::warning
For **edits**, change the *PostMapping* to **PutMapping**
:::
### JSP
#### Basic Setup
- Main beginning template
```jsx!
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.min.css">
<!-- CSS only -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
<!-- My CSS -->
<link rel='stylesheet' href='/css/styles.css'>
<!-- JS for Bootstrap / jQuery -->
<script src='/webjars/jquery/jquery.min.js'> </script>
<script src='/webjars/bootstrap/js/boostrap.min.js'> </script>
<!-- JavaScript Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"> </script>
<!-- My JS -->
<script type = "text/javascript" src='/js/scripts.js'></script>
<meta charset="UTF-8">
<!-- Title -->
<title>Project Title</title>
</head>
<body>
<!-- HEADER -->
<header>
<nav>
</nav>
</header>
<!-- MAIN -->
<main>
</main>
<!-- FOOTER -->
<footer>
</footer>
</body>
</html>
```
#### Java Values
- Print
```jsx!
<c:out value="${}"/>
```
- To forEach loop
```jsx!
<c:forEach items="${}" var="i">
</c:forEach>
```
- In **items**: name of list you want to loop through.
- EX: items="${allItems}"
- In **var**: the iteratior value to access individual model attributes.
- EX: var="i"
- `<p> ${i.price} </p> // in the forEach loop`
:::info
Notice how there is no c:out needed for printing the variable, as it still counts as being inside a jsp function since the forEach loop has not ended yet.
:::
- Conditionals
```jsx!
<c:if test="${ }">
</c:if>
```
:::info
There are no else if or else statements in jsp.
(Use another c:if with the inverse of the first test for the second one)
:::
#### Forms
- For a more in depth look into forms:
- [Spring MVC From Tutorial - Baeldung](https://www.baeldung.com/spring-mvc-form)
- At the top of the jsp page, add the following
```!
<!-- for forms -->
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix = "fmt" uri = "http://java.sun.com/jsp/jstl/fmt" %>
<!-- for validation -->
<%@ page isErrorPage="true" %>
```
- Basic form set up with validations
```jsx!
<form:form action="/" method="post" modelAttribute="modelForm" class="d-flex">
<button>Submit</button>
</form:form>
```
:::warning
If the form is for an **Edit** PostMapping, add the following in the form
:::
```jsx!
<input type="hidden" name="_method" value="put">
```
- Show Error
```jsx!
<!-- Validation Error -->
<form:errors path="<attribute_name>" class="text-warning"/>
```
- String Variable
```jsx!
<!-- Attribute Information -->
<div class="d-flex">
<label for="<attribute_name>">LABEL</label>
<input type="text" name="<attribute_name>">
</div>
```
- Integer Variable
```jsx!
<!-- Attribute Information -->
<div class="d-flex">
<label for="<attribute_name>">LABEL</label>
<input type="number" step="1" name="<attribute_name>">
</div>
```
#### Table (w/ bootstrap)
- Basic Setup
```jsx!
<table class="table">
<thead>
<!--- Column Labels --->
<tr>
<th scope="col">1</th>
<th scope="col">2</th>
<th scope="col">3</th>
<th scope="col">4</th>
</tr>
</thead>
<tbody>
<!--- Row Data --->
<tr>
<th scope="row">A</th>
<td>B</td>
<td>C</td>
<td>D</td>
</tr>
</tbody>
</table>
```
#### Additional Inputs
- For inputting own style sheet named style.css
```jsx!
<link rel="stylesheet" type="text/css" href="/css/style.css">
```
- For inputting a js file
```jsx!
<script type="text/javascript" src="/js/app.js"></script>
```
### Login and Registration
#### Models
- User
```java!
@Entity
@Table(name="users")
public class User {
// ==========================
// ATTRIBUTES
// ==========================
// create unique id
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message="Name is required!")
@Size(min=3, max=30, message="First Name must be between 3 and 30 characters")
private String userName;
@NotEmpty(message="Email is required!")
@Email(message="Please enter a valid email!")
private String email;
@NotEmpty(message="Password is required!")
@Size(min=8, max=128, message="Password must be between 8 and 128 characters")
private String password;
@Transient // don't go to the db
@NotEmpty(message="Confirm Password is required!")
@Size(min=8, max=128, message="Confirm Password must be between 8 and 128 characters")
private String confirm;
// This will not allow the createdAt column to be updated after creation
@Column(updatable=false)
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date createdAt;
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date updatedAt;
// ==========================
// RELATIONSHIPS
// ==========================
// ==========================
// CONSTRUCTOR
// ==========================
public User(){}
// ==========================
// GETTERS / SETTERS
// ==========================
@PrePersist
protected void onCreate(){ this.createdAt = new Date();}
public Date getCreatedAt() { return createdAt; }
public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; }
@PreUpdate
protected void onUpdate(){ this.updatedAt = new Date();}
public Date getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(Date updatedAt) { this.updatedAt = updatedAt; }
// add getters/setters for ALL attributes
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUserName() { return userName; }
public void setUserName(String userName) { this.userName = userName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getConfirm() { return confirm; }
public void setConfirm(String confirm) { this.confirm = confirm; }
}
```
- LoginUser
```java!
public class LoginUser {
@NotEmpty(message="Email is required!")
@Email(message="Please enter a valid email!")
private String email;
@NotEmpty(message="Password is required!")
@Size(min=8, max=128, message="Password must be between 8 and 128 characters")
private String password;
public LoginUser() {}
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
```
#### Repository
- User
```java!
@Repository
public interface UserRepo extends CrudRepository<User, Long> {
// Model gets imported here
List<User> findAll(); // probably not going to be used unless admin
Optional<User> findByEmail(String email);
// No need to add .save here because CrudRepository already has it
// Repo gets "exported" to model Service
}
```
#### Service
- User
```java!
@Service
public class UserServ {
@Autowired
private UserRepo userRepo;
// ==========================
// REGISTRATION
// ==========================
public User register(User newUser, BindingResult result) {
// if email already in use
if(userRepo.findByEmail(newUser.getEmail()).isPresent()) {
result.rejectValue("email", "Unique", "This email is already in use!");
}
// if the passwords do not match
if(!newUser.getPassword().equals(newUser.getConfirm())) {
result.rejectValue("confirm", "Matches", "The Confirm Password must match Password!");
}
// where there errors in the last checks or form?
if(result.hasErrors()) {
return null;
} else {
// encrypting the password
String hashed = BCrypt.hashpw(newUser.getPassword(), BCrypt.gensalt());
newUser.setPassword(hashed);
return userRepo.save(newUser);
}
}
// ==========================
// LOGIN
// ==========================
public User login(LoginUser newLogin, BindingResult result) {
// is the email valid?
Optional<User> potentialUser = userRepo.findByEmail(newLogin.getEmail());
if(!potentialUser.isPresent()) {
result.rejectValue("email", "Unique", "Unknown email!");
return null;
}
// if the passwords do not match
User user = potentialUser.get();
if(!BCrypt.checkpw(newLogin.getPassword(), user.getPassword())) {
result.rejectValue("password", "Matches", "Invalid Password!");
}
// where there errors in the last checks or form?
if(result.hasErrors()) {
return null;
} else {
return user;
}
}
// ==========================
// CRUD METHODS
// ==========================
// create
public User createOne(User i) {
return userRepo.save(i);
}
// read all
public List<User> getAll() {
return userRepo.findAll();
}
// read one
public User getOne(Long id) {
return userRepo.findById(id).orElse(null);
}
// update
public User updateOne(User i) {
return userRepo.save(i);
}
// delete
public void deleteOne(Long id) {
userRepo.deleteById(id);
}
}
```
#### Controller
- User
```java!
@Controller
public class UserController {
@Autowired
private UserServ userServ;
@GetMapping("/logReg")
public String logReg(Model model, HttpSession session){
// check for session
if(session.getAttribute("userID") != null) {
return "redirect:/dashboard";
}
// Bind empty User and LoginUser objects to the JSP
// to capture the form input
model.addAttribute("newUser", new User());
model.addAttribute("newLogin", new LoginUser());
return "logReg.jsp";
}
@PostMapping("/register")
public String register(@Valid @ModelAttribute("newUser") User newUser,
BindingResult result, Model model, HttpSession session) {
// call a register method in the service
// to do some extra validations and create a new user!
userServ.register(newUser, result);
if(result.hasErrors()) {
// Be sure to send in the empty LoginUser before
// re-rendering the page.
model.addAttribute("newLogin", new LoginUser());
return "logReg.jsp";
}
// No errors!
// Store their ID from the DB in session, i.e. log them in.
session.setAttribute("userID", newUser.getId());
return "redirect:/";
}
@PostMapping("/login")
public String login(@Valid @ModelAttribute("newLogin") LoginUser newLogin,
BindingResult result, Model model, HttpSession session) {
// Add once service is implemented:
User user = userServ.login(newLogin, result);
if(result.hasErrors()) {
model.addAttribute("newUser", new User());
return "logReg.jsp";
}
// No errors!
// Store their ID from the DB in session, i.e. log them in.
session.setAttribute("userID", user.getId());
return "redirect:/";
}
// logout
@GetMapping("/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/";
}
}
```
#### JSP
- Registration
```jsx!
<form:form action="/register" method="POST" modelAttribute="newUser" class="mx-5 my-2">
<div class="border text-center py-3">
<h3>Register</h3>
</div>
<div class="border py-2">
<section>
<form:label path="userName" class="border-end w-50 ps-2 me-1">User Name:</form:label>
<form:input type="text" class="input" path="userName" />
</section>
<form:errors path="userName" class="text-danger ps-2" />
</div>
<div class="border py-2">
<section>
<form:label path="email" class="border-end w-50 ps-2 me-1">Email:</form:label>
<form:input type="email" class="input" path="email" />
</section>
<form:errors path="email" class="text-danger ps-2" />
</div>
<div class="border py-2">
<section>
<form:label path="password" class="border-end w-50 ps-2 me-1">Password:</form:label>
<form:input type="password" class="input" path="password" />
</section>
<form:errors path="password" class="text-danger text-right" />
</div>
<div class="border py-2">
<section>
<form:label path="confirm" class="border-end w-50 ps-2 me-1">Confirm Password:</form:label>
<form:input type="password" class="input" path="confirm" />
</section>
<form:errors path="confirm" class="text-danger ps-2" />
</div>
<button class="btn btn-secondary my-1 w-100">Register</button>
</form:form>
```
- Login
```jsx!
<form:form action="/login" method="POST" modelAttribute="newLogin" class="mx-5 mt-3">
<div class="border text-center py-3">
<h3>Login</h3>
</div>
<div class="border py-2">
<section>
<form:label path="email" class="border-end w-50 ps-2 me-1">Email</form:label>
<form:input type="email" class="input" path="email" />
</section>
<form:errors path="email" class="text-danger" />
</div>
<div class="border py-2">
<section>
<form:label path="password" class="border-end w-50 ps-2 me-1">Password</form:label>
<form:input type="password" class="input" path="password" />
</section>
<form:errors path="password" class="text-danger" />
</div>
<button class="btn btn-primary my-1 w-100">Login</button>
</form:form>
```