Submitting a post request but remain on the page using spring mvc - html

I have a simple reset password form screen. When the user submits via the Recover button the page posts to http://localhost:8080/api/v1/accountmanagement/user/password_reset . In that endpoint the user's email is validated then a hash is generated that is concatenated into a link which is sent by the system to the user via email. Everything works fine, its just that, what I want to happen is when the user clicks the recover button, I want them to remain on the recover password page. What I plan to do is to just unhide a div saying "Thank you for submitting a recover your password request etc.". Showing the div is not a problem (might be using plain javascript -- I don't have a js framework -- maybe use jQuery), its just that when the user clicks the recover button, the post request goes to the rest api therefore changing the page that is being displayed.
I tried to add target="_blank" in the form, but it didn't work. My front end is written in html and thymeleaf only.
There might be a simple solution out there that I'm just missing.
resetPassword.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<title>Reset Password</title>
<link href="css/main.css" rel="stylesheet"/>
</head>
<body>
<div>
<div>
<h2>Recover your password</h2>
<p>To recover your password follow the instructions below to reset your password.</p>
</div>
<form th:action="#{/api/v1/accountmanagement/user/password_reset}" method="post"
id="resetPasswordForm" th:object="${account}" target="_blank">
<div id="field_email">
<label for="email">Enter your email address :</label>
<p>Enter the email address with your account.</p>
<div><input type="text" id="email" name="email" th:field="*{email}"/></div>
</div>
<div id="successRecover" style="display: none;">
An email has been sent to your email address. The email contains instructions to reset your password.
</div>
<div th:if="${param.error}" class="login-panel_error error">Error.</div>
<div>
<button type="submit" class="btn-primary">
<i class="fa fa-spin fa-spinner hidden"></i>
<span>Recover</span>
</button>
</div>
</form>
</div>
</body>
</html>
PasswordManagementController.java
#Controller
#RequestMapping("/api/v1/accountmanagement")
public class PasswordManagementController {
#Autowired
private PasswordResetService passwordResetService;
private Logger LOG = LoggerFactory.getLogger(this.getClass());
#RequestMapping(value = "/user/password_reset", method = RequestMethod.POST)
public ResponseEntity processResetPassword(#ModelAttribute(value = "account") Account account,
BindingResult result, HttpServletRequest request) {
LOG.info("account email " + account.getEmail());
// build the hash here for hashUrl
if (account != null) {
if (!StringUtil.isBlank(account.getEmail())) {
try {
passwordResetService.processResetPassword(account, hashUrl);
} catch (AccountConfigException ace) {
// deal with exception here.
// return
} catch (Exception e) {
// deal with exception here.
// return
}
return new ResponseEntity(HttpStatus.OK);
} else {
// deal with an exception here.
// return
}
} else {
// deal with an exception here.
// return
}
}
}

If you're returning a ResponseEntity in the case of a successful request you will indeed be redirected.
I don't know how you handle errors but what you might want to do is changing the return type of your controller method to String. This way you can redirect to the same page in case of a successful response while passing a boolean value to the model that will decide to render your successRecover div, without needing any Javascript.
The new handler:
#RequestMapping(value = "/user/password_reset", method = RequestMethod.POST)
public String processResetPassword(#ModelAttribute(value = "account") Account account, Model model) {
// build the hash here for hashUrl
try {
passwordResetService.processResetPassword(account, hashUrl);
} catch (AccountConfigException ace) {
// deal with exception here.
// return
} catch (Exception e) {
// deal with exception here.
// return
}
model.addAttribute("isOK", true);
return "index";
}
And then in the view:
<div id="successRecover" th:if="${isOK}">
An email has been sent to your email address. The email contains instructions to reset your password.
</div>

Related

Thymleaf how to take an input and then redirect to another page

