Display PostgreSQL DB contents back to HTML with Thymeleaf and JSP - java

I am creating a Spring Boot application in which I have to consume this API, add it's JSON contents to a PostgreSQL DB and adding also a timestamp column to capture and save the exact point of data transmission. Now I want to transmit back to my webpage the contents, I've collected so far.
I searched here and although everything works fine, I am not able to display my data back to my webpage. I am providing a screenshot sample from my webpage, as well as my code so far. You can see also the HTML code at the end of this post.
Is it possible to play with Thymeleaf and JSP, on HTML at the same time?
Any help would be appreciated
Main Class
package com.andrekreou.iot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#EnableJpaRepositories
public class IotApplication {
public static void main(String[] args) {
SpringApplication.run(IotApplication.class, args);
}
}
EntityClass
package com.andrekreou.iot.bitpay;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import javax.persistence.*;
import java.time.LocalDateTime;
#Entity
#Table
#JsonIgnoreProperties(ignoreUnknown = true)
public class BitPayRates {
#Id
#SequenceGenerator(
name = "bitpay_sequence",
sequenceName = "bitpay_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "bitpay_sequence"
)
private Integer id;
private String code;
private String name;
private Long rate;
private java.time.LocalDateTime timestamp;
protected BitPayRates() {
}
public BitPayRates(String code, String name, Long rate, LocalDateTime timestamp) {
this.code = code;
this.name = name;
this.rate = rate;
this.timestamp = timestamp;
}
// Getters and setters
#Override
public String toString() {
return "BitPayRates{" +
"id=" + id +
", code='" + code + '\'' +
", name='" + name + '\'' +
", rate=" + rate +
'}';
}
}
Controller Class
package com.andrekreou.iot.bitpay;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//The controller for the project, which handles HTTP requests
#RestController
public class RestSpringBootController {
//Dependency injection to connect with Service layer
private final Service service;
#Autowired
public RestSpringBootController(Service service) {
this.service = service;
}
#GetMapping(path = "/bitpay")
public List<List<BitPayRates>> getData(){
return service.getData();
}
}
Controller 2 Class
package com.andrekreou.iot.bitpay.controller;
import com.andrekreou.iot.bitpay.service.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
#Controller
public class WelcomeController {
Service service;
#Autowired
public WelcomeController(Service service) {
this.service = service;
}
#Value("${welcome.message}")
private String message;
#GetMapping("/")
public String main(Model model){
model.addAttribute("message", message);
return "welcome";
}
#GetMapping("/hello")
public String mainWithParam(
#RequestParam(name = "name", required = false, defaultValue = "")
String name, Model model){
model.addAttribute("message", name);
return "welcome";
}
#GetMapping("/show-contents")
public String showAllRates(HttpServletRequest request){
request.setAttribute("rates", service.showAllRates());
request.setAttribute("mode","ALL_CONTENTS");
return "databasecontents";
}
}
Service Class
package com.andrekreou.iot.bitpay.service;
import com.andrekreou.iot.bitpay.model.BitPayRates;
import com.andrekreou.iot.bitpay.repository.BitPayRatesRepo;
import org.springframework.beans.factory.annotation.Autowired;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
//The service layer class for business logic implementation
#org.springframework.stereotype.Service
#Transactional
public class Service {
//Dependency injection to connect with Repository layer
private final BitPayRatesRepo bitPayRatesRepo;
#Autowired
public Service(BitPayRatesRepo bitPayRatesRepo) {
this.bitPayRatesRepo = bitPayRatesRepo;
}
public List<List<BitPayRates>> getData() {
return Collections.singletonList(bitPayRatesRepo.findAll());
}
public List<BitPayRates> showAllRates(){
List<BitPayRates> rates = new ArrayList<>();
rates.addAll(bitPayRatesRepo.findAll());
return rates;
}
}
Repository Interface
package com.andrekreou.iot.bitpay;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
//Interface that multiple classes can use and connects with data JPA
//The JPA is a map that takes the variables mapped in BitPayRates class
//as first parameter and as second, returns the data type of the Id.
#Repository
public interface BitPayRatesRepo
extends JpaRepository<BitPayRates,Integer> {
}
Configuration Class
package com.andrekreou.iot.bitpay;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.time.LocalDateTime;
import java.util.List;
//The configuration class to fetch data from url and execute the insertion
//of the data into the PostgreSQL database
#Configuration
public class BitPayRatesConfig {
#Bean
CommandLineRunner commandLineRunner(BitPayRatesRepo bitPayRatesRepo) {
return args -> {
String url = "https://bitpay.com/api/rates";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<List<BitPayRates>> postEntity = restTemplate.exchange(
url,
HttpMethod.GET,
null,
new ParameterizedTypeReference<>() {
});
List<BitPayRates> results = postEntity.getBody();
bitPayRatesRepo.saveAll(results);
System.out.println(results);
};
}
}
HTML Code
<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:c="http://www.w3.org/1999/XSL/Transform">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Spring Boot Thymeleaf Hello World Example</title>
<link rel="stylesheet" th:href="#{/webjars/bootstrap/css/bootstrap.min.css}"/>
<link rel="stylesheet" th:href="#{/styles/main.css}"/>
<script type="text/javascript" th:src="#{/webjars/bootstrap/js/bootstrap.min.js}"></script>
</head>
<body>
<ul>
<li>Home</li>
<li>News</li>
<li>Contact</li>
<li>Github</li>
</ul>
<main role="main" class="container">
<div class="starter-template">
<h1>IoT Application</h1>
</div>
</main>
<c:when test="${mode=='ALL_CONTENTS' }">
<div class="container text-center" id="tasksDiv">
<h3>Database Contents</h3>
<hr>
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Id</th>
<th>Code</th>
<th>Name</th>
<th>Rate</th>
<th>Timestamp</th>
</tr>
</thead>
<tbody>
<c:forEach var="rate" items="${rates}">
<tr>
<td> <c:out value="${rate.id}"/> </td>
<td> <c:out value="${rate.code}"/> </td>
<td> <c:out value="${rate.name'"/> </td>
<td> <c:out value="${rate.rate}"/> </td>
<td> <c:out value="${rate.timestamp}"/> </td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</c:when>
</body>
</html>
POM.XML
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.andrekreou</groupId>
<artifactId>iot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>iot</name>
<description>MSc Thesis</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>0.45</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7-1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.4.0-b180830.0359</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

