Showing input parameter on same page in Thymeleaf - spring

I am trying to show the results on the same page as the form in Thymleaf and Spring and this is what I have tried till now:
Controller:
#Controller
public class ThymeleafController {
#GetMapping("/hello")
public String getHello() {
return "hello";
}
#PostMapping("/test")
public String test(#RequestParam("text1")String str, Model model) {
model.addAttribute("sample", str);
return "test";
}
}
test.html page:
<!DOCTYPE html>
<html xmlns:th ="http://www.thymeleaf.org" >
<head>
<meta charset ="UTF-8" ></meta>
<title> Hello World</title>
</head>
<body>
<h1> Hello World</h1>
<form method ="post" action ="/test" >
Enter: <input type ="text" name ="text1" />
<input type ="submit" value ="click" />
</form>
<span th:text ="${sample == null} ? 'null' : ${sample}" ></span>
</body>
</html>
When i go on test page i see this error:
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'GET' not supported
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
Can someone give me an ideea or an example how I could display a value on the same page as the form / input ? Thanks

Ref : https://hellokoding.com/handling-form-submission-example-with-java-spring-boot-and-freemarker/
Add for get , so this issue will get solved.
#Controller
public class ThymeleafController {
#GetMapping("/hello")
public String getHello() {
return "hello";
}
#GetMapping("/test")
public String testfrm() {
return "test";
}
#PostMapping("/test")
public String test(#RequestParam("text1")String str, Model model) {
return "test";
}
}

Related

Template might not exist or might not be accessible by any of the configured Template Resolvers springboot

