Servlet: Cannot forward after response has been committed - java

I'm working on servlet page that renders content based on geo-location, and I want to use both sendRedirect and forward together; e.g; you browse example.com/aPage.jsp from France; first I want the servlet to redirect you to example.com/fr/aPage.jsp and then forward you to the resources page.
This is what I have in my servlet:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
....
response.sendRedirect(REDIRECT_URL_BASED_ON_GEO);
// after redirect forward the resources page
RequestDispatcher view = request.getRequestDispatcher(RESOURCES_PAGE);
view.forward(request, response);
...
}
But I get:
java.lang.IllegalStateException: Cannot forward after response has been committed
I know the error appears because I can't use both sendRedirect and forward one after another, but I don't know how to achieve what I want (as described above) without this.
any help?

response.sendRedirect(REDIRECT_URL_BASED_ON_GEO);
// after redirect forward the resources page
After that line , Your response start writing to clinet.
And you are trying to add additional data to it.
The server has already finished writing the response header and is writing the body of the content, and which point you are trying to write more to the header - of course it cant rewind.
So,Thumb rule while dealing with servlet is
Finish your logic before redirect or forward add return statement.So execution ends there .

When you call
response.sendRedirect(REDIRECT_URL_BASED_ON_GEO);
you are sending your client a 302 HTTP status code with the location to redirect to. Your client then needs to make a new HTTP request to that location. Whichever Servlet is supposed to handle the path REDIRECT_URL_BASED_ON_GEO should then use the RequestDispatcher to forward to the resource described by RESOURCES_PAGE.
To better explain your exception
java.lang.IllegalStateException: Cannot forward after response has been committed
a committed response is a response where HTTP headers are already sent. If we look at your code
response.sendRedirect(REDIRECT_URL_BASED_ON_GEO);
After this line, you've already sent the response along with the headers (302).
RequestDispatcher view = request.getRequestDispatcher(RESOURCES_PAGE);
view.forward(request, response);
After these lines, you're asking the resource RESOURCES_PAGE to finish processing the request. That includes writing HTTP headers and body. But the request has already been processed and a response has already been sent, so it will fail, throwing the exception.

You have to be aware that a redirect is a complete response to the browser and the browser will in turn issue a new request to the url you redirected to. Even though you can't really sse it when dealing with the browser you always have to be aware that this is what happens.
Now, if you use the same controller for the second request you have to check wether a redirect is necessary or you can now do the forward instead.
if (!path.startsWith(locationPrefix)) {
response.sendRedirect(locationPrefix + path);
return;
} else {
RequestDispatcher view = request.getRequestDispatcher(RESOURCES_PAGE);
view.forward(request, response);
return;
}
Of course it would be nicer to have a distinct controller per request, but depending of url structure and framework this is not always possible.

Once you redirect, the servlet you're working on is no longer in control. You need to get the servlet that is the target of the redirect to recognize the correct condition to forward and then call forward there, with similar code:
RequestDispatcher view = request.getRequestDispatcher(RESOURCES_PAGE);
view.forward(request, response);
Even if it's the same servlet, it's a new invocation of the servlet's doGet( or other similar method.

Related

sendredirect vs requestdispatcher

