Prevent Form Action From Changing URL After File Upload - java

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.)

Related

Java Spring MVC PRG Pattern preserve Data on reload

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)

How to Map Servlet Actions to JSP Path with Annotations?

I have a simple Maven servlet/jsp application that I deploy to a local Tomcat 9 (via Eclipse). JSP pages are stored under root folder (src\main\webapp\*.jsp) which when Maven installs a WAR, they go under the root folder (MyAppContext\*.jsp along side MyAppContext\META-INF\ and MyAppContext\WEB-INF\).
The servlets' URL patterns are annotated for each servlet, e.g. /doactionone, /doactiontwo, etc. Most servlets perform the dispatching to various JSP pages, but I do have a direct anchor link on one.
I wanted to move these JSP pages into their own respective directory, so I moved them to src\main\webapp\jsp\*.jsp folder, and when the Maven install is run, they get placed under MyAppContext\jsp\.
The only entry I have in web.xml is a welcome file that after relocating the JSP files, it points to jsp\doactionone.jsp which loads that corresponding JSP page. This page contains a simple form:
<form action="doactionone" method="post">
...
<a href="jsp/doactiontwo.jsp">
<input type="submit" />...
</form>
The submission on this page actually calls the right servlet (the one defined with doactionone URL pattern). I also have a link that takes the user to the second page (doactiontwo.jsp).
However, when I navigate to that second page via this link, which has another simple form (see below), and perform the submission (post), I see in browser's debugging that the URL request is http://localhost:8080/MyAppContext/jsp/doactiontwo which, for obvious reason, would return a 404 status (and get no hit to this servlet's doPost() (or doGet()) methods either).
<form action="doactiontwo" method="post">
...
<input type="submit" />...
</form>
If I try to modify the second servlet's URL pattern to /jsp/doactiontwo, I can hit the servlet, but when doactiontwo actually dispatches/forwards the request after processing to the first servlet (doactionone) with:
RequestDispatcher rd = request.getRequestDispatcher("doactionone.jsp");
rd.forward(request, response);
when it gets loaded, when hover over the URL on the first page that initially was pointing to the second JSP page (<a href="jsp/doactiontwo.jsp">), now actually points to:
jsp/jsp/doactiontwo.jsp
The funny part is that the source code of doactionone.jsp still shows it as jsp/doactiontwo.jsp, but hovering over it shows http://localhost:8080/MyAppContext/jsp/jsp/doactiontwo, and when clicked, it obviously results in 404 status.
Can somebody explain why, first of all, the submission on the second JSP page requires the servlet to have a pattern of /jsp/doactiontwo to work rather than /doactiontwo? And is there a way around to avoid appending /jsp to the URL pattern?
And second, why when the second servlet processes the request and dispatches/forwards it to the first page, the URL now contains two jsp/'s?
You need to change your design to allow the controllers, a.k.a. Servlets, to drive your application. In this particular case, use the URL Pattern of second Servlet (doactiontwo) in place of you link:
#WebServlet(urlPatterns = { "doactiontwo" }
public class DoActionTwoServlet extends HttpServlet { /* ... */ }
<form action="doactionone" method="post">
...
<a href="doactiontwo"> <!-- This should match your second servlet's URL pattern -->
<input type="submit" />...
</form>
Now, since the default method when anchor link is invoked is GET, you need to overwrite DoActionTwoServlet's doGet() method, and forward those requests to an actual doactiontwo.jsp:
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
RequestDispatcher rd = request.getRequestDispatcher("jsp/doactiontwo.jsp");
rd.forward(request, response);
}

SpringMVC: Set pathvariable using form data

So I'm a little unclear on how to properly use #PathVariable in the context I'm trying to here. Let me explain.
I have a JSP page with a table. The table contains all the records in a table in my database. That all works fine.
At the bottom of the table, there are buttons. Add, Edit, Delete. They are all using separate forms because I want to have separate URL paths/ HTTP Methods for each.
To accomplish this, I'm using JQuery so that when I hit submit on the form, it retrieves the ID value of the currently selected record. All of that works perfectly, and is not what I'm asking about.
Here is the form where the submit action is taking place:
<form:form id="editForm" action="./${courseId}.html" path="courseId" method="get">
<input type="hidden" id="editCourseId" name="courseId"/>
<input class="btn btn-lg btn-default btn-shadow" type="submit"
value="Edit"/>
</form:form>
So, what I want is for this form to call a URL with courseId as the #PathVariable value. The value of courseId is contained in the hidden input field with the name courseId. Again, that input field's value is set by JQuery code, and I've already tested that and it seems to be working perfectly.
What I want is to use courseId as the #PathVariable, and have it work in the following controller method:
#RequestMapping (value="/{courseId}", method=RequestMethod.GET)
public String editCourse(Model model,
#PathVariable ("courseId") String courseId){
System.out.println("CourseID: " + courseId);
return "course-form";
}
Obviously that method is still in the early stages, just trying to get confirmation that it works.
So, what do I have to do to make this happen? Again, I want to pass the value of courseId in the URL, as a RESTful style URL command, using it as a #PathVariable. How do I do this?
Thanks so much.
You can get a very clear explanation here
#RequestMapping (path="/{courseId}", method=RequestMethod.GET)
public String editCourse(Model model,
#PathVariable String courseId){
System.out.println("CourseID: " + courseId);
return "course-form";
}

Duplicating url - Resteasy, Spring, HTML

Spring MVC "duplicates" parts of the URL
I looked at the previous link but the answers did not work and did not seem relevant (unless I'm doing something wrong).
My jboss-web.xml root is "/portal"
I have an jsp doc (with only html in it) that posts to a java controller that authenticates a username and password. The formaction is "portal/index".
<input type="submit" name="submit" value="Submit" onclick="return validation()" formaction="portal/index"/>
The controller's class level path annotation is "portal". The method that I want it to post to has a path annotation "/index".
#Controller
#Path("portal")
public class TheController
{....}
#POST
#Path("/index")
public Response login()
{....}
If the password and username are correct, then the method will forward to home.jsp. Otherwise, it will go back to index.jsp. For some reason, when I submit the form with the incorrect username and password the 1st time, it will go back to "portal/portal/index" (the form will show up) and if I submit it incorrectly a 2nd time, it will go to "portal/portal/portal/index" and the security filter it goes through will return a 404 not found. What is wrong with my set up that is leading to the duplicating URL?
Try using /index in form action, you are using relative path, I think that is causing the issue.
Also remove #path("portal") from your controller.
Update:
Try this in formaction
"${pageContext.request.contextPath}/index"

How to re-populate form fields on a jsp page after failed server validation

I have a very simple Java MVC web application and am using a servlet to handle form validation. If the form is validated, the request is forwarded to the appropriate view. However, if the form fails validation, the request is forwarded back to the form, which then displays the appropriate error message(s).
My question is this -- what is the most efficient way to re-populate all of the form fields with the data that was originally entered in the form by the user?
I am not using an MVC framework, just simple HttpServlets as the controller with .jsp as the view.
The easiest and probably least effort is to just use
<input name="foo" type="text" value="${param.foo}"/>
This should default to "" when the user first visits the form.
A little more can be done to create a custom tag which binds to the request. However this is probably not the solution you were looking for.
Edit: You may want to use <c:out value="${param.foo}"/> to protect against XSS attack.
Pass the fields back to the jsp as part of the request object. request.setAttribute(..)
Use those attributes to set the form fields.

Categories

Resources