I have a microservice app built-in spring boot, the main module is working at 8080 port and the other module is working at 7070 port. At /clicker endpoint I'm trying to redirect to
the site which is located on this port but always when I'm using /clicker endpoint I can see an error like this. But when I try to get [http://192.168.254.115:7070] directly from the browser, everything is working fine so I think that's not because of the bad config in the application.properties.
Error resolving template [http://192.168.254.115:7070], template might not exist or might not be accessible by any of the configured Template Resolvers
This controller works at 8080 port.
#CrossOrigin(origins = "*")
public class MainController {
#RequestMapping("/")
public String index() {
return "redirect:/login";
}
#RequestMapping("/home")
public String home(){
return "home.html";
}
#CrossOrigin
#RequestMapping("/clicker")
public ModelAndView goToClicker(HttpServletRequest request) {
//http://localhost:7070/
//http://192.168.254.115:7070/
String url = "http://" + request.getServerName() + ":" + "7070";
return new ModelAndView(url);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Home</title>
<link rel="stylesheet" type="text/css" th:href="#{/styles/style.css}"/>
</head>
<body>
<div class = "main">
<div class = "container">
<button type="button">CLICKER</button>
</div>
</div>
</body>
</html>
And that's the controller which works at 7070 port.
#Controller
#CrossOrigin(origins = "*")
public class ClickerController {
#CrossOrigin
#RequestMapping("/")
public String index() {
return "clicker.html";
}
}

Spring mvc form submission bindingresult throws error

So my bindingresult throws an error but I'm not able to see it for some reason and I'm not able to figure out what the problem is. I'm guessing the problem lies with the variable targetDate where there is a problem the type. I've pasted my controller and JSP code below. Any help is appreciated!
#Controller
public class ToDoController {
#Autowired
private ToDoService service;
// All date parameters displayed as mm/DD/yyyy
#InitBinder
protected void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("mm/DD/yyyy");
binder.registerCustomEditor(Date.class, new CustomDateEditor(
dateFormat, false));
}
#RequestMapping(value = "/list-todo", method= RequestMethod.GET)
// HttpSession allows access to the session
public String showToDo(ModelMap model, HttpSession httpSession) {
String user = (String) httpSession.getAttribute("name");
model.addAttribute("todos", service.retrieveTodos(user));
return "list-todos";
}
// redirect to update form
#RequestMapping(value = "/update-todo", method= RequestMethod.GET)
public String getUpdateForm(ModelMap model, #RequestParam int id) {
System.out.println("ID " + id);
// To work with command bean
model.addAttribute("id", id);
model.addAttribute("todo", service.retrieveTodo(id-1));
return "updateToDo";
}
// What does Valid do?
#RequestMapping(value = "/update-todo", method= RequestMethod.POST)
public String submitUpdate(ModelMap model, #Valid ToDo todo, BindingResult result) {
if (result.hasErrors()) {
System.out.println("ERROR" + result.getAllErrors());
// Redirect and pass on the id value
return "redirect:/update-todo?id=" + todo.getId();
}
System.out.println("Update todo" + todo);
service.updateToDo(todo);
model.clear();
return "redirect:/list-todo";
}
// Will be executed first
#RequestMapping(value = "/add-todo", method= RequestMethod.GET)
public String showAddForm(ModelMap model) {
model.addAttribute("todo", new ToDo());
return "addToDo";
}
/*
* Will be executed after form is submitted
* #Valid ToDo - command bean from addToDo.jsp.
* #Valid to validate the information
* #BindingResult showcases the result of the validation
*/
#RequestMapping(value = "/add-todo", method= RequestMethod.POST)
public String submitAddForm(ModelMap model , #Valid ToDo todo, HttpSession httpSession, BindingResult result) {
System.out.println("running" + result);
// If there is validation error , return to addToDos page for user to fix the error
if (result.hasErrors()) {
return "redirect:/showAddForm?id=?" + todo.getId();
}
String user = (String) httpSession.getAttribute("name");
service.addTodo(user, todo.getDescription(), todo.getTargetDate(), false);
// Clears the url e.g. name?=jyj123
model.clear();
// return to the url which executes the showToDO
return "redirect:/list-todo";
}
// delete to do entry
#RequestMapping(value = "/delete-todo", method= RequestMethod.GET)
public String deleteToDo(ModelMap model, #RequestParam int id) {
service.deleteTodo(id);
model.clear();
return "redirect:/list-todo"; }
}
My JSP
<%# taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%# include file = "common/header.jspf" %>
<%# include file = "common/nav.jspf" %>
<div class="container">
<H1>Update your task!</H1>
<form:form method="POST" commandName="todo">
<!-- Carry on the id value -->
<form:hidden path = "id"/>
<form:hidden path = "user"/>
<fieldset class="form-group">
<form:label path="description">Description:</form:label>
<form:input path="description" type="text" class="form-control"
required="required" />
<form:errors path="description" cssClass="text-warning" />
</fieldset>
<fieldset class="form-group">
<form:label path="targetDate">Target Date</form:label>
<form:input path="targetDate" type="text" class="form-control"
required="required" />
<form:errors path="targetDate" cssClass="text-warning" />
</fieldset>
<fieldset class="form-group">
<form:radiobutton path="completion" value="true" />
<form:radiobutton path="completion" value="false" />
</fieldset>
<button type="submit" class="btn btn-success" >Submit Update</button>
</form:form>
<spring:hasBindErrors htmlEscape="true" name="todo">
<c:if test="${errors.errorCount gt 0}">
<h4>The error list :</h4>
<font color="red">
<c:forEach items="${errors.allErrors}" var="error">
<spring:message code="${error.code}"
arguments="${error.arguments}"
text="${error.defaultMessage}"/><br/>
</c:forEach>
</font>
</c:if>
</spring:hasBindErrors>
</div>
<%# include file = "common/footer.jspf" %>
EDIT: bindingresult throws this
ERROR[Field error in object 'todo' on field 'targetDate': rejected value [Tue Jan 05 00:00:00 SGT 2021]; codes [typeMismatch.todo.targetDate,typeMismatch.targetDate,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [todo.targetDate,targetDate]; arguments []; default message [targetDate]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'targetDate'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value 'Tue Jan 05 00:00:00 SGT 2021'; nested exception is java.lang.IllegalArgumentException]]
In your jsp you submit the form to "todo", not "update-todo". It would be nice to see your ToDo bean. Also: result.getAllErrors().

HttpStatus 406 "not acceptable" when returning Flux<String> on Spring 4.3.7 MVC Controller to a EventSource in html page

Im trying to make a timer of how much time a page is open via spring webflux, i had made this example work on springboot but with spring mvc is not working, so i know the code does work. may i have to set up something im not aware of?
Controller:
#Controller
public class HomeController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(){
return "home";
}
#RequestMapping(value = "/timer", method = RequestMethod.GET, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public #ResponseBody Flux<String> startTimer(){
Instant startTime = Instant.now();
return Flux.fromStream(
Stream.generate(() -> calculateTimeMethod(startTime, Instant.now()))
).delayElements(Duration.ofSeconds(1));
}
}
JSP home.jsp :
<html>
<head>
<title> Home </title>
</head>
<body>
<label> tiempo trascurrido </label>
<label id="timer"></label>
<script>
var eventSource = new EventSource("url/timer");
eventSource.onmessage = function ( event ) {
document.getElementById("timer").innerText = event.data;
};
</script>
</body>
</html>
home page load but timer doesnt start, console output shows: Failed to load resource: the server responded with a status of 406 (No Aceptable)

Why the value in model.put is appended in URL

