Java Spring MVC PRG Pattern preserve Data on reload - java

i'm currently woking on a spring mvc project. I have a page with a form, which represents a configurator.
The user can choose some data in a bunch of select fields and proceeds to the next step, where he gets the same jsp-page but with some more fields, depending on his inputs he made. This will be repeated a few times until he gets his result on another page. Each time a POST will be performed.
Now if the user uses the back function of the Browser he doesn't get to the previous page, but to a browser default "broken page", where Chrome for example says something like "Please confirm resubmission of the form data...". To actually resubmit the data he has to press reload and confirm a popup.
The resubmission itself isn't really a problem, because the data does not get inconsistent, it just performs another call to the backend and receives the data it provides.
The real no-go is the fact that the user has to manually refresh the page and by chance gets confused by the default browser page.
I did some research and found out, that the PRG (Post-Redirect-Get) Pattern might solve this problem.
In fact i can now navigate through the browser or reload the page and does not get the popup or broken page - because it's now a GET request of course.
The problem now is, that if i navigate back, the last page does not contain the data it contained before, but is now empty because no data at all is existing.
I understand that it is now a GET request and no data gets posted, but i thought the previous page would be "reused", like shown here.
Now with the PRG-Pattern the handling of the application is even worse, because if the user reloads or navigates back, he basically has to start from scratch.
Did i misunderstood the meaning of this Pattern?
A quick look into some code, how i implemented this:
#PostMapping("/config")
public String handlePostRequestConfig(RedirectAttributes redirectAttributes, ProductForm productForm){
//Handle productForm and add additional content to it
if(noMoreStepsLeft){
return "redirect:/result";
}
redirectAttributes.addFlashAttribute("form", productForm);
return "redirect:/config";
}
#GetMapping("/config")
public String handleGetRequestConfig(Model model, #ModelAttribute("form") ProductForm productForm{
model.addAttribute("form", productForm);
return getJsp("product");
}
Inside JSP:
<form method="post" action="/config">
<c:foreach items="${form.selectFields}" var="selectField">
<input...>
</c:foreach>
<button type="submit">Submit</button>
</form>

In PRG, P is not the first step of user action flow. PRG is a part of the full flow.
The following shows a flow and how PRG fits in it:
User will hit a URL. For example: http://localhost:8080/myContextPath/config.
This will be handled using a GET handler:
#GetMapping("/config")
public String show(ModelMap model) {
// code
model.put("form", productForm);
return "product"; // returning view name which will be resolved by a view resolver
}
product.jsp:
<form commandName="form" method="post">
<c:foreach items="${form.selectFields}" var="selectField">
<input...>
</c:foreach>
<input type="submit" value="Submit"/>
</form>
This submit action will be handled by a POST handler:
#PostMapping("/config")
public String submit(#ModelAttribute("form") ProductForm productForm,
RedirectAttributes redirectAttributes){
// code, may be service calls, db actions etc
return "redirect:/config";
}
This redirect to /config will be handled again by /config GET handler. (Or you can redirect to any GET handler of course)

Related

Stripe Checkout Charge using JSF: Form being submitted twice

The form is being submitted twice:
1. On Page Load
2. When user clicks on Checkout's button
I want to avoid the first submission, it is throwing an error because the token returned is null:
com.stripe.exception.InvalidRequestException: Invalid source object: must be a dictionary or a non-empty string. See API docs at https://stripe.com/docs'; request-id: req_DjRbT4rGULYGnB
Following the documentation I added the following code to my XHTML:
<div>
<form submit="#{studentBean.chargeStudent()}" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="pk_test_xxxxxx"
data-amount="111"
data-name="myApp"
data-description="Example charge"
data-zip-code="true"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-locale="auto">
</script>
</form>
</div>
Here is my Managed Bean's function:
#Named
#ViewScoped
public class StudentBean implements Serializable {
#EJB
StripeChargeLogic stripeChargeLogic;
public void chargeStudent(){
Map<String,String> requestParams = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
logger.info("charge:" + requestParams.get("stripeToken"));
stripeChargeLogic.chargeStudent(requestParams.get("stripeToken"));
}
}
Can someone please guide me why the form is being submitted twice and how I can prevent the submission during page load Thank you!
You are not doing jsf here, you have plain html, most likely (but mot clear from you post) in an xhtml/facelets file but not JSF.
In your form action you have an EL that, since it all is in no way related to jsf, is called on page load, sort of like what hapens here
The rest of the behaviour is even more 'undefined' because of this. Take a step back and learn the basics of web technology and jsf and then look at your problem again

