Simple question! Don't know why Google doesn't know the answer!
In a Java servlet, how can I set the URL anchor (hash) when returning to the user?
The URL anchor is handled by the browser only and never even reaches the server (it's not part of the request).
What this means is that server-side, either in a servlet as you propose or with any other server-side technology (e.g. PHP), you can redirect to an URL which has the URL anchor set, but you cannot check if an URL anchor was provided in the request you are currently processing.
This limitation prevents you from setting the URL anchor while keeping the rest of the URL unchanged, because the server has no way to differentiate between the address with and without the URL anchor.
So, this, you can do: the canonical address to this answer is this
http://stackoverflow.com/a/27988314/4402557
but the server redirects it to this
http://stackoverflow.com/questions/27987762/how-to-set-url-anchor-hash-in-servlets/27988314#27988314
Note that the part of the URL before the anchor is not the same.
In an HTTP servlet, you can achieve this by using the sendRedirect(String) method of the HTTPServletResponse object passed to your service method, for example
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
/* ... */
response.sendRedirect("http://example.com/your/url#hash");
}
However, this, you cannot do: redirect this
http://example.com/some/url
to this
http://example.com/some/url#there
As far as the server is concerned, both are the same address, it cannot distinguish between them. That makes it impossible to check if the address was the first (the one without the URL anchor) and redirect conditionally if it is. Redirecting without checking will, of course, create a redirect loop.
However, depending on what it is exactly you are trying to accomplish, even if you cannot do it in your servlet, you can probably achieve it with client-side scripting (i.e. JavaScript). Have a look at the window.location.hash property.
Once I encountered a similar need.
My problem was:
I had two different forms on the same jsp page (one for registrations and one for logging in). The forms were made visible by hashes in urls, i.e. http://myapp.com/auth#login made login form visible and http://myapp.com/auth#signup made signup form visible.
I needed to show the validation errors to the user by reloading the same page, but couldn't navigate to hashes from my servlet.
Here's how I showed validation errors (jsp):
<form method="post" action="authentication">
<c:forEach items="${errorSignupMessage}" var="message">
<div class="error-message">${message}</div>
</c:forEach>
..................................
</form>
and errorSignupMessage was the attribute I passed in through servlet
request.setAttribute("errorSignupMessage", array);
The solution:
Firstly, I created the hidden field in my form that had to contain the needed attributes:
<input type="text" name="ACTION" class="hidden important" value="signup" data-attr="${errorSignupMessage != null ? true : false}"/>
the important thing to look at is the attribute data-attr. As you can see, I set the boolean value on the attribute depending on actions from servlet, i.e. if the errorSignupMessage is null, than the user interacted with another form and if errorSignupMessage exists and is not null, then the current form is the one the user interacted with.
Then I went to javascript and using window.onload event did the data attribute detection and wrote some logic for changing the location.hash value.
var authForms = document.getElementsByClassName('important');
function changeHash() {
for(var i = 0; i < authForms.length; ++i) {
var form = authForms[i];
if (form.value === 'signup' && form.getAttribute('data-attr') === 'true') {
location.hash = '#signup';
} else {
location.hash = '#login';
}
}
}
window.onload = changeHash;
This is how I solved that. Of course, you'll need to adopt all this for your needs, but I think you still get the general idea.
Hope it was helpful!
Related
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)
Is there a way to, from inside of a jsp:include page, get its requesting page to respond with an HTTP 500 error? I've tried using the response.sendError(418, "I'm a teapot.");, but that only works in the JSP that contains the jsp:include, and only if it is the first line because you can't call it after the response has been committed. What I have is this:
Index.jsp:
// other HTML
<jsp:include page="Exapmle.jsp">
<jsp:param name="aVariable" value="aValue" />
</jsp:include>
// other HTML
Example.jsp:
<%
String aVariable = request.getParameter("aVariable");
if (aVariable != null && !aVariable.trim().isEmpty) {
// code to generate content
%><%=someContent%><%
} else {
response.sendError(418, "I'm a teapot");
}
%>
So is there any way to do this? I'm doubtful based on how JSP's work, but hoping somewhere here can help. Also, servlets aren't an option (right now, at least).
If you get it to work, you shouldn't rely on it to always work on all platforms and future updates. As you state correctly: Once the response has been committed (e.g. all the HTTP headers are on the way to the client) there's no more way for the server to add any other HTTP headers. Let alone change the status code.
That's the reason why JSPs are considered the VIEW layer of an application, not the CONTROLLER. The times when all application logic was written in JSPs are long over and you should have proper request handling somewhere (if only in a servlet, but probably rather in a more powerful abstraction/framework) - decide about the result and either redirect to an error message or the proper visualization in that code.
JSP is good to render the content that you send along with the proper status code. Not to decide which status code to send.
I have a URL say abc.com/somecontroller?someparam=1 which renders a form. Form on submit sends the form params to /ajaxAction
Is there a way I could get this abc.com/somecontroller?someparam=1 (i.e. the form URL?)
I am more interested in getting the someparam value from the URL of the form.
PS: the above URL abc.com/somecontroller?someparam=1 is dynamically generated, so I can not access it otherwise.
And request.forwardURI will give me /ajaxAction (i.e. the URL of the action in form and not the url of the form itself).
EDIT:
I have no control over form as it is also dynamic and user has hundreds of templates to select from. Each template has different no. of fields.
So if I would prefer some other way to get the URL.
Why don't you use javascript in form and add to ajax request array with GET params? (or with the url of the action which generated form)
You can get them from original request f.e. by this script.
While rendering the GSP of your form, you can do like this:
myaction.gsp
<html>
<body>
<!-- Your content -->
<script>
var paramsString = '${params.collect { k, v-> "$k=$v" }.join("&") }';
</script>
</body>
</html>
So, when you GSP is rendered, the script tag will have something like this (for a URL abc.com/somecontroller?someparam=1&foo=2:
var paramsString = 'someparam=1&foo=2';
Now, in your AJAX code, you can pass that string as the query arguments:
$.ajax({
url: '/ajaxAction?' + paramsString
/* rest of the AJAX code */
});
Now, in your ajax controller action, you can simply do the params.someparam.
EDIT
Well, I just realized that you don't have to any GSP stuff I mentioned above. Simply do the AJAX call like this:
$.ajax({
url: '/ajaxAction' + location.search
/* rest of the AJAX code */
});
The location.search will give you the direct string: ?someparam=1&foo=2
I ended up using flash to store the someparam
In my controller which is being used to render the form at abc.com/somecontroller?someparam=1 I use flash as this:
flash.put("someparam", params.someparam)
This worked as a quick workaround to the issue. I feel this would work well in situations where I have no control over the gsp.
If anyone finds any issue, please comment otherwise I will mark this as the answer.
Ok, so I've got an interesting case of login page redirection going on.
My webservice has a login page (login.html) with some javascript to handle logging in and redirecting to a hardcoded 'default' page. The webservice is written in Java with a servlet filter handling redirection if a user is unauthenticated (so if a user tries to access domain/statistics without being logged in, they are directed to domain/login.html). The redirection from the protected services works: I can redirect to the login page and once a user is authenticated, redirect them to a default page. I am having issues, however, redirecting to the previous page.
I know this is usually handled with the argument document.referrer in the Javascript, which I have tried, but due to the Java's redirection with response.sendRedirect, the Referer header is not sent.
How can I get these two aspects to redirect to the previously called page? Is it something I need to add on the Javascript side, the Java side, or both?
What I've done is to drop the original (redirected) URL into a hidden input field on the login form. The code that does the authentication should just check that parameter, and if it's not empty it can redirect after establishing the session.
You have to be careful doing this to prevent XSS attacks etc., but it's not that hard and it works just fine.
In my framework (Stripes), I can push the original URL (taken from the HttpServletRequest object, a combination of the servlet path, the "path info", and the query string) into a special holding box that will cause the framework to give it back to me on the next request as a parameter. Without that, the simple thing to do is add the URL as a parameter when you redirect. Just URL-encode the original attempted URL and tack it onto the redirect URL with some parameter name. Then, on your login page, you just check for it:
<c:if test='${not empty param.attemptedUrl}'>
<input type='hidden' name='attemptedUrl' value='${fn:escapeXml(param.attemptedUrl)}'>
</c:if>
Then your login action will get that parameter too when the login form is submitted, and it can act on it as appropriate.
Send Redirect will ask to the client to repeat the request to the resource you choose. Have you think of Spring Security with minimal configuration you can achieve this quite easily.
Take a look at this:
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/ns-config.html
Is it possible to use request.setAttribute on a JSP page and then on HTML Submit get the same request attribute in the Servlet?
No. Unfortunately the Request object is only available until the page finishes loading - once it's complete, you'll lose all values in it unless they've been stored somewhere.
If you want to persist attributes through requests you need to either:
Have a hidden input in your form, such as <input type="hidden" name="myhiddenvalue" value="<%= request.getParameter("value") %>" />. This will then be available in the servlet as a request parameter.
Put it in the session (see request.getSession() - in a JSP this is available as simply session)
I recommend using the Session as it's easier to manage.
The reply by Phil Sacre was correct however the session shouldn't be used just for the hell of it. You should only use this for values which really need to live for the lifetime of the session, such as a user login. It's common to see people overuse the session and run into more issues, especially when dealing with a collection or when users return to a page they previously visited only to find they have values still remaining from a previous visit. A smart program minimizes the scope of variables as much as possible, a bad one uses session too much.
You can do it using pageContext attributes, though:
In the JSP:
<form action="Enter.do">
<button type="SUBMIT" id="btnSubmit" name="btnSubmit">SUBMIT</button>
</form>
<% String s="opportunity";
pageContext.setAttribute("opp", s, PageContext.APPLICATION_SCOPE); %>
In the Servlet (linked to the "Enter.do" url-pattern):
String s=(String) request.getServletContext().getAttribute("opp");
There are other scopes besides APPLICATION_SCOPE like SESSION_SCOPE. APPLICATION_SCOPE is used for ServletContext attributes.
If you want your requests to persists try this:
example: on your JSP or servlet page
request.getSession().setAttribute("SUBFAMILY", subFam);
and on any receiving page use the below lines to retrieve your session and data:
SubFamily subFam = (SubFamily)request.getSession().getAttribute("SUBFAMILY");
Try
request.getSession().setAttribute("SUBFAMILY", subFam);
request.getSession().getAttribute("SUBFAMILY");
Correct me if wrong...I think request does persist between consecutive pages..
Think you traverse from page 1--> page 2-->page 3.
You have some value set in the request object using setAttribute from page 1, which you retrieve in page 2 using getAttribute,then if you try setting something again in same request object to retrieve it in page 3 then it fails giving you null value as "the request that created the JSP, and the request that gets generated when the JSP is submitted are completely different requests and any attributes placed on the first one will not be available on the second".
I mean something like this in page 2 fails:
Where as the same thing has worked in case of page 1 like:
So I think I would need to proceed with either of the two options suggested by Phill.
i think phil is right request option is available till the page load. so if we want to sent value to another page we want to set the in the hidden tag or in side the session if you just need the value only on another page and not more than that then hidden tags are best option if you need that value on more than one page at that time session is the better option than hidden tags.