I think this guide can help you.
Use Model to return rates. It's more easier than your approach. Something like this.
#GetMapping("/show-contents")
public String showAllRates(Model model){
model.addAttribute("rates", service.showAllRates());
model.addAttribute("mode", "ALL_CONTENTS");
return "databasecontents";
}

Related

org.firebirdsql.jdbc.FBSQLExceptionInfo: validation error for column "USERS"."CPF", value "*** null ***"

I'm getting a null value even filling the field and passing the correct parameters, but something is not coming out right,
look at this login page image filled in correctly
https://uploaddeimagens.com.br/imagens/P6b20Tg
SpringSecurity
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
#Configuration
#EnableWebSecurity
public class SpringSecurity {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public static PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests((authorize)-> authorize
.antMatchers("/register/**").permitAll()
.antMatchers("/index").permitAll()
.antMatchers("/users").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
).formLogin(
form -> form
.loginPage("/login")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/users")
.permitAll()
).logout(
logout -> logout
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.permitAll()
);
return http.build();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
}
AuthController
package com.xxxxxxxxxxxxxxxxxxxxxxxxxx.controller;
import com.xxxxxxxxxxxxxxxxxxxxxxxxxx.dto.UserDto;
import com.xxxxxxxxxxxxxxxxxxxxxxxxxx.entity.User;
import com.xxxxxxxxxxxxxxxxxxxxxxxxxx.service.UserService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import javax.validation.Valid;
import java.util.List;
#Controller
public class AuthController {
private UserService userService;
public AuthController(UserService userService) {
this.userService = userService;
}
#GetMapping("index")
public String home(){
return "index";
}
#GetMapping("/login")
public String loginForm() {
return "login";
}
// método handler para lidar com a solicitação de registro do usuário
#GetMapping("register")
public String showRegistrationForm(Model model){
UserDto user = new UserDto();
model.addAttribute("user", user);
return "register";
}
// método manipulador para lidar com a solicitação de envio do formulário de registro do usuário
#PostMapping("/register/save")
public String registration(#Valid #ModelAttribute("user") UserDto user,
BindingResult result,
Model model){
User existing = userService.findByEmail(user.getEmail());
if (existing != null) {
result.rejectValue("email", null, "Já existe uma conta cadastrada com esse e-mail");
}
if (result.hasErrors()) {
model.addAttribute("user", user);
return "register";
}
userService.saveUser(user);
return "redirect:/register?success";
}
#GetMapping("/users")
public String listRegisteredUsers(Model model){
List<UserDto> users = userService.findAllUsers();
model.addAttribute("users", users);
return "users";
}
}
UserDto
package com.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class UserDto
{
private Long id;
#NotEmpty
private String Name;
#NotEmpty
private String CPF;
#NotEmpty(message = "O CPF não deve estar vazio")
#Email
private String email;
#NotEmpty(message = "A senha não deve estar vazia")
private String password;
}
Role
package com.xxxxxxxxxxxxxxxxxxxxxxx.entity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import java.util.List;
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name="roles")
public class Role
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable=false, unique=true)
private String name;
#ManyToMany(mappedBy="roles")
private List<User> users;
}
User
package com.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.entity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name="users")
public class User
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "Nome" ,nullable=false)
private String name;
#Column(nullable = false)
private String CPF;
#Column(nullable=false, unique=true)
private String email;
#Column(nullable=false)
private String password;
#ManyToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
#JoinTable(
name="users_roles",
joinColumns={#JoinColumn(name="USER_ID", referencedColumnName="ID")},
inverseJoinColumns={#JoinColumn(name="ROLE_ID", referencedColumnName="ID")})
private List<Role> roles = new ArrayList<>();
}
RoleRepository
package com.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.repository;
import com.xxxxxxxxxxxxxxxxxxxxxxxxx.entity.Role;
import org.springframework.data.jpa.repository.JpaRepository;
public interface RoleRepository extends JpaRepository<Role, Long> {
Role findByName(String name);
}
UserRepository
package com.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.repository;
import com.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(String email);
}
CustomUserDetailsService
package com.xxxxxxxxxxxxxxxxxxxxxxxxxxxx.service;
import com.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx.entity.Role;
import com.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx.entity.User;
import com.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx.repository.UserRepository;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.stream.Collectors;
#Service
public class CustomUserDetailsService implements UserDetailsService {
private UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
if (user != null) {
return new org.springframework.security.core.userdetails.User(user.getEmail(),
user.getPassword(),
mapRolesToAuthorities(user.getRoles()));
}else{
throw new UsernameNotFoundException("Invalid username or password.");
}
}
private Collection < ? extends GrantedAuthority> mapRolesToAuthorities(Collection <Role> roles) {
Collection < ? extends GrantedAuthority> mapRoles = roles.stream()
.map(role -> new SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toList());
return mapRoles;
}
}
UserServiceImpl
package com.xxxxxxxxxxxxxxxxxxxxxxxxxx.service.impl;
import com.xxxxxxxxxxxxxxxxxxxxxxxxxx.dto.UserDto;
import com.xxxxxxxxxxxxxxxxxxxxxxxxxx.entity.Role;
import com.xxxxxxxxxxxxxxxxxxxxxxxxxx.entity.User;
import com.xxxxxxxxxxxxxxxxxxxxxxxxxx.repository.RoleRepository;
import com.xxxxxxxxxxxxxxxxxxxxxxxxxx.repository.UserRepository;
import com.xxxxxxxxxxxxxxxxxxxxxxxxxx.service.UserService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
#Service
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
private RoleRepository roleRepository;
private PasswordEncoder passwordEncoder;
public UserServiceImpl(UserRepository userRepository,
RoleRepository roleRepository,
PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
this.passwordEncoder = passwordEncoder;
}
#Override
public void saveUser(UserDto userDto) {
User user = new User();
user.setName(userDto.getName() + " " + userDto.getCPF());
user.setEmail(userDto.getEmail());
// criptografar a senha usando o spring security
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
Role role = roleRepository.findByName("ROLE_ADMIN");
if(role == null){
role = checkRoleExist();
}
user.setRoles(Arrays.asList(role));
userRepository.save(user);
}
#Override
public User findUserByEmail(String email) {
return userRepository.findByEmail(email);
}
#Override
public User findByEmail(String email) {
return null;
}
#Override
public List<UserDto> findAllUsers() {
List<User> users = userRepository.findAll();
return users.stream()
.map((user) -> mapToUserDto(user))
.collect(Collectors.toList());
}
private UserDto mapToUserDto(User user){
UserDto userDto = new UserDto();
String[] str = user.getName().split(" ");
userDto.setName(str[0]);
userDto.setCPF(str[1]);
userDto.setEmail(user.getEmail());
return userDto;
}
private Role checkRoleExist(){
Role role = new Role();
role.setName("ROLE_ADMIN");
return roleRepository.save(role);
}
}
register.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
>
<head>
<meta charset="UTF-8">
<title>Registration and Login System</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" th:href="#{/index}">Registration and Login System</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" th:href="#{/login}">Login</a>
</li>
</ul>
</div>
</div>
</nav>
<br /><br /><br />
<div class="container">
<div class="row col-md-8 offset-md-2">
<div class="card">
<div class="card-header">
<h2 class="text-center">Registration</h2>
</div>
<div th:if="${param.success}">
<div class="alert alert-info">
You have successfully registered our app!
</div>
</div>
<div class="card-body">
<form
method="post"
role="form"
th:action="#{/register/save}"
th:object="${user}"
>
<div class="form-group mb-3">
<label class="form-label">First Name</label>
<input
class="form-control"
id="Name"
name="Name"
placeholder="Enter first name"
th:field="*{Name}"
type="text"
/>
<p th:errors = "*{Name}" class="text-danger"
th:if="${#fields.hasErrors('Name')}"></p>
</div>
<div class="form-group mb-3">
<label class="form-label">CPF</label>
<input
class="form-control"
id="CPF"
name="CPF"
placeholder="CPF"
th:field="*{CPF}"
type="text"
/>
<p th:errors = "*{CPF}" class="text-danger"
th:if="${#fields.hasErrors('CPF')}"></p>
</div>
<div class="form-group mb-3">
<label class="form-label">Email</label>
<input
class="form-control"
id="email"
name="email"
placeholder="Enter email address"
th:field="*{email}"
type="email"
/>
<p th:errors = "*{email}" class="text-danger"
th:if="${#fields.hasErrors('email')}"></p>
</div>
<div class="form-group mb-3">
<label class="form-label">Password</label>
<input
class="form-control"
id="password"
name="password"
placeholder="Enter password"
th:field="*{password}"
type="password"
/>
<p th:errors = "*{password}" class="text-danger"
th:if="${#fields.hasErrors('password')}"></p>
</div>
<div class="form-group">
<button class="btn btn-primary" type="submit">Register</button>
<span>Already registered? <a th:href="#{/login}">Login here</a></span>
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
application.properties
spring.jpa.hibernate.ddl-auto=update
#logging.level.org.springframework=DEBUG
application.yml
spring:
datasource:
driver-class-name: org.firebirdsql.jdbc.FBDriver
url: jdbc:firebirdsql:localhost/3050:C:/DB/usuarios.FDB?sql_dialect=3&charSet=utf-8
username: SYSDBA
password: masterkey
hikari:
connection-timeout: 1000
login-timeout: 1000
minimum-idle: 10
jpa:
database-platform: org.hibernate.community.dialect.FirebirdDialect
hibernate:
ddl-auto: validate
spom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xxxxxxx</groupId>
<artifactId>xxxxxxxxxxx</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>DashBoardBackend</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<squiggly.version>1.3.18</squiggly.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.firebirdsql.jdbc</groupId>
<artifactId>jaybird-jdk17</artifactId>
<version>3.0.10</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-community-dialects</artifactId>
<version>6.0.0.Alpha9</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.github.bohnman</groupId>
<artifactId>squiggly-filter-jackson</artifactId>
<version>1.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Logback
2023-01-16 17:27:20.533 WARN 9548 --- [nio-8080-exec-8] o.a.c.util.SessionIdGeneratorBase : Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [154] milliseconds.
2023-01-16 17:27:38.328 WARN 9548 --- [nio-8080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 335544347, SQLState: 23000
2023-01-16 17:27:38.328 ERROR 9548 --- [nio-8080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper : validation error for column "USERS"."CPF", value "*** null ***" [SQLState:23000, ISC error code:335544347]
2023-01-16 17:27:38.334 WARN 9548 --- [nio-8080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: 0, SQLState: null
2023-01-16 17:27:38.334 WARN 9548 --- [nio-8080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper : Connection.isValid does not support non-zero timeouts, timeout value 5 has been ignored
2023-01-16 17:27:38.336 ERROR 9548 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
org.firebirdsql.jdbc.FBSQLExceptionInfo: validation error for column "USERS"."CPF", value "*** null ***"

Take input from Thymeleaf. Translate into ModelAttribute. Doesn't recognize the #ModelAttribute bean. Cannot redirect to new page

I am trying to create a new Movie. This means that I want to access the "addMovie" URL, to generate a form in which I add the title of a new movie. This title should be stored in a movieDTO already from the Thymeleaf form. Then, when I press "Submit" I want it to send me to a new page "/saveMovie" that receives the movieDTO from the previous form. Using this movieDTO, I create a new Movie and then "redirect:movie/movies-all" to show all the current movies (including the added one).
However, this does not happen. When I access the URL localhost:8081/addMovie I get these errors:
org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor' (template: "movie/movie-add" - line 18, col 40)
and this one
Caused by: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'movieDTO' available as request attribute at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:153) ~[spring-webmvc-5.3.9.jar:5.3.9].
Thus, I don't understand what the issue is. Is it that it does not store the information in the movieDTO correctly (or at all)? or is it because the bean movieDTO is not sent to the post method?
The Project Structure:
The Controller:
package movie_API.movie.controllers;
import movie_API.movie.services.MovieService;
import movie_API.movie.services.beans.MovieDTO;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import java.util.Arrays;
import java.util.List;
#Controller
//#RequestMapping("/something") <- if you want a general something before the other resources
public class MovieController {
private final MovieService movieService;
public MovieController(MovieService movieService) {
this.movieService = movieService;
}
#RequestMapping("/addMovie")
public String add() {
return "movie/movie-add";
}
#RequestMapping(value = "/saveMovie", method = RequestMethod.POST)
public String create(#ModelAttribute("movieDTO") #Valid MovieDTO movieDTO, Model model, BindingResult result) {
System.out.println("got into post");
if (result.hasErrors()) {
System.out.println("error in result");
return "movie/movie-add";
}
//movieService.saveNewMovie(movieDTO);
movieService.showMovies(model);
return "redirect:movie/movies-all";
}
}
The movieDTO + the corresponding getters and setters:
package movie_API.movie.services.beans;
import java.io.Serializable;
public class MovieDTO implements Serializable {
private static final long serialVersionUID = -8040351309785589042L;
private String metascore;
private String title;
private String year;
private String description;
private String genreId;
public MovieDTO(String metascore, String title, String year, String description, String genreId) {
this.metascore = metascore;
this.title = title;
this.year = year;
this.description = description;
this.genreId = genreId;
}
}
The movie-add.html:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Add Movie</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css">
</head>
<body>
<div class="container my-5">
<h2 class="mb-5">New Movie</h2>
<div class="row">
<div class="col-md-6">
<form action="#" th:action="#{/saveMovie}" th:object="${movieDTO}" method="post">
<label>
<input type="text" th:field="*{title}" name="title" class="form-control" placeholder="insert string here">
</label>
<input type="submit" class="btn btn-primary" value="Submit">
</form>
</div>
</div>
</div>
</body>
</html>
The movies-all.html:
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org/" lang="en">
<head>
<title> All Movies </title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<h2>Movies table</h2>
<p>Here are all the movies we have in the database:</p>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>metascore</th>
<th>title</th>
<th>year</th>
<th>description</th>
<th>genre</th>
</tr>
</thead>
<tbody>
<tr th:each="movie, stat: ${allMovies}">
<td th:text="${movie.id}"></td>
<td th:text="${movie.metascore}"></td>
<td th:text="${movie.title}"></td>
<td th:text="${movie.year}"></td>
<td th:text="${movie.description}"></td>
<td th:text="${allGenres[stat.index]}"></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
The movieService:
package movie_API.movie.services;
import movie_API.movie.models.Movie;
import movie_API.movie.repositories.GenreRepository;
import movie_API.movie.repositories.MovieRepository;
import movie_API.movie.services.beans.MovieDTO;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import java.util.List;
#Service
public class MovieService {
private final MovieRepository movieRepository;
private final GenreRepository genreRepository;
private List<Movie> movies;
private Movie movie;
public MovieService(MovieRepository movieRepository, GenreRepository genreRepository) {
this.movieRepository = movieRepository;
this.genreRepository = genreRepository;
}
public void showMovies(Model m) {
m.addAttribute("allMovies", getMovies());
//System.out.println("done with movies");
m.addAttribute("allGenres", getGenreNames());
//System.out.println("done with genres");
}
public void showMovieByID(Model m, String id) {
m.addAttribute("movie", getMovieById(id));
m.addAttribute("genreName", getGenreNameByMovieId());
}
public List<Movie> getMovies() {
movies = movieRepository.findAll();
//System.out.println(movies.get(0).toString());
return movies;
}
public Movie getMovieById(String id) {
this.movie = movieRepository.findById(id).get();
return movie;
}
public List<String> getGenreNames() {
return movieRepository.getGenreNames(movies, genreRepository);
}
public String getGenreNameByMovieId() {
String genreId = movie.getGenreId();
return genreRepository.findById(genreId).get().getName();
}
public void saveNewMovie(MovieDTO movieDTO) {
Movie movie = new Movie();
movie.setTitle(movieDTO.getTitle());
movie.setYear(movieDTO.getYear());
movie.setMetascore(movieDTO.getMetascore());
movie.setDescription(movieDTO.getDescription());
movie.setGenreId(movieDTO.getGenreId());
movieRepository.save(movie);
}
public void updateMovie(MovieDTO movieDTO, String id) {
Movie movie = movieRepository.findById(id).get();
movie.setTitle(movieDTO.getTitle());
movie.setYear(movieDTO.getYear());
movie.setMetascore(movieDTO.getMetascore());
movie.setDescription(movieDTO.getDescription());
movie.setGenreId(movieDTO.getGenreId());
movieRepository.save(movie);
}
public void deleteMovie(String id) {
movieRepository.deleteById(id);
}
}
The pom.xml:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>movie_API</groupId>
<artifactId>movie</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>movie</name>
<description>Movie REST API</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-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>
Main problems:
What are the errors? Why is the movieDTO not created according to the given input from the User? (I can only assume it is not properly created because it's always null when I want to access it)
Why is the redirect not recognizing the movies-all location?
for the first error, just add an empty DTO to the model;
#RequestMapping("/addMovie")
public String add(Model model) {
model.addAttribute("movieDTO", new MovieDTO());
return "movie/movie-add";
}
For the second error, remove the "redirect:" part
movieService.showMovies(model);
return "movie/movies-all";
Then you will get the pages you want;
A warning;
Please add default constructor, setters/getters and id field to the DTO.

Spring Boot Application Error Jslt/jsp tags giving exception

Error
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Mon Apr 27 21:00:28 BST 2020
There was an unexpected error (type=Internal Server Error, status=500).
The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the jar files deployed with this application
controller
package com.sales.controllers;
import java.util.ArrayList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.sales.models.Product;
import com.sales.services.ProductService;
#Controller
public class MainController {
#Autowired
ProductService ps;
#RequestMapping(value = "/showProducts.html")
public String getProducts(Model model) {
ArrayList<Product> products = ps.getAllProducts();
model.addAttribute("products", products);
return "listProducts";
}
#RequestMapping(value = "/addProduct.html")
public String addPerson(Model model) {
Product p = new Product();
model.addAttribute("products", p);
return "addProduct";
}
}
listProducts.js
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>List of Products</title>
</head>
<body>
<h1>List of Products</h1>
<table>
<tr>
<th>Product ID</th>
<th>Description</th>
<th>Quantity in Stock</th>
</tr>
<tr>
<c:forEach items="${products}" var="product">
<tr>
<td>${product.pId}</td>
<td>${product.pDesc}</td>
<td>${product.qtyInStock}</td>
</tr>
</c:forEach>
</tr>
<tr>
<td>Home</td>
</tr>
</table>
</body>
</html>
product class
package com.sales.models;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.Min;
import org.hibernate.validator.constraints.NotBlank;
#Entity
#Table(name="PRODUCTS")
public class Product {
#Id
#GeneratedValue
#Column(name="PID")
private Long pId;
#Column(name="PDESC")
#NotBlank
private String pDesc;
#Column(name="QTYINSTOCK")
#Min(value=0)
private int qtyInStock;
#OneToMany(mappedBy="prod")
private List<Order> ordersForProduct = new ArrayList<Order>();
public Long getpId() {
return pId;
}
public void setpId(Long pId) {
this.pId = pId;
}
public String getpDesc() {
return pDesc;
}
public void setpDesc(String pDesc) {
this.pDesc = pDesc;
}
public int getQtyInStock() {
return qtyInStock;
}
public void setQtyInStock(int qtyInStock) {
this.qtyInStock = qtyInStock;
}
public List<Order> getOrdersForProduct() {
return ordersForProduct;
}
public void setOrdersForProduct(List<Order> ordersForProduct) {
this.ordersForProduct = ordersForProduct;
}
}
run class
package com.sales;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class FinalProjV1Application {
public static void main(String[] args) {
SpringApplication.run(FinalProjV1Application.class, args);
}
}
pom.xml
<?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.sales</groupId>
<artifactId>Sales</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>FinalProjV1</name>
<description>Sales Application</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.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-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

PostMapping Spring boot annotation is not working

I am new to spring boot and do not know why my #PostMapping is not working and leading to a white label error. Everything else works fine and I can add customers to the h2 database. However, once I add customers and go to URL localhost:8084/getdetails and type 1, a white label error occurs. However, the form in the ViewCustomers.jsp file is still displayed but cannot take in any input. The form should invoke toString() of the customer object and its fields.
Controller
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class FormController {
#Autowired
CustomerRepo repo;
#RequestMapping("/")
public String details() {
return "edureka";
}
#RequestMapping("/details")
public String details(Customers customers) {
repo.save(customers);
return "edureka";
}
#RequestMapping("/getdetails")
public String getdetails() {
return "ViewCustomers";
}
#PostMapping("/getdetails")
public ModelAndView getdetails(#RequestParam int cid) {
System.out.println("here");
ModelAndView mv = new ModelAndView("Retrieve");
Customers customers = repo.findById(cid).orElse(null);
mv.addObject(customers);
return mv;
}
}
edureka.jsp
<%# page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Edureka Customers</title>
</head>
<body>
<form method="post" action="details">
Enter Customer ID: <input type="text" name="cid"><br><br>
Enter Customer Name: <input type="text" name="cname"><br><br>
Enter Customer Email Address: <input type="email" name="cemail"><br><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
ViewCustomer.jsp
<%# page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>View Customer Details</h1>
<h2>Details as submitted as follows</h2>
<form method="getdetails" action="post">
<input type="number" name="cid"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
Retrieve.jsp
<%# page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Retrieve Customer Details</h1>
<h2>Details as submitted as follows:</h2>
<h5>${customers}</h5>
</body>
</html>
Customers.java
package com.example.demo;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class Customers {
#Id
private int cid;
private String cname;
private String cemail;
public int getCid() {
return cid;
}
public void setCid(int cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getCemail() {
return cemail;
}
public void setCemail(String cemail) {
this.cemail = cemail;
}
#Override
public String toString() {
return "Customers [cid=" + cid + ", cname=" + cname + ", cemail=" + cemail + "]";
}
}
CustomerRepo interface
package com.example.demo;
import org.springframework.data.repository.CrudRepository;
public interface CustomerRepo extends CrudRepository<Customers,Integer>{
}
SubmissionFormApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import com.example.demo.SubmissionFormApplication;
#ComponentScan
#SpringBootApplication
public class SubmissionFormApplication extends SpringBootServletInitializer{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SubmissionFormApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(SubmissionFormApplication.class, args);
}
}
pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>SubmissionForm</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SubmissionForm</name>
<description>First Spring Boot Web app</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jasper -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>9.0.31</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
The ViewCustomer.jsp form's Method should have method as post and action as get details. The problem is in html and not spring.
A few things:
Your Controller can be annotated like #RestController instead of just #Controller.
Your mappings for POST is #PostMapping - For consistency, I would recommend your mapping for GET to be #GetMapping - So I'd replace all #RequestMapping with #GetMapping (this removes ambiguity and improves readability as well)
The form in your eureka.jsp, needs to change the URL is posting to. Instead of action=details it should point to action=getdetails
The form in your view viewcustomer.jsp has the action and the post switched (the method should be POST the action should be getdetails)
The #PostMapping("/getdetails") maps to the path "/getdetails". Such a mapping already exists. Try to map to another path in order to resolve this conflict.

Spring Boot - Request method 'POST' not supported (Method not allowed)

As I followed this tutorial: https://spring.io/guides/gs/handling-form-submission/ after a while I came to a dead end. I tried to figure out what the problem is for some hours now. I hope you can help me.
When I submit my form, I get this error-message:
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'POST' not supported
My App (App.java):
package de.poc.logging.main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
My Controller (InformationController.java):
package de.poc.logging.main;
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.RequestMethod;
#Controller
#RequestMapping(value = "/info")
public class InformationController {
#RequestMapping(method = RequestMethod.GET, produces = "text/html")
public String infoForm(Model model) {
model.addAttribute("information", new Information());
return "infoForm.html";
}
#RequestMapping(method = RequestMethod.POST, produces = "text/html")
public String infoSubmit(#ModelAttribute Information information) {
return "infoResult.html";
}
}
I created an additional class for security (WebSecurityConf.java):
package de.poc.logging.main.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.frameOptions().sameOrigin()
.httpStrictTransportSecurity().disable();
http.csrf().disable();
}
}
And I have following two HTML-Files:
infoForm.html:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Handling Form Submission</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h1>Form</h1>
<form action="#" th:action="#{/info}" th:object="${information}" method="post">
<p>Id: <input type="text" th:field="*{id}"/></p>
<p>Message: <input type="text" th:field="*{content}"/></p>
<p><input type="submit" value="Submit"/>
<input type="reset" value="Reset"/></p>
<!--<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>-->
</form>
</body>
</html>
infoResult.html:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Handling Form Submission</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h1>Result</h1>
<p th:text="'id: ' + ${information.id}"/>
<p th:text="'content: ' + ${information.content}"/>
Submit another message
</body>
</html>
Edit (additional information):
my Information.java:
package de.poc.logging.main;
public class Information {
private long id;
private String content;
public long getId() {
return id;
}
public String getContent() {
return content;
}
public void setId(long id) {
this.id = id;
}
public void setContent(String content) {
this.content = content;
}
}
my pom-dependencies:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>1.5.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
</dependencies>
I'm using Java 1.8u121 (jdk).
Edit2:
I tried 3 different versions of spring boot now.
Also I downloaded the project from here: https://github.com/spring-guides/gs-handling-form-submission and added spring boot via pom.xml.
The downloaded project does not work for me.
I'm getting really frustrated.
I copied your exact same classes except the web security class and tried to run. The post method is working for me. The only change I made was returning file names without the .html extension.
My controller class looks like this
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.RequestMethod;
import com.example.models.Information;
#Controller
#RequestMapping(value = "/info")
public class InformationController {
#RequestMapping(method = RequestMethod.GET, produces = "text/html")
public String infoForm(Model model) {
model.addAttribute("information", new Information());
return "infoForm";
}
#RequestMapping(method = RequestMethod.POST, produces = "text/html")
public String infoSubmit(#ModelAttribute Information information) {
return "infoResult";
}
}
I found the problem. My pom.xml was wrong.
The right one looks like this:
<?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>org.springframework</groupId>
<artifactId>gs-handling-form-submission</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
Did you try with #RequestParam instead of #ModelAttribute?
Something like this..
#RequestMapping(method = RequestMethod.POST, produces = "text/html")
public String infoSubmit(#RequestParam("info") Information information) {
return "infoResult.html";
}

Categories

Resources