Prevent Form Action From Changing URL After File Upload

I have a simple form in my Spring MVC project that handles a file upload.
form.jsp
<form method="POST" enctype="multipart/form-data" action="/upload" id="form">
<input id="file" type="file" name="file"><br />
<input type="submit" value="Upload">
<input type="reset" value="Reset">
</form>
Because of the form's action however, I am redirected to localhost:8080/myApp/upload rather than localhost:8080/myApp/form as specified in my #Controller
#RequestMapping(method = RequestMethod.POST, value = "${context}/upload", headers = "content-type=multipart/*")
public String uploadFile(#RequestParam("file") MultipartFile file, Model model) {
//do some upload stuff
model.addAttribute("uploadSuccess", "Upload Successful.");
return "/form";
}
Is it possible to prevent only the form action's redirection, but still retain the value ${context}/upload so that my Controller method gets called? I need my page to land back on myApp/form because this particualr url makes a call to a separate Controller method that retrieves some data
Note that I don't wish to rename my jsp file to 'upload.jsp' and I don't want to make an AJAX Post (Thanks for nothing IE9). Any thoughts mates?
Found the answer for my exact case on 17.5.3 Redirecting to views from Spring's Docs
It is sometimes desirable to issue an HTTP redirect back to the
client, before the view is rendered. This is desirable, for example,
when one controller has been called with POST data, and the response
is actually a delegation to another controller (for example on a
successful form submission). In this case, a normal internal forward
will mean that the other controller will also see the same POST data,
which is potentially problematic if it can confuse it with other
expected data.
This can be done with the following:
return "redirect:/form";
A nice benefit to the redirect is that it prevents the user from accidentally re-POSTing the same data by performing a refresh. The refresh forces a GET of the result page, not a resend of the initial POST data.
UPDATE:
The default redirect:/view will append your model attributes to your URL as query params. This may be undesirable in some cases as it could expose sensitive information to the user. Utilize RedirectAttributes addFlashAttribute(String attributeName, Object attributeValue) method to in order to store Flash Attributes that will be used to store attributes when redirected from one URL to another (Success/Failure messages, etc.)

Collect user input in Servlet and return at the same point to continue program: Java

I have a web application in Java that performs title matching.
The Servlet is the controller and in one of the methods of the Servlet, I am comparing two list of titles. The first list is in a HashMap and the second is from a query ResultSet.
What I want to do is to automatically match those with same title and give the user the option to confirm the ones with some similarities (business logic). Basically, I need to get user input and then return at the same point to continue.
I tried JOptionPane dialog box and it didn't work.
Now I am trying to forward to another HTML page to get user input and then return to the Servlet.
Below is the Servlet code:
while (Querylist.next()) {
String title = Querylist.getString(1).trim().toLowerCase();
if (MyMap.containsKey(title))
{
// confirm match
} else
{
//some title2 is like title
request.setAttribute("Title1", title);
request.setAttribute("Title2", title2);
RequestDispatcher view = request.getRequestDispatcher("TitleMatch.jsp");
view.forward(request, response);
ResultMatch= request.getParameter("ResultMatch");
if (ResultMatch.equals("YES"))
{
// confirm match
}
}
}
HTML Page:
<B> <%= request.getAttribute("Title1")%></B>
<B> <%= request.getAttribute("Title2")%></B>
<FORM method="get" action="DataMerge">
<input type = "radio" name="MatchResult" value="YES" /> YES
<input type = "radio" name="MatchResult" value="NO" checked/>NO
<button type = "submit" formaction="DataMerge" > <b>CONFIRM</b>
</FORM>
EDIT: the loop works and I'm having a java.lang.IllegalStateException Exception.
Does anyone can help to figure out how to do that efficiently in plain Java?
I searched all over SO and haven't found something similar. Thanks in advance.
You might want to reconsider your approach as there are number of fundamental problems with the code you have written. For example:
The while loop test it not correct. Assuming that you are using an Iterator then the test should be list.hasNext();
The if test is nested and incorrect. You cannot use the identifier Map as it is the name of the class, you should use the name of the map object.
If the loop worked the view.forward(request, response); would result in an java.lang.IllegalStateException exception, on the second cycle, as its not possible to resend a response.
I suggest that instead of trying to send each title pair one at a time, that you display them all (or some if there are too many) on one JSP with a yes button next to each pair and as the user clicks the yes button an AJAX call is made to another servlet that updates the database (or an array to latter be used to update the database).
There are some good tutorial about using AJAX and JSP here of SOF and in YouTube.