I'm learning Spring boot. I have a list of products with unique ids, and I want to implement a "lookup by id" functionality, but I don't know how to do it, I searched but got totally different stuff.
I already have a #Getmapping method like this:
#Getmapping(/products/{id})
If I manually type in the id in the url I'll get what I what. But I want to have an input box in the HTML page like:
<form>
Look up by id: <input/>
</form>
and after I submit the form it'll redirect to that page. For example, if I enter input of 1, it'll go to localhost:8080/products/1
I've been searching but all I got was stuff about #Postmapping.
Add a #PostMapping to your controller:
#Controller
#RequestMapping("/products")
public class ProductController {
#GetMapping //Controller method for showing the empty form
public String index(Model model) {
model.addAttribute("formData", new SearchFormData()); // Create an empty form data object so Thymeleaf can bind to it
return "index";
}
#PostMapping
public String searchById(SearchFormData formData) {
return "redirect:/products/" + formData.getId(); //Use the value the user entered in the form to do the redirect
}
#GetMapping("/{id}")
public String showProduct(#PathVariable("id") long id) {
...
}
}
With SearchFormData representing the form fields (there is only 1 field in this case):
public class SearchFormData {
private long id;
// getters and setters
And update Thymeleaf template like this:
<form th:action="#{/products}" th:method="post" th:object="${formData}">
<input th:field="*{id}" type="number">
<button type="submit">Search</button>
</form>
Note that the value of th:object needs to match with the name used to add the SearchFormData instance to the model.
See Form handling with Thymeleaf for more info.
The following simple code will direct you to a URL that is generated from a concatenation of the base address of the <form>'s action attribute and the value of its first <input>:
document.querySelector("form").addEventListener("submit",function(ev){
ev.preventDefault();
this.action="/product/"+this.querySelector("input").value;
console.log(this.action);
// in real code: uncomment next line!
// this.submit()
})
<form>
Look up by id: <input type="text" value="123" />
</form>
In the real code you will delete the console.log() und uncomment the following line: this.submit().
Alternatively you could also do:
document.querySelector("form").addEventListener("submit",function(ev){
ev.preventDefault();
location = "/product/"+this.querySelector("input").value;
})
This will redirect you to the page without actually submitting the form.

.net mvc html help ActionLink and parameters to controller class

so simply want to make a button that will call the controller action passing a parameter...
have everything I believe in place but unable to configure/reference the parameter in the actionlink helper...
Yes I will be refactoring my button onclick once I get through this html helper setup...
<h1 style="font-size:30px">Enter The Core-i Product Key (format RST 102A08R EPCA 00007)</h1>
<form action="/action_page.php">
<label for="productKey">Product Key:</label>
<input type="text" id="productKey" name="productKey"><br><br>
</form>
<p>Click the "Get Key" button and a trial key will be generated custom to your IBMi hardware".</p>
<p>
#Html.ActionLink(
"Get Key",
"GetTrialKey", // controller action
"HomeController", // controller
new { productKey }, // IT DOES NOT LIKE PRODUCTKEY (REFERENCED ABOVE)
new { #class = "btn btn-info" }) // html attributes
</p>
<div class="display-2">
<a button class="text-center, btn btn-info form-control text-white" typeof="button" onclick="location.href='#Url.Action("GetTrialKey(productKey)")'">Get Key</button></a>
<p>
<br />
</p>
</div>
refactored to...
view...
<form action="HomeController/getTrialKey" method="POST">
<label for="productKey">Product Key:</label>
<input type="text" name="productKey" maxlength="22" value="xxx xxxxxxx xxxx xxxxx"><br><br>
<input type="submit" value="Get Trial Key" class="btn btn-primary" />
</form>
controller...
[HttpPost]
public async Task<IActionResult> getTrialKey(string productKey)
{
when I run it i get...
This localhost page can’t be foundNo webpage was found for the web address: https://localhost:44346/HomeController/getTrialKey
Referring back to one of the comments:
I didn't discourage you from using HTML Helpers. I just meant the way you constructed the form and you used ActionLink was wrong. And it would be easier to just have an input for the product key inside the form, if that's the only thing you want to post back to the server.
And I would highly recommend you to read through documentations from Microsoft, at least this one: https://dotnet.microsoft.com/apps/aspnet/mvc to understand what a MVC is. From your code sample, I didn't see you used M - Model at all.
Anyway, if you just only want to get the product key the user types in, I would do it like this:
Define a Controller
I dislike the idea of putting everything under /home (i.e., HomeController). Just think about the URL to the page that would make sense to the user.
Now I am guessing what you are trying to do. I saw terms like product keys and trial keys. What about a controller called ProductKeyController:
public class ProductKeyController : Controller
{
// This corresponds to /productkeys, and you can list all the product keys
// on the view it returns.
public ActionResult Index()
{
return View();
}
// This corresponds to /productkeys/create, and you can create a specific product
// key by asking the user to provide a trial key?
// The view this returns might be the page where you build the form
public ActionResult Create()
{
...
return View();
}
// This corresponds the form post.
[HttpPost]
public ActionResult Create(CreateProductKeyViewModel model)
{
...
return View(model);
}
}
The view model
Your MVC controller is responsible to fetch data, if needed, build a view model, and pass it to the view. When you create a product key, if you need to ask the user to enter anything, you can declare a model and properties within it:
public class CreateProductKeyViewModel
{
[Required]
[Display(Name = "Trial Key")]
[MaxLength(22)]
public string TrialKey { get; set; }
}
The View Create.cshtml
Since you know the controller will be passing the view model to the view, you can declare it on top of the view so that everything you do with the view model inside the view is strongly-typed.
#model CreateProductViewModel
#{
Layout = "xxx";
}
<h1>Enter The Core-i Product Key (format RST 102A08R EPCA 00007)</h1>
#using(Html.BeginForm("create", "productKey", new { area = "" }, FormMethod.Post))
{
#Html.LabelFor(x => x.TrialKey)
#Html.TextBoxFor(x => x.TrialKey)
<button type="submit">Create</button>
}
See how everything within the view is strongly-typed? You don't have to manually create the form and the input for asking user for the trial key.
And when the user enters the trial key and presses submit, it will post back to the Post method of Create. Since the view is declared with the view model, and the view model is the parameter of the create method, MVC has already done the model binding for you hence you will get what user entered on the post back.
This is at least something to get you started.
Note: I wrote everything by hand. Not tested.

how the values transfer between thymeleaf and spring controller, viceversa

I am new to thymeleaf... Can someone please tell how values is passed between thymeleaf html and spring controllers... Please suggest good tutorials for thymeleaf-spring-mvc...
In the below example, please let me know how the user entered value of owner in text field is passed to spring controller so that it validates and returns the results. And viceversa how the results returned by controller, is taken by thymeleaf to display the results.. How the value of LASTNAME is known to controller.. how it get passed to owner object of controller owner.getLastName()..
Find Owners
<form th:object="${owner}" action="ownersList.html" th:action="#{'/owners.html'}" method="get" class="form-horizontal"
id="search-owner-form">
<fieldset>
<div class="control-group" id="lastName">
<label class="control-label">Last name </label>
<input type="text" th:field="*{lastName}" size="30" maxlength="80"/>
<span class="help-inline" th:errors="*{lastName}">[Errors]</span>
</div>
<div class="form-actions">
<button type="submit">Find Owner</button>
</div>
</fieldset>
</form>
#RequestMapping(value = "/owners", method = RequestMethod.GET)
public String processFindForm(Owner owner, BindingResult result, Model model) {
// allow parameterless GET request for /owners to return all records
if (owner.getLastName() == null) {
owner.setLastName(""); // empty string signifies broadest possible search
}
// find owners by last name
Collection<Owner> results = this.clinicService.findOwnerByLastName(owner.getLastName());
if (results.size() < 1) {
// no owners found
result.rejectValue("lastName", "notFound", "not found");
return "owners/findOwners";
}
if (results.size() > 1) {
// multiple owners found
model.addAttribute("selections", results);
return "owners/ownersList";
} else {
// 1 owner found
owner = results.iterator().next();
return "redirect:/owners/" + owner.getId();
}
}
Suppose you have a controller method like:
void method-name(Owner owner){
// ...
}
When the submit button is clicked the values is automatically set to domain class, before that create a new method and set model attribute of object
model.addAttribute("owner",new Owner);

Servlet + Jsp Issue

I have a JSP page, from where onClick I want to execute a servlet page. Can anybody help me, how can it possible ?
<input type="submit" value="Send" id="click" name="click"/>
if(request.getParameter("click")!=null)
{
request.sendRedirect("URL");
}
or
public class Dispatcher extends HttpServlet
{
public void doGet(HttpServletRequest request, HttpServletResponse response)
{
RequestDispatcher dispatcher =
request.getRequestDispatcher("URL");
if (dispatcher != null) dispatcher.forward(request, response);
}
}
Google search for the difference between request.sendRedirect("URL"); and request.getRequestDispatcher("URL");
You could use an AJAX call to invoke the servlet if you don't want to reload the current page or perform a redirect using the window.location.href property. If you want to send some values to the server you could send them in the AJAX request body or as query string parameters if you decide to redirect.
<form method=GET action="servlet/nextPage">
some text here
<input type=submit>
</form>
You need a form with single button, and send form to the servlet
or you can use simple some text to be clicked or link under image
your Servlet must be described in web.xml or by annotation to let it be called.

Webmatrix: passing ModelState to partial page

I am building a WebPages site and have an issue when I try to pass ModelState data to a partial page.
Here is the code for Create.cshtml:
#{
Page.Title = "Create Fund";
var name = "";
if (IsPost) {
name = Request.Form["name"];
if (!name.IsValidStringLength(2, 128)) {
ModelState.AddError("name", "Name must be between 2 and 128 characters long.");
}
}
}
#RenderPage("_fundForm.cshtml", name)
Here is the code for _fundForm.cshtml:
#{
string name = PageData[0];
}
<form method="post" action="" id="subForm">
<fieldset>
#Html.ValidationSummary(true)
<legend>Fund</legend>
<p>
#Html.Label("Name:", "name")
#Html.TextBox("name", name)
#Html.ValidationMessage("name", new { #class = "validation-error" })
</p>
<p>
<input type="submit" name="submit" value="Save" />
</p>
</fieldset>
</form>
The issue I am having is when there is an error for "name", the validation error does not display. Is there a special way to pass ModelState between the two pages?
_fundForm is going to be shared between Create.cshtml and Edit.cshtml.
ModelState is a readonly property of System.Web.WebPages.WebPage class. Its backing field is a private ModelStateDictionary and is initialized at first access. I can't see any way to force ModelState across pages, apart from doing it via reflection as seen in SO question: Can I change a private readonly field in C# using reflection?
Otherwise, you can simply use a third parameter in the invocation, like this:
#RenderPage("_fundForm.cshtml", name, ModelState);
In effect, the first parameter after the page name will become the Model of the new page, so there is enough space (i.e. the next parameter) to pass the ModelState.
In your "_fundForm.cshtml" merge the ModelState received by the calling page with the local one, like this:
#{
//In _fundForm.cshtml
var ms = PageData[1];
ModelState.Merge((ModelStateDictionary)ms);
}