Request parameter with thymeleaf - spring

In a Spring Boot web app, User wants to reset his password, so he enters Reset password page. Now I want to let him type his email address, push Reset and I want to redirect to myapp/resetPassword?email=HIS_EMAIL to handle password reset.
MY code:
#RequestMapping(value = "/resetPassword", method = RequestMethod.GET)
public String resetPasswordForm(Model model){
model.addAttribute("email", new String());
return "resetPassword";
}
#RequestMapping(value = "/resetPassword", method = RequestMethod.POST)
public String resetPassword(#RequestParam("email") String email) {
User user = userService.findUserByEmail(email);
//playing with logic
return "redirect:/";
}
How can I achieve it on my thymeleaf page? I tried something like this:
<form th:action="#{/resetPassword(email=${email})}" method="post">
<input type="email" th:field="${email}" th:placeholder="Email" />
<div class="clearfix">
<button type="submit">Reset</button>
</div>
</form>
Unfortunately my email is always null. Can somebody help?

"th:field" is for Entity-Beans only. This should work:
#GetMapping(value = "/resetPassword")
public String resetPassword(#RequestParam(value="email",required=false) String email) {
if(email==null)
return "resetPassword";
User user = userService.findUserByEmail(email);
//playing with logic
return "redirect:/";
}
<form th:action="#{/resetPassword}" method="get">
<input type="email" th:name="email" th:placeholder="Email" />
<div class="clearfix">
<button type="submit">Reset</button>
</div>
</form>
And don't forget: Thymeleaf is not Javascript. It is rendered on Server. Things like #{/resetPassword(email=${email})} would output e.g. /resetPassword?email=anValueYouAddedToModelInController

Related

I'm getting error for user field in registration form

java.lang.IllegalStateException: Neither BindingResult nor plain
target object for bean name 'user' available as request attribute at
org.springframework.web.servlet.support.BindStatus.(BindStatus.java:153)
~[spring-webmvc-5.3.13.jar:5.3.13] at
org.springframework.web.servlet.support.RequestContext.getBindStatus(RequestContext.java:903)
~[spring-webmvc-5.3.13.jar:5.3.13] at
org.thymeleaf.spring5.context.webmvc.SpringWebMvcThymeleafRequestContext.getBindStatus(SpringWebMvcThymeleafRequestContext.java:227)
~[thymeleaf-spring5-3.0.12.RELEASE.jar:3.0.12.RELEASE]
HTML
<form action="#" th:action="#{/process_register}" th:object="${user}" method="post">
<h4 class="mb-4 pb-3">Sign Up</h4>
<div class="form-group">Username
<input type="text" th:field="*{username}" class="form-style">
</div>
<div class="form-group mt-2">Password
<input type="password" th:field="*{password}" class="form-style">
</div>
...</form>
Controller:
#GetMapping("/register")
public String showRegistrationForm(Model model) {
model.addAttribute("user", new User());
return "signup_form";
}
#PostMapping("/process_register")
public String processRegister(Model theModel, User user) {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(encodedPassword);
userService.save(user);
return "Wellcome";
}
Most likely your forgot to add user object to Model in a controller method that landed you on this form page. To make sure that doesn't happen best is to just add model attributing method to controller that always takes care of this:
#ModelAttribute("user")
public User user() {
return new User();
}
Also please make sure User class have getters, setters and no arg constructor defined - that is important for Thymeleaf

Is greeting view and the result view pointing to the same object?

Here our controller may return one of two views.
In such a case where these 2 method signatures both contained Model Model Map and ModelAttribute, do the views share access to the Model and ModelAttribute loaded by a previous request handle?
#Controller
public class GreetingController {
#GetMapping("/greeting")
public String greetingForm(Model model) {
model.addAttribute("greeting", new Greeting());
return "greeting";
}
#PostMapping("/greeting")
public String greetingSubmit(#ModelAttribute Greeting greeting) {
return "result";
}
}
It does not point the same object.
I am assuming you are using https://spring.io/guides/gs/handling-form-submission/
and its code naming convention is quite confusing.
Please see the following test code.
I changed URL, variable name purposely.
Greeting.java
public class Greeting {
private long id;
private String content;
//... getters and setters
}
Greeting2.java
//created for testing
public class Greeting2 {
private long id;
private String content;
//... getters and setters
}
GreetingController.java
#Controller
public class GreetingController {
#GetMapping("/greeting") // greeting URL and GET request method
public String greetingForm(Model model) {
// th:object="${foo}" in template and thymeleaf
model.addAttribute("foo", new Greeting());
return "greeting_tmpl"; // src/main/resources/templates/greeting_tmpl.html
}
#PostMapping("/greeting_post")
public String greetingSubmit(#ModelAttribute Greeting2 bar) {
//I expected using bar variable in result_tmpl, but it used Greeting2(lowercase) as variable
return "result_tmpl"; // src/main/resources/templates/result_tmpl.html
}
}
src/main/resources/templates/greeting_tmpl.html
...
<body>
<h1>Form</h1>
<form action="#" th:action="#{/greeting_post}" th:object="${foo}" 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>
</form>
</body>
</html>
src/main/resources/templates/result_tmpl.html
...
<body>
<h1>Result</h1>
<p th:text="'id: ' + ${greeting2.id}" /> <!-- this name should be like bar.id not greeting2 -->
<p th:text="'content: ' + ${greeting2.content}" />
Submit another message
</body>
</html>
Simply,
Browser triggers #GetMapping.
Server Parses Greeting model to HTML form values in greeting template and response to the browser.
Submit Form Data using POST method triggers #PostMapping.
#ModelAttribute Greeting2(Can be any model which can parse form values(in this case, id,content) will parse form values to Greeting2 model.
Server Parses Greeting2 model to HTML form values in greeting template and response to the browser.