Struts 2: Sending values of form fields from jsp to action class

I know it must be simple, but still I am not able to figure it out.
I have a link on a jsp page.
When this link is clicked I want another tab (of browser) to open up.
The view of this new page is defined by action class, which needs a form field value.
The problem I am facing is that I can not get the values of form fields to the action class without submitting the form. And submitting the form changes the view of the original jsp as well, which defeats the whole purpose of opening a new tab.
So I need a way to get the form field values to action class without resetting the view of original jsp page.
One way I came across was URL re-writing but that would be my last option.
Please suggest something!!
Thanks!!
Firstly I would like to point out that currently possible (to my knowledge anyway) to force a new tab to appear, it is dependent on the users' browser and the settings that they have see here for more infomation.
Now onto your question, since links cannot send form data (on their own) you have 2 options:
You can use a form "submit" button pointing to the URL you want to send the data to and to and add the target="_blank" to the form which will cause a new page to open up when the form is submitted.
You can add a javascript event to your link so that when it is pressed you append the value of the input to the URL and open a new window with that URL.
Personally I would choose the first option.
Here is a simple example of option one which doesn't remove the input value when you submit...
<html>
<body>
<form action="test1.html" method="post" target="_blank">
<input type="text" name="bob" />
<input type="submit" value="Hello"/>
</form>
</body>
</html>
You could do an ajax call, or dynamically build the link url with get parameters in it.

How do I access the ActionBeanContext within a JSP?

I'm new to Stripes and appreciate every hint that brings me nearer to a functioning web-app!
technological setup: java, dynamic web project, stripes, jsp
scenario:
users can login (index.jsp). After correct email-adress and password (LoginFormActionBean.java), the user is forwarded to a welcoming page (loggedin.jsp).
The content on this welcoming page is something like "welcome < username >, you've been successfully logged in!".
implementation:
i have a form in the index.jsp where i take the user input and pass it to a method in the LoginFormActionBean.java --> works!
in the corresponding method i check whether the user is correct and if so, i insert the user in the ActionBeanContext:
getContext.setUser(loggedinUser);
after that i forward to the loggedin.jsp:
return new ForwardResolution("/loggedin.jsp");
the loggedin.jsp contains following important lines:
<jsp:useBean id="loggedinBean" class="mywebapp.controller.LoggedinBean" scope="session" />
...
${loggedinBean.context.user} //show the whole user object
...
<s:form beanclass="mywebapp.controller.LoggedinBean" name="ButtonForm">
<s:submit name="foo" value="PrintUser" />
</s:form>
<s:form beanclass="mywebapp.controller.LoggedinBean" name="TextForm">
<s:text name="user" />
</s:form>
...
the LoggedinBean.java contains a MyActionBeanContext attribute (like the LoginFormActionBean.java).
to get the userobject out of the context i use:
public String getUser(){
return getContext().getUser().toString();
}
furthermore the LoggedinBean.java contains a method, which is annotated with #DefaultHandler and forwards to loggedin.jsp (the same page)
result:
now, what happens is: after logging in correctly, i'm forwarded to the loggedin.jsp,
the line "${loggedinBean.context.user}" is empty and so is the < s:text >-field.
BUT after clicking the "PrintUser" Button, the < s:text >-field in the "TextForm"-form is filled with the user object of the logged in user!
conclusion:
what i think happens, is that the "setContext()" method of the LoggedinBean.java is not called before i manually execute a method in the bean. Because the "setContext()" method in the bean is not called before i press the button!
the online documentation says to use a context attribute in a JSP just write "${actionBean.context.user}". But the context is null!
even the book "pragmatic stripes"(2008) gives no more information about using the ActionBeanContext.
question:
what happens there?
how can i get the "${loggedinBean.context.user}" line to display the logged in user at all?
and how can i get the < s:text >-field to display the user object after loading the JSP, but without pressing the button?
i hope my problem is clear and my remarks are satisfying
I would like to recommend the usage of the MVC pattern. This pattern will lead to an implementation were the Action Beans will act as controllers that handle all http requests and the JSP pages will become passive views with little logic, only accessible via the Action Bean controllers (no direct access to JSP pages any more!).
If you use this pattern, you always have an "actionBean" available in your JPS and thus you can refer to ${actionBean.context} (see: getContext).

Categories

Resources