I am building a Spring MVC Web App. In one Controller, I used model.put method to pass some information to a jsp file.
The following is the snippet of controller
#RequestMapping(value = "/login" , method= RequestMethod.POST)
public String loginPost(HttpSession session,ModelMap model, #RequestParam(value = "username") String username, #RequestParam(value = "password") String password) throws Exception {
User user = getUser(username, password);
if(user !=null){
session.setAttribute("username",username);
model.put("message","login successful ");
return "redirect:/index";
}else{
model.put("message","login failed");
return "redirect:/login";
}
}
The index.jsp and login.jsp:
<%# page contentType="text/html; charset=UTF-8" %>
<html>
<body>
Click to upload page
</body>
</html>
<%--
Created by IntelliJ IDEA.
User: cbl
Date: 2016/1/6
Time: 15:34
To change this template use File | Settings | File Templates.
--%>
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>${message}</h1>
<form method = "POST" action = "/login">
<label for="username">Username</label>
<input id="username" type="text" name = "username">
<label for="password">Password</label>
<input id="password"type="password" name="password">
<input type="submit" value="login">
</form>
</body>
</html>
The ${message} is extected to be display within body, however, it is appended after URl:http://localhost:8000/login?message=login+failed and http://localhost:8000/index?message=login+successful++
Any response is appreciated
Since you are returning a redirect, a new GET request is created under the covers, that is why is getting attached to the url.
If you want to keep the data in the model only, you can use the flash scope with RedirectAttributes
#RequestMapping(value = "/login" , method= RequestMethod.POST)
public String loginPost(HttpSession session,
RedirectAttributes ra,
#RequestParam(value = "username") String username,
#RequestParam(value = "password") String password) throws Exception {
User user = getUser(username, password);
if(user !=null){
session.setAttribute("username",username);
ra.addFlashAttribute("message","login successful ");
return "redirect:/index";
}else{
ra.addFlashAttribute("message","login failed");
return "redirect:/login";
}
}

Handling MultipartException with Spring Boot and display error page

I have a very simple file upload set up with Spring Boot.
I was wondering if there was an easy way to display an error page when the maximum file size is exceeded.
I have uploaded a very simple example of what I'm trying to achieve on github.
Basically, the idea is to catch the MultipartException in a global Spring exception handler:
#ControllerAdvice
public class UploadExceptionHandler {
#ExceptionHandler(MultipartException.class)
public ModelAndView handleError(MultipartException exception) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("error", exception.getMessage());
modelAndView.setViewName("uploadPage");
return modelAndView;
}
}
The controller which handles the file upload is really simple:
#RequestMapping("/")
public String uploadPage() {
return "uploadPage";
}
#RequestMapping(value = "/", method = RequestMethod.POST)
public String onUpload(#RequestParam MultipartFile file) {
System.out.println(file.getOriginalFilename());
return "uploadPage";
}
And the uploadPage.html thymeleaf template associated with it too:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
<title>Upload</title>
</head>
<body>
<div style="color: red" th:text="${error}" th:if="${error}">
Error during upload
</div>
<form th:action="#{/}" method="post" enctype="multipart/form-data">
<input type="file" id="file" name="file"/>
<button type="submit" name="save">Submit</button>
</form>
</body>
</html>
The idea is to display an error message in the same upload page when the file is too big.
It was my understanding that one would configure Spring's MultipartResolver to resolve exceptions lazily and be able to catch those exceptions at Spring's level (MVC exception handlers) but this code does not seem to help:
#Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public StandardServletMultipartResolver multipartResolver() {
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
multipartResolver.setResolveLazily(true);
return multipartResolver;
}
So before I resort to extreme measures like a filter or extending the MultipartResolver...
Do you know a clean way to handle those exceptions with Spring MVC?
Answer
Thanks to #rossen-stoyanchev.
Here is what I ended up doing:
#RequestMapping("uploadError")
public ModelAndView onUploadError(HttpServletRequest request) {
ModelAndView modelAndView = new ModelAndView("uploadPage");
modelAndView.addObject("error", request.getAttribute(WebUtils.ERROR_MESSAGE_ATTRIBUTE));
return modelAndView;
}
#Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return container -> container.addErrorPages(new ErrorPage(MultipartException.class, "/uploadError"));
}
Works like a charm and feels like an elegant solution.
I updated the project on github if someone is interested.
Many thanks!
Multipart request parsing happens before a handler is selected and hence there is no #Controller and therefore no #ControllerAdvice to speak of yet. You can use an ErrorController (see http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-error-handling) for that.
BTW you don't need #RequestParam. It's enough that the argument type is MultipartFile.

Resources