Spring boot SecurityContextHolder: principal is changing

I have encountered an issue with the SecurityContextHolder.
Given an Thymeleaf Template where userdetails can be changed and submitted. As admin I am entering the /edit Controller. So far everything is fine:
#RequestMapping(value="/user/{username}/edit", method= RequestMethod.GET)
public ModelAndView edit(#PathVariable String username, ModelMap model) {
User user = userService.findUser(username);
User loggedInUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
....
}
In the loggedInUser variable I see the admin user. That works as expected:
When the form is submitted. I check the pricipal object again. Now it suddenly changed to the user I am currently editing.
Form snippet
<form class="form-horizontal" role="form" th:action="#{/users/save}" method="post" th:object="${user}">
<input type="hidden" id="old_username" name="old_username" th:value="${user.username}" required>
<input type="text" class="form-control" id="username" th:field="${user.username}" required>
....
</form
Receiving POST Controller:
#RequestMapping(value="/users/save", method= RequestMethod.POST)
public String save(User user, HttpServletRequest request, RedirectAttributes redir) {
User loggedInUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
....
Here the loggedInUser changed to the user being edited:
What is going on?

Request method 'GET' not supported Spring Booth with Thymeleaf

I am building a very simple crud app using Spring boot, Jpa and Thymeleaf, but I am stuck at a "Request method 'GET' not supported" problem. I get this error whenever I want to access the /add page through which I can add a new student. The snippets associated with this error are as below:
Thymeleaf form:
<h1>Form</h1>
<form action="#" th:action="#{/add}" th:object="${addStudent}"
method="post">
<p>Full name: <input type="text" th:field="*{fname}" /></p>
<p>Major: <input type="text" th:field="*{major}" /></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
Controller addNewStudentMethod
#PostMapping("/add")
public String addNewStudent( #ModelAttribute StudentEntity studentEntity, Model model) {
model.addAttribute("addStudent",studentRepository.save(studentEntity) );
return "/allstudents";
}
The error I get:
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'GET' not supported
Thanks,
In your controller you only have a method which has mapped to POST request "/add". You have to have a GET request mapped to a different method OR change the #PostMapping("/add") to #RequestMapping("/add").
Please note:
#PostMapping is for mapping POST request only.
#GetMapping is for mapping GET request only.
#RequestMapping maps all request types
You have some issues with how you have it set up. What you may want is:
#GetMapping("/add")
public String addNewStudent(Model model) {
model.addAttribute("studentEntity", new StudentEntity()); //create a new bean so that your form can bind the input fields to it
return "add"; //let's say add.html this is the name of your form
}
#PostMapping("/add")
public String addNewStudent( #ModelAttribute StudentEntity studentEntity, Model model) {
//call any service methods to do any processing here
studentRepository.save(studentEntity);
return "redirect:/allstudents"; //this would be your confirmation page
}
Your add.html form would have something like:
<form th:object="${studentEntity}" th:action="#{/add}" method="post" action="allstudents.html">
<!-- input fields here --->
</form>
Note that the th:object is what you added to the model in the #GetMapping method.
change your Controller methods's #PostMapping("/any-url") to either #GetMapping("/any-url") or #RequestMapping("/any-url")
In simple words, change your above controller's method to
#RequestMapping("/add")
public String addNewStudent( #ModelAttribute StudentEntity studentEntity, Model model) {
model.addAttribute("addStudent",studentRepository.save(studentEntity) );
return "/allstudents";
}

#NumberFormat Annotation not working

Trying to show the currency symbol in JSP but I don't see it. Did my research and I just don`t know what more should I add to get it working. This is what I have.
<mvc:annotation-driven />
Controller
#NumberFormat(style = Style.CURRENCY)
private Double value = 50.00;
#ModelAttribute("value")
#NumberFormat(style = Style.CURRENCY)
public Double getValue() {
return value;
}
#RequestMapping(method = RequestMethod.GET)
public ModelAndView loadForm(#ModelAttribute("user") User user) {
ModelAndView instance
modelAndView.addObject("value", 100.00);
return modelAndView;
}
JSP
<spring:bind path="value">
<input type="text" name="${value}" value="${value}"/>
</spring:bind>
<spring:bind path="value">
${value}
</spring:bind>
Output
<input type="text" name="value" value="100.0"/>
100.0
Try using the string literal value for the name attribute instead of resolving it with EL
<spring:bind path="value">
<input type="text" name="value" value="${value}"/>
</spring:bind>
Also, move the field value into a new object. Currently I do not believe the code is using the field in the controller or the getter in the controller.
public class MyForm(){
#NumberFormat(style = Style.CURRENCY)
private Double value = 50.00;
#ModelAttribute("value")
#NumberFormat(style = Style.CURRENCY)
public Double getValue() {
return value;
}
}
Then add the object to the model in the controller:
#RequestMapping(method = RequestMethod.GET)
public ModelAndView loadForm(#ModelAttribute("user") User user) {
ModelAndView instance
modelAndView.addObject("myForm", new MyForm());
return modelAndView;
}
Then access via the jsp:
<spring:bind path="myForm.value">
<input type="text" name="${status.expression}" value="${status.value}"/>
</spring:bind>
<spring:bind path="myForm.value">
${status.value}
</spring:bind>
The major issue at the moment with the code is that it is not using the field/accessor, it is simply placing a value in the model, which does not use any of the annotated fields/methods.
References:
http://www.captaindebug.com/2011/08/using-spring-3-numberformat-annotation.html#.UOAO_3fghvA
How is the Spring MVC spring:bind tag working and what are the meanings of status.expression and status.value?

Resources