I have a Java application server under the following URL:-
http://t4-dev.pta.com/gui
The user who lands to this page will be asked to login using CAS Server and the URL is returned back to http://t4-dev.pta.com/gui/ReturnLoginViaMax.
I have implemented a Servlet to handle this URL -
public class CasRedirectServlet extends HttpServlet{
public void service(HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException{
//RequestDispatcher rd = req
// .getRequestDispatcher("/test.jsp");
RequestDispatcher rd = req.getRequestDispatcher("banana/index.html#/dashboard/file/t4.json");
rd.forward(req, res);
//res.sendRedirect("banana/index.html#/dashboard/file/t4.json");
}
}
so, now if the user hits the above servlet he will be redirected to another json file:-
banana/index.html#/dashboard/file/t4.json
This works if the If I do a sendredirect but when I use requestdispatcher it fails
message /gui/banana/index.html#/dashboard/file/t4.json
description The requested resource is not available.
I'm not sure why the above resource is not found.
It doesn't work because your path contains a URL token (the part following the # sign), which is a client-side thing interpreted by the browser only, while RequestDispatcher.forward() does a server-side internal forward and does not send the token to the browser.
If you want to use URL tokens in this way, your only option is to use sendRedirect(). This triggers a round-trip to the browser and updates the actual URL that the browser is accessing, thus giving the browser access to the URL token.
It is simply because forward and redirect are not the same at all.
When you redirect, you pass an URL back to the client browser. The browser interprets the given URL banana/index.html#/dashboard/file/t4.json and emits a request for URL /gui/banana/index.html, gets the page, and automatically scrolls it to the anchor /dashbord/file/t4.json (or just displays it if it cannot find the anchor).
But when forwarding, you ask the servlet container to pass the current request to the servlet able to serve the given URL and the servlet container cannot process the # part which is for a browser and gives you an error.

GetRequestDispatcher.forward is sending me through doGet again

I have a servlet with doGet overridden. The relevant pieces would be:
// called from doGet(req,res)
private void serviceInternal(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.info("servicing a request");
// magically compute jsp path from request params (includes the
// logging of the app/mod/act as mentioned below
log.info(jspPath);
request.getRequestDispatcher(jspPath).forward(request, response);
}
Now I'm logging the urls that come into the service method and if the url is server:8080/context/app/mod/act?param=1 the log message would say app='app' mod='mod' act='act' and I've verified that this is working well.
When I try to hit something that should be routing me to a jsp, for some silly reason it sends me back into my doGet method! My log looks like this:
INFO 2015-04-12 21:35:07,511 servicing a request
INFO 2015-04-12 21:35:07,519 app='' mod='' act='' uid='null'
INFO 2015-04-12 21:35:07,571 /WEB-INF/index.jsp
INFO 2015-04-12 21:35:07,571 servicing a request
INFO 2015-04-12 21:35:07,571 app='WEB-INF' mod='index.jsp' act='' uid='null'
But I don't want to send my jsp through my doGet - I want to send it to the JSP! Why would it be doing this?
My servlet is catching "/*", if that helps at all. But I wouldn't have expected it to catch jsps.
What am I doing wrong here?
My servlet is catching "/*", if that helps at all. But I wouldn't have
expected it to catch jsps.
You just found the problem yourself. That is the reason.

What is the difference between response.sendRedirect() and request.getRequestDispatcher().forward(request,response) [duplicate]

This question already has answers here:
RequestDispatcher.forward() vs HttpServletResponse.sendRedirect()
(9 answers)
Closed 6 years ago.
I have got a problem with my page jump when I use JAVA,
if I use:
response.sendRedirect("login.jsp")
then I get this url: http://localhost:8080/login.jsp
But if I use
request.getRequestDispathcer("login.jsp").forward(request, response)
then I get this url: http://localhost:8080/Shopping/login.jsp (the "Shopping" is the name of my module).
What's the difference?
To simply explain the difference,
response.sendRedirect("login.jsp");
doesn't prepend the contextpath (refers to the application/module in which the servlet is bundled)
but, whereas
request.getRequestDispathcer("login.jsp").forward(request, response);
will prepend the contextpath of the respective application
Furthermore, Redirect request is used to redirect to resources to different servers or domains. This transfer of control task is delegated to the browser by the container. That is, the redirect sends a header back to the browser / client. This header contains the resource url to be redirected by the browser. Then the browser initiates a new request to the given url.
Forward request is used to forward to resources available within the server from where the call is made. This transfer of control is done by the container internally and browser / client is not involved.
forward
Control can be forward to resources available within the server from where the call is made. This transfer of control is done by the container internally and browser / client is not involved. This is the major difference between forward and sendRedirect. When the forward is done, the original request and response objects are transfered along with additional parameters if needed.
redirect
Control can be redirect to resources to different servers or domains. This transfer of control task is delegated to the browser by the container. That is, the redirect sends a header back to the browser / client. This header contains the resource url to be redirected by the browser. Then the browser initiates a new request to the given url. Since it is a new request, the old request and response object is lost.
For example, sendRedirect can transfer control from http://google.com to http://anydomain.com but forward cannot do this.
‘session’ is not lost in both forward and redirect.
To feel the difference between forward and sendRedirect visually see the address bar of your browser,
in forward, you will not see the forwarded address (since the browser is not involved)
in redirect, you can see the redirected address.
The main difference between the forward() and sendRedirect() methods is
that in the case of forward(), redirect happens at the server end and
is not visible to the client, but in the case of sendRedirect(),
redirection happens at the client end and it's visible to the client.
Other difference between Forward(ServletRequest request, ServletResponse response) and sendRedirect(String url) is
forward():
The forward() method is executed on the server-side.
The request is transferred to another resource within the same server.
It does not depend on the client’s request protocol since the forward () method is provided by the servlet container.
The request is shared by the target resource.
Only one call is consumed in this method.
It can be used within the server.
We cannot see the forwarded messages, it is transparent.
The forward() method is faster than sendRedirect() method.
It is declared in the RequestDispatcher interface.
sendRedirect():
The sendRedirect() method is executed on the client-side.
The request is transferred to another resource to a different server.
The sendRedirect() method is provided under HTTP so it can be used only with HTTP clients.
New request is created for the destination resource.
Two request and response calls are consumed.
It can be used within and outside the server.
We can see redirected address, it is not transparent.
The sendRedirect() method is slower because when new request is created old request object is lost.
It is declared in HttpServletResponse.
Which one is good? It depends upon the scenario for which method is more useful.
If you want control is transfer to a new server or context, and it is
treated as a completely new task, then we go for sendRedirect.
Generally, a forward should be used if the operation can be safely
repeated upon a browser reload of the web page and will not affect the
result.
1.redirect return the request to the browser from server,then resend the request to the server from browser.
2.forward send the request to another servlet (servlet to servlet).
Redirect and Request dispatcher are two different methods to move form one page to another.
if we are using redirect to a new page actually a new request is happening from the client side itself to the new page.
so we can see the change in the URL.
Since redirection is a new request the old request values are not available here.

redirecting between java servlets from url containing #

Hey,
Maybe the title is not the best choice, but I really don't know how to better describe the problem.
The thing is when you point your browser to url that contains #
http://anydomain.com/test/elsem/1234#dogeatdog
and for some reason (ie. there is a business logic) you want to redirect to other page
http://anydomain.com/test/els/1234
the #dogeatdog will be added to new url.
I found this behavior while developing wicket app, but just now I tested it with simple pure java servlet. Can someone explain it to me?
Here is the code just in case I'm doing something wrong:
private void process(HttpServletRequest req, HttpServletResponse res)
{
res.setContentType("text/plain");
try
{
HttpSession session = req.getSession();
Object as = session.getAttribute("as");
if (as == null)
{
log.info("redirecting");
session.setAttribute("as", 1);
res.sendRedirect("/test/");
}
else
{
log.info("writing");
PrintWriter out = res.getWriter();
out.write("after redirect "+as);
out.flush();
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
Hash fragments (#a_hash_fragment) never leave the browser, they are not part of HTTP request.
What the web server gets in this case is GET /test/elsem/1234, and it responds with redirect 3xx code and the new url /test/els/1234, which your browser picks and appends #dogeatdog. Makes sense now?
UPDATE: Thanks to Zack, here's a W3C document that exactly explains how this (should) work:
http://www.w3.org/Protocols/HTTP/Fragment/draft-bos-http-redirect-00.txt
From the sendRedirect Javadoc:
Sends a temporary redirect response to the client using the specified
redirect location URL. This method can accept relative URLs; the
servlet container must convert the relative URL to an absolute URL
before sending the response to the client. If the location is relative
without a leading '/' the container interprets it as relative to the
current request URI. If the location is relative with a leading '/'
the container interprets it as relative to the servlet container root.
Because of repetitive use of "relative" in the Javadoc, I suspect the new URL is using what it can from the old URL and then building from there...
In the brief amount of what I've read, forwarding should be used if possible instead of redirect.
See this for a good explanation of forward verses redirect.
See this for straight-forward examples of forwarding requests to Servlets or JSPs.
Of course, with forwarding, the original URL will remain intact so that may not be what you're looking for...
EDIT
With information from milan, I found some more information regarding URL fragments (the stuff after "#" - I didn't know that was their official name until corresponding with milan).
There's another SOF post that has some good information concerning this and possibly the best answer: URL Fragment and 302 redirects
I have "+1'd" milan for giving good direction on this...

Can we somehow change the url in addressbar after dispatching request from servlet to jsp

I am having a weird problem here, and I am really stuck, need to get this work badly.
so i have a page say index.jsp with a link say "a href=servlet?id=10". when I click on this link it will go to doGet() on my servlet and here is the code in my servlet.
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String action = request.getParameter("id");
// search database and create an arraylist
if(//user logged in)
address = "s/results.jsp";
else
address = "results.jsp";
// set arraylist in session object
RequestDispatcher dispatcher = request.getRequestDispatcher(address);
dispatcher.forward(request,response);
}
So the above code works fine but after request forwarding, my browser shows the url as
http://localhost/project/servlet?id=10.
I don't want the above url as i am forwarding to two different jsp's based on the user login status one is in 's' folder and other is outside of that.
if user is logged in then i forward to 's/results.jsp' and if user is not logged in i am forwarding to 'results.jsp'.
in case of s/results.jsp i am accessing resources like images and scripts from outside of 's' folder by using ../ in the results.jsp.
as url is not changing to s/results.jsp , i am unable to access the resources with '../'
and as i am using jsp pagination , when i click next the url is changing to s/results.jsp
and in that case i am able to access resources using ../
one solution in my mind is to copy all resources in s folder , but that would increase
redundancy.
one other solution in my mind is to create two different servlets for two jsp's
but i don't know where to put the servlet so that it can access resources outside of s folder with ../
is their any other good way i can do the task..
I have tried to find information about this but haven't been able to figure it out.
Any help will be very much appreciated.
You have basically instructed your webbrowser to send a request to exactly that URL. The forward does not change the URL. It is entirely server side. Apart from using response.sendRedirect() instead -which would trash the current request, including all of its attributes, and create a brand new request on the given URL-, you could also just change your link to <a href="results?id=10">, or when the user is logged in, to <a href="s/results?id=10">.
<a href="${user.loggedin ? 's/' : ''}results?id=10">
Finally alter the servlet mapping accordingly so that it get invoked on those URLs.
<url-pattern>/results</url-pattern>
<url-pattern>/s/results</url-pattern>
You'll only miss the JSP extension. But JSPs which are to be used by a dispatcher belong in /WEB-INF folder anyway so that they cannot be viewed by the enduser directly without invoking the servlet first. You also end up with nicer URLs.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String action = request.getParameter("id");
// search database and create an arraylist
if(//user logged in)
address = "s/results.jsp";
else
address = "results.jsp";
// set arraylist in session object
RequestDispatcher dispatcher = request.getRequestDispatcher(address);
dispatcher.forward(request,response);
}
in the above code instead of using request dispatcher,
RequestDispatcher dispatcher = request.getRequestDispatcher(address);
dispatcher.forward(request,response);
we can try with
response.sendRedirect(request.getContextPath()+"/address");

Categories

Resources