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";
}
Related
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";
}
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.
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>
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.
I try run my simple "hello world" Spring Boot web application, but it doesn't work, I get "Whitelabel error page" all the time.
My Controller
package com.packt.webapp.controller;
#Controller
#RequestMapping("/")
public class HomeController {
public String home(Model model){
String welcome = new String("Witam");
model.addAttribute("welcome", welcome);
return "home";
}
}
Application.java
package com.packt.webapp.controller;
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
home.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Test</title>
<link rel="stylesheet" th:href="#{/css/style.css}" />
</head>
<body>
<h1>Hello</h1>
<span th:text="'Message: ' + ${welcome}"></span>
</body>
</html>
pom.xml
<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.packt.webapp</groupId>
<artifactId>basket</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.1.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
What am I doing wrong? I tried change my project structure, was playing with dependencies, nothing helps. Can somebody enlighten me?
Your home() method is most likely not being registered by spring since there is no #RequestMapping() above the method.
If you want home to be registered to /, you have two options. You can move the #RequestMapping that is currently at the class level to the home method like so...
#Controller
public class HomeController {
#RequestMapping("/")
public String home(Model model){
String welcome = new String("Witam");
model.addAttribute("welcome", welcome);
return "home";
}
}
Or you can add an addition annotation above the home() method and leave it empty.
#Controller
#RequestMapping("/")
public class HomeController {
#RequestMapping()
public String home(Model model){
String welcome = new String("Witam");
model.addAttribute("welcome", welcome);
return "home";
}
}
Based on your comments above, you're accessing the wrong url. Since you've mapped your controller to / and you only have the 1 method, you should access it like:
http://localhost:8080/