I am stuck with an error in a simple Spring Boot application.
My error: Bean property 'menu' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
The problem arises when I click on the "Add Menu" button in the Thymeleaf view.
I searched on the web for possible solutions and even if it may look like a straight forward bug, I don't figure out how to manage it.
I changed and checked several times the Menu class but I don't figure out the problem.
The application is for creating a custom menu with cheeses.
The MenuController:
package com.ghiurutan.springdemo.controllers;
import com.ghiurutan.springdemo.data.CheeseDAO;
import com.ghiurutan.springdemo.data.MenuDAO;
import com.ghiurutan.springdemo.models.Cheese;
import com.ghiurutan.springdemo.models.Menu;
import com.ghiurutan.springdemo.models.forms.AddMenuItemForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.validation.Valid;
#Controller
#RequestMapping("menu")
public class MenuController {
#Autowired
private MenuDAO menuDAO;
#Autowired
private CheeseDAO cheeseDAO;
#RequestMapping(value = "")
public String index(Model model) {
model.addAttribute("title", "Menus");
model.addAttribute("menus", menuDAO.findAll());
return "menu/index";
}
#RequestMapping(value = "add", method = RequestMethod.GET)
public String add(Model model) {
model.addAttribute("title", "Add Menu");
model.addAttribute(new Menu());
return "menu/add";
}
#RequestMapping(value = "add", method = RequestMethod.POST)
public String add(Model model, #ModelAttribute #Valid Menu menu, Errors errors) {
if (errors.hasErrors()) {
model.addAttribute("title", "Add Menu");
model.addAttribute(new Menu());
return "menu/add";
}
menuDAO.save(menu);
return "redirect:view/" + menu.getId();
}
#RequestMapping(value = "view/{menuId}", method = RequestMethod.GET)
public String viewMenu(Model model, #PathVariable int menuId) {
Menu menu = menuDAO.findOne(menuId);
model.addAttribute("title", menu.getName());
model.addAttribute("cheeses", menu.getCheeses());
model.addAttribute("menuId", menu.getId());
return "menu/view";
}
#RequestMapping(value = "add-item/{menuId}", method = RequestMethod.GET)
public String addItem(Model model, #PathVariable int menuId) {
Menu menu = menuDAO.findOne(menuId);
AddMenuItemForm form = new AddMenuItemForm(cheeseDAO.findAll(), menu);
model.addAttribute("title", "Add item to menu: " + menu.getName());
model.addAttribute("form", form);
return "menu/add-item";
}
#RequestMapping(value = "add-item", method = RequestMethod.POST)
public String addItem(Model model, #ModelAttribute #Valid AddMenuItemForm form, Errors errors) {
if (errors.hasErrors()) {
model.addAttribute("form", form);
model.addAttribute("title", "Add item to menu: " + form.getMenu().getName());
return "menu/add-item";
}
Cheese cheese = cheeseDAO.findOne(form.getCheeseId());
Menu menu = menuDAO.findOne(form.getMenuId());
menu.addItem(cheese);
menuDAO.save(menu);
return "redirect:/menu/view/" + menu.getId();
}
}
The Menu model class:
package com.ghiurutan.springdemo.models;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
#Entity
public class Menu {
#Id
#GeneratedValue
private int id;
#NotNull
#Size(min = 3, max = 15)
private String name;
#ManyToMany
private List<Cheese> cheeses;
public Menu() {
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void addItem(Cheese item) {
cheeses.add(item);
}
public List<Cheese> getCheeses() {
return cheeses;
}
}
The thymeleaf view from where I press the Add Menu button:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head th:replace="fragments :: head"></head>
<body class="container">
<h1 th:text="${title}">Default title</h1>
<nav th:replace="fragments :: navigation"></nav>
<ul>
<li th:each="menu : ${menus}">
<a th:href="'/menu/view/' + ${menu.id}" th:text="${menu.name}"></a>
</li>
</ul>
<p>
Add Menu
</p>
</body>
</html>
And the thymeleaf view template that crashes when is trying to render:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head th:replace="fragments :: head"></head>
<body class="container">
<h1 th:text="${title}">Default Title</h1>
<nav th:replace="fragments :: navigation"></nav>
<form method="post" style="max-width: 600px" th:object="${menu}">
<div class="form-group">
<label th:for="name">Name</label>
<input class="form-control" th:field="*{menu}"/>
<span th:errors="*{name}" class="error"></span>
</div>
<input type="submit" value="Create Menu"/>
</form>
</body>
</html>
In the web page appears: Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor' (menu/add:12)
The line 12 is: <input class="form-control" th:field="*{menu}"/>
Thanks a lot!
Related
I'm trying to create a login system with MySQL and spring, and I've been able to successfully register a user and enter that data into MySQL, but I've been unable to login a user, as I keep getting incorrect username/password error. My #GetMapping works for the login controller since the log.info keeps giving me this every time I reload the page:
Logging in User(id=null, firstname=null, lastname=null, email=null, username=null, password=null)
But the #PostMapping refuses to work and I can't even log anything.
My Login controller
package com.example.candyshop;
import javax.validation.Valid;
import java.util.Optional;
import java.time.*;
import java.util.*;
import lombok.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64.Encoder;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.BindingResult;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.authentication.*;
import org.springframework.security.core.*;
import java.security.Principal;
import lombok.extern.slf4j.Slf4j;
#Slf4j
#Controller
#RequestMapping(value={"/login"})
public class LoginController {
#Autowired
private UserRepository userRepo;
#Autowired
private UserService userService;
#Data
class Message{
private String message;
public Message(String m) {
message = m;
}
}
class ErrorMessage{
private ArrayList<Message> messages = new ArrayList<Message>();
public void add(String m){
messages.add(new Message(m));
}
public ArrayList<Message> getMessages(){
return messages;
}
public void print(){
for(Message msg:messages){
System.out.print(msg.message);
}
}
}
#GetMapping
public String login(#ModelAttribute("login") User user, Model model) {
log.info("Logging in " + user);
return "login";
}
#PostMapping
public String login(#ModelAttribute("user") User user) {
User oauthUser = userService.login(user.getUsername(), user.getPassword());
log.info("User " + oauthUser);
if(Objects.nonNull(oauthUser)) {
return "welcome";
} else {
return "login";
}
}
}
Login.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Candy Shop Login</title>
<link rel="stylesheet" href="../static/css/homepage.css" th:href="#{/css/homepage.css}">
<link rel="stylesheet" href="../static/css/styles.css" th:href="#{/css/styles.css}">
</head>
<body>
<div class = "topnav">
<img class="logo" src="../static/images/candyshoplogo.webp" th:src="#{/images/candyshoplogo.webp}">
</div><br>
<div>
<a class="form-group" th:href="#{/homepage}">Candy Shop</a>
</div>
<div class="signup-form">
<div th:if="${param.error}">
Invalid username and password.
</div>
<div th:if="${param.logout}">
You have been logged out.
</div>
<form action="/examples/actions/confirmation.php" th:action="#{/login}" th:object="${login}" method="post">
<div class="form">
<h2>Sign in</h2>
</div>
<div class="form">
<label>Username</label>
<input type="text" class="form-control" name="username" th:field="*{username}" required="required">
</div>
<div class="form">
<label>Password</label>
<input type="text" class="form-control" name="password" th:field="*{password}" required="required">
</div>
<div class="form">
<button type="submit" class="btn btn-primary btn-block btn-lg">Sign In</button>
</div>
</form>
</div>
</body>
</html>
UserService Class
package com.example.candyshop;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class UserService {
#Autowired
private UserRepository userRepo;
public User login(String username, String password) {
User user = userRepo.findByUsernameAndPassword(username, password);
return user;
}
}
User class
package com.example.candyshop;
import lombok.*;
import javax.persistence.*;
import javax.validation.*;
import java.util.*;
import org.hibernate.validator.constraints.Length;
#Entity
#Table(name ="Users")
#Data
#RequiredArgsConstructor
public class User {
private #Id #GeneratedValue Long id;
private String firstname ;
private String lastname ;
private String email ;
private String username ;
private String password ;
}
you should not use #ModelAttribute for logging in.
#ModelAttribute will take a query string. data is being passed to the server through the url.
you should you #RequestBody instead.
data will be pass to the server through a full JSON body.
basically, #ModelAttribute and #RequestBody work with different formats.
and for that reason you are getting Null.
--> when i am Updating it was not saving the entered details insted it was saving null. when deleting it throws error.
DataBase : restonetoone
application.properties
/*****************************/
crm.rest.url=http://localhost:8080/restonetoone/api/details
-->when i use to update it was taking null value insted of inserted data.
Details [id=25, city=null, mobileno=0, student=Student [id=25, name=null, clas=0]]
->when i was deleting it throws this error.
Mar 06, 2021 4:09:53 PM org.springframework.web.servlet.DispatcherServlet noHandlerFound
WARNING: No mapping for GET /restclient/student/delete
the Details.java class Onetoone with student.java class
Details.java
/******************/
package com.rest.entity;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
#Entity
#Table(name="details")
public class Details {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String city;
private long mobileno;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="student_id")
private Student student;
public Details(){
}
public Details(int id, String city, long mobileno, Student student) {
super();
this.id = id;
this.city = city;
this.mobileno = mobileno;
this.student = student;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public long getMobileno() {
return mobileno;
}
public void setMobileno(long mobileno) {
this.mobileno = mobileno;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
#Override
public String toString() {
return "Details [id=" + id + ", city=" + city + ", mobileno=" + mobileno + ", student=" + student + "]";
}
}
student.java class
Student.java
/******************/
package com.rest.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="student")
public class Student {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
private int clas;
public Student() {
}
public Student(int id, String name, int clas) {
super();
this.id = id;
this.name = name;
this.clas = clas;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getClas() {
return clas;
}
public void setClas(int clas) {
this.clas = clas;
}
#Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", clas=" + clas + "]";
}
}
DetailsServImpl.java class(service layers)
DetailsServImpl.java
/******************/
package com.rest.service;
import java.util.List;
import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.rest.entity.Details;
#Service
public class DetailsServImpl implements DetailsServ {
private RestTemplate restTemplate;
private String crmRestUrl;
private Logger logger = Logger.getLogger(getClass().getName());
#Autowired
public DetailsServImpl(RestTemplate theRestTemplate,
#Value("${crm.rest.url}") String theUrl) {
restTemplate = theRestTemplate;
crmRestUrl = theUrl;
logger.info("Loaded property: crm.rest.url=" + crmRestUrl);
}
#Override
public List<Details> getDetails() {
logger.info("in getDetails(): Calling REST API " + crmRestUrl);
// make REST call
ResponseEntity<List<Details>> responseEntity =
restTemplate.exchange(crmRestUrl, HttpMethod.GET, null,
new ParameterizedTypeReference<List<Details>>() {});
// get the list of customers from response
List<Details> theDetails = responseEntity.getBody();
logger.info("in getDetails(): details" + theDetails);
return theDetails;
}
#Override
public Details getDetails(int theId) {
logger.info("in getDetails(): Calling REST API " + crmRestUrl);
// make REST call
Details theDetails =
restTemplate.getForObject(crmRestUrl + "/" + theId,
Details.class);
logger.info("in saveDetails(): theDetails=" + theDetails);
return theDetails;
}
#Override
public void saveDetails(Details theDetails) {
logger.info("in saveSt(): Cudentalling REST API " + crmRestUrl);
int detailsId = theDetails.getId();
// make REST call
if (detailsId == 0) {
// add employee
restTemplate.postForEntity(crmRestUrl, theDetails, String.class);
} else {
// update employee
restTemplate.put(crmRestUrl, theDetails);
}
logger.info("in saveDetails(): success");
}
#Override
public void deleteDetails(int theId) {
logger.info("in deleteDetails(): Calling REST API " + crmRestUrl);
// make REST call
restTemplate.delete(crmRestUrl + "/" + theId);
logger.info("in deleteDetails(): deleted Details theId=" + theId);
}
}
StudentServImpl.java class
StudentServImpl.java
/******************/
package com.rest.service;
import java.util.List;
import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.rest.entity.Details;
import com.rest.entity.Student;
#Service
public class StudentServImpl implements StudentServ {
private RestTemplate restTemplate;
private String crmRestUrl;
private Logger logger = Logger.getLogger(getClass().getName());
#Autowired
public StudentServImpl(RestTemplate theRestTemplate,#Value("${crm.rest.url}") String theUrl){
restTemplate = theRestTemplate;
crmRestUrl = theUrl;
logger.info("Loaded property: crm.rest.url=" + crmRestUrl);
}
#Override
public List<Student> getStudent() {
logger.info("in getCustomers(): Calling REST API " + crmRestUrl);
// make REST call
ResponseEntity<List<Student>> responseEntity =
restTemplate.exchange(crmRestUrl, HttpMethod.GET, null,
new ParameterizedTypeReference<List<Student>>() {});
// get the list of customers from response
List<Student> theStudent = responseEntity.getBody();
logger.info("in getCustomers(): customers" + theStudent);
return theStudent;
}
#Override
public void saveStudent(Student theStudent) {
logger.info("in saveSt(): Cudentalling REST API " + crmRestUrl);
int studentId = theStudent.getId();
// make REST call
if (studentId == 0) {
// add employee
restTemplate.postForEntity(crmRestUrl, theStudent, String.class);
} else {
// update employee
restTemplate.put(crmRestUrl, theStudent);
}
logger.info("in saveStudent(): success");
}
#Override
public Student getStudent(int theId) {
logger.info("in getCustomer(): Calling REST API " + crmRestUrl);
// make REST call
Student theStudent =
restTemplate.getForObject(crmRestUrl + "/" + theId,
Student.class);
logger.info("in saveCustomer(): theCustomer=" + theStudent);
return theStudent;
}
#Override
public void deleteStudent(int theId) {
logger.info("in deleteStudent(): Calling REST API " + crmRestUrl);
// make REST call
restTemplate.delete(crmRestUrl + "/" + theId);
logger.info("in deleteStudent(): deleted Student theId=" + theId);
}
}
Now the controller class(here i am using single controller for multiple entites).
ClientController.java
/******************/
package com.rest.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.rest.entity.Details;
import com.rest.entity.Student;
import com.rest.service.DetailsServ;
import com.rest.service.StudentServ;
#Controller
#RequestMapping("/cont")
public class ClientController {
#Autowired
private DetailsServ detailsServ;
#Autowired
private StudentServ studentServ;
#GetMapping("/showForm")
public String showForm(Model theModel){
Details theDetails = new Details();
Student theStudent = new Student();
theDetails.setStudent(theStudent);
theModel.addAttribute("for",theDetails);
return "the-for";
}
#PostMapping("/saveForm")
private String saveForm(#ModelAttribute("for") Details theDetails) {
detailsServ.saveDetails(theDetails);
return "redirect:/cont/list";
}
#GetMapping("/list")
public String showList(Model theModel) {
List<Details> theDetails = detailsServ.getDetails();
theModel.addAttribute("details", theDetails);
return "list-table";
}
#GetMapping("/update")
public String updateDetails(#RequestParam("detailsId") int theId, Model theModel) {
Details theDetails =detailsServ.getDetails(theId);
theModel.addAttribute("for",theDetails);
return "the-for";
}
#GetMapping("/delete")
public String deleteDetails(#RequestParam("detailsId") int theId) {
detailsServ.deleteDetails(theId);
return "redirect:/cont/list";
}
}
I use this form for both saving and updating.
the-for.jsp
/******************/
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%# page import="java.sql.*"%>
<!DOCTYPE html>
<html>
<head>
<title>Save Customer</title>
<link type="text/css"
rel="stylesheet"
href="${pageContext.request.contextPath}/resources/css/style.css">
<link type="text/css"
rel="stylesheet"
href="${pageContext.request.contextPath}/resources/css/add-customer-style.css">
</head>
<body>
<div id="wrapper">
<div id="header">
<h2>Form</h2>
</div>
</div>
<div id="container">
<h3>Save StudentDetails</h3>
<form:form action="saveForm" modelAttribute="for" method="POST">
<!-- need to associate this data with customer id -->
<form:hidden path="id" />
<table>
<tbody>
<tr>
<td><label>City name:</label></td>
<td><form:input path="city" /></td>
</tr>
<tr>
<td><label>mobileno:</label></td>
<td><form:input path="mobileno" /></td>
</tr>
<tr>
<td><label>name:</label></td>
<td><form:input path="student.name" /></td>
</tr>
<tr>
<td><label>class:</label></td>
<td><form:input path="student.clas" /></td>
</tr>
<tr>
<td><label></label></td>
<td><input type="submit" value="Save" class="save" /></td>
</tr>
</tbody>
</table>
</form:form>
<div style="clear; both;"></div>
<p>
Back to List
</p>
</div>
</body>
</html>
Now the table which shows the CRUD dynamically.
list-table.jsp
/******************/
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%# page import = "java.sql.*" %>
<!DOCTYPE html>
<html>
<head>
<title>List</title>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link
href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.0-beta1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1"
crossorigin="anonymous">
</head>
<body>
<div class="container">
<h2 class="pt-2">Student Manager</h2>
<!-- put new button: Add Student -->
<input type="button" value="Add Student"
onclick="window.location.href='showForm'; return false;"
class="btn btn-primary btn-sm mb-3" />
<!-- add our html table here -->
<table class="table table-bordered table-striped">
<thead class="table-dark">
<tr>
<th>city</th>
<th>mobileno</th>
<th>name</th>
<th>class</th>
<th>Action</th>
</tr>
</thead>
<!-- loop over and print our students -->
<tbody>
<c:forEach var="tempDetails" items="${details}">
<!-- construct an "update" link with student id -->
<c:url var="updateLink" value="/cont/update">
<c:param name="detailsId" value="${tempDetails.id}" />
</c:url>
<!-- construct an "delete" link with student id -->
<c:url var="deleteLink" value="/student/delete">
<c:param name="studentId" value="${tempStudent.id}" />
</c:url>
<tr>
<td>${tempDetails.city}</td>
<td>${tempDetails.mobileno}</td>
<td>${tempDetails.student.name}</td>
<td>${tempDetails.student.clas}</td>
<td>
<!-- display the update link -->
Update
|
<a href="${deleteLink}"
onclick="if (!(confirm('Are you sure you want to delete this student?'))) return false">Delete</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</body>
</html>
For more clearity sake i am shering the link of my project which include Both client side and backend code files with database.
LINK( https://drive.google.com/file/d/128pvCd6bYdCNhOiG6ERRXbPRUlnhZlTp/view?usp=sharing )
Hey I've got a problem with updating my object. I've got a table of movies where can i delete it or go to details. In details i wanted to have my "update" option. Im giving whole object to movieDetailPage, it shows object details in input fields but i cant update it. My "updateMovie" method doesnt see what i typed in input. For example im changing name from "Movie1" to "Movie2" and method still see "Movie1"
Here's my movieDetailsPage.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form th:object="${movieToUpdate}" method=post >
<p>Movie name: <input type="text" th:field="*{name}"></p>
<p>Movie description: <input type="text" th:field="*{description}"></p>
<img th:src="${movieToUpdate.getImageUrl()}" th:width="300" th:height="300"><br>
<p>Nowy image url: <input type="text" th:field="*{imageUrl}" class="cloudinary-fileupload"></p>
<a th:href="${'/movie/update/'+movieToUpdate.getId()}">Update</a><br>
</form>
</body>
</html>
and my controller class
package pl.fsaaa.filmapp.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import pl.fsaaa.filmapp.model.Movie;
import pl.fsaaa.filmapp.service.CloudinaryImageUploader;
import pl.fsaaa.filmapp.service.MovieService;
import java.io.IOException;
import java.util.List;
#Controller
public class MovieController {
private CloudinaryImageUploader cloudinaryImageUploader;
private MovieService movieService;
#Autowired
public MovieController(MovieService movieService, CloudinaryImageUploader cloudinaryImageUploader) {
this.movieService = movieService;
this.cloudinaryImageUploader = cloudinaryImageUploader;
}
#GetMapping("/addmovie")
public String getNewMovie(Model model) {
model.addAttribute("newMovie",new Movie());
// model.addAttribute("newImage",new File(""));
return "addMoviePage";
}
#PostMapping("/add-movie")
public String addMovie(#ModelAttribute Movie movie) throws IOException {
cloudinaryImageUploader.saveImageToCloudinary(movie.getImageUrl());
movie.setImageUrl(cloudinaryImageUploader.getCloudinaryImageUrl());
movieService.addMovie(movie);
return "redirect:/addmovie";
}
#GetMapping("/movies")
public String getAllMovies(Model model) {
List<Movie> movieList = movieService.getAllMovies();
model.addAttribute("allMovies",movieList);
return "moviesPage";
}
#GetMapping("/movie/{id}")
public String getMovieDetail(Model model, #PathVariable Long id) {
Movie movieToUpdate = movieService.getMovieById(id);
System.out.println(movieToUpdate);
model.addAttribute("movieToUpdate", movieToUpdate);
return "movieDetailsPage";
}
#RequestMapping(value = "/movie/update/{id}", method = {RequestMethod.GET,RequestMethod.PUT})
// #PutMapping("movie/update/{id}")
public String updateMovie(#PathVariable Long id, #ModelAttribute Movie movieToUpdate){
// movieToUpdate = movieService.getMovieById(id);
// System.out.println(movieToUpdate);
movieService.addMovie(movieToUpdate);
return "redirect:/movies";
}
#RequestMapping(value = "/movie/delete/{id}", method = {RequestMethod.DELETE,RequestMethod.GET})
public String deleteMovie(#PathVariable Long id){
movieService.deleteMovie(id);
return "redirect:/movies";
}
}
Problem solved.
I've added submit button to details page instead of link:
<form th:action="'/movies/update/'+*{id}" th:object="${movieToUpdate}" method=put >
<p>Movie name: <input type="text" th:field="*{name}"></p>
<p>Movie description: <input type="text" th:field="*{description}"></p>
<img th:src="${movieToUpdate.getImageUrl()}" th:width="300" th:height="300"><br>
<p>New image url: <input type="text" th:field="*{imageUrl}" class="cloudinary-fileupload"></p>
<p><input type="submit" value="Update"></p>
</form>
and changed update edited "movie" method a little bit, because previous one wasn't uploading changed image to cloudinary.
#RequestMapping(value = "/update/{id}", method = {RequestMethod.GET,RequestMethod.PUT})
public String updateMovie(#PathVariable Long id, #ModelAttribute Movie movieToUpdate) throws IOException {
movieToUpdate.setId(id);
cloudinaryImageUploader.saveImageToCloudinary(movieToUpdate.getImageUrl());
movieToUpdate.setImageUrl(cloudinaryImageUploader.getCloudinaryImageUrl());
movieService.addMovie(movieToUpdate);
return "redirect:/movies/all";
}
I'm working on a school project, where you add Todo-items to database, show them, and grant user acces to individual pages created to each item. I'm using Spring and my Delete button nor individual pages showing specific task aren't working. In this project we use JPA to handle database.
Heres my TodoItemDatabaseController:
package wad.tododatabase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
#Controller
public class TodoItemDatabaseController {
#Autowired
private TodoRepository todoRepository;
#GetMapping("/")
public String home(Model model) {
model.addAttribute
("items", this.todoRepository.findAll());
return "index";
}
Here is the Delete that is not working.
#DeleteMapping("/{itemId}")
public String remove(Model model, #PathVariable Long itemId) {
this.todoRepository.delete(this.todoRepository.getOne(itemId));
return "redirect:/";
}
#PostMapping("/")
public String post(#RequestParam String name) {
if (!name.trim().isEmpty()) {
TodoItem item = new TodoItem(name);
todoRepository.save(item);
}
return "redirect:/";
}
Here I'm trying to get specific id, to create individual page for the item.
#GetMapping("/{id}")
public String findOne(Model model, #PathVariable Long id) {
TodoItem t1 = todoRepository.getOne(id);
t1.count();
model.addAttribute("item", t1);
return "item";
}
}
Here are my html files. I post the DELETE part of the index.html file, and item.html, which is the page of individual to-do item.
Index.html:
<table>
<tr>
<th>Task name</th>
<th></th>
</tr>
<tr th:each="item : ${items}">
<td><a th:href="#{/{item}(item=${item.id})}" th:text="${item.name}">Item name</a></td>
<td><form th:action="#{/{item}(item=${item.id})}" th:method="DELETE"><input type="submit" value="Done!"/></form></td>
</tr>
</table>
Item.html:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>TodoDatabase</title>
</head>
<body>
<h1 th:text="${item.name}">item name</h1>
<p>Item has been checked a total of <span th:text="${item.checked}">-1</span> times.</p>
<a th:href="#{/}">Back to listing.</a>
</body>
</html>
Spring also gives 2 WARN statments, I guess they are warnings:
2017-11-13 21:52:13.902 WARN 29312 --- [nio-8080-exec-4] o.s.web.servlet.PageNotFound : Request method 'DELETE' not supported
2017-11-13 21:52:13.903 WARN 29312 --- [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'DELETE' not supported
Here is my TodoRepository interface:
package wad.tododatabase;
/**
*
* #author riku
*
*/
import org.springframework.data.jpa.repository.JpaRepository;
public interface TodoRepository extends JpaRepository<TodoItem, Long> {
}
Any advice how to get this work?
Have a try with this adnotation:
#RequestMapping(value = "/{itemId}"", method = RequestMethod.DELETE)
public #ResponseBody String remove(Model model, #PathVariable Long itemId) {
this.todoRepository.delete(this.todoRepository.getOne(itemId));
return "redirect:/";
}
I have a problem on validating a simple "NumberValidate" Object
Here you see the JSP file:
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<%#taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%#taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<h1>Vul een nummer in:</h1>
<form:form action="form" modelAttribute="number" method="POST">
<form:input path="number"/>
<form:errors path="number"/>
<input type="submit" value="submit"/>
</form:form>
</body>
</html>
Controller:
package controller;
import domain.NumberValidate;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class ValidationController {
#RequestMapping(value = {"form"}, method = RequestMethod.GET)
public String showHomePage(Model model){
model.addAttribute("number", new NumberValidate());
return "validation";
}
#RequestMapping(value = {"form"}, method = RequestMethod.POST)
public String showHomePage(#Valid #ModelAttribute NumberValidate number, BindingResult result){
if(result.hasErrors())
return "validation";
return "success";
}
}
The "NumberValidate" Class:
package domain;
import javax.validation.constraints.Min;
public class NumberValidate {
#Min(50)
private int number;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
When i run the application it starts normal with the textbox etc.
When I type a number less then 40 it gives the error:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'number' available as request attribute
Can somebody help me with this problem?
In showHomePage method, change to #ModelAttribute("number") and:
if(result.hasErrors()) {
return "validation";
}
return "success";
if validation has error then you need to add number attribute in model when returning to validation view. Code snippet is below :
#RequestMapping(value = {"form"}, method = RequestMethod.POST)
public String showHomePage(#Valid #ModelAttribute NumberValidate number, BindingResult result){
if(result.hasErrors()){
model.addAttribute("number", number);
return "validation";
}
return "success";
}