sendredirect vs requestdispatcher - java

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.

Related

How to do sessions if client doesn't accept cookies? Understanding response.encodeURL() inside Servlet

I am practicing maintaining sessions between client and server. For that I used simple solution with JSESSIONID stored inside cookie. So my little program works like this:
index.html:
<html><body>
<form action="testing">
<input type="text" name="animal">
<button>Go</button>
</form>
</body></html>
MyServlet.java (mapped in XML as /testing):
public class MyServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
String name = req.getParameter("animal");
PrintWriter pw = resp.getWriter();
pw.print("Welcome " + name + "<br>");
HttpSession session = req.getSession();
if (session.isNew()) {
pw.print("New session: " + session.getId() + "<br>");
} else {
pw.print("Old session: " + session.getId() + "<br>");
}
pw.print("<a href=\'index.html\'> Home </a>");
}
}
So, if I submit something from FORM more than twice, Server should "find" my JSESSIONID stored in heap. Here is a picture of how it would look when FORM is submitted (I typed 'parrot' inside input):
But then I disabled cookies in my browser. After, my server never finds user's JSESSIONID because user never actually stores it anywhere. Remember that before, JSESSIONID was stored in a cookie, but since I disabled them, it can't possibly store it there anymore. So now, I am stuck.
What can I do in this case? I came across response.encodeURL() that uses URL and appends its JSESSIONID into URL. But I have trouble understanding how to implement it and how it works internally. Can someone tell me how to fix this using encodeURL() and actually explain how does code work after made such implementation?
As per the specification, the server should support a few ways of tracking sessions: with cookies, SSL sessions, or URL rewriting.
You are asking about URL rewriting, which works like this:
URL rewriting is the lowest common denominator of session tracking. When a client will not accept a cookie, URL rewriting may be used by the server as the basis for session tracking. URL rewriting involves adding data, a session ID, to the URL path that is interpreted by the container to associate the request with a session.
The session ID must be encoded as a path parameter in the URL string. The name of the parameter must be jsessionid. Here is an example of a URL containing encoded path information:
http://www.myserver.com/catalog/index.html;jsessionid=1234
URL rewriting exposes session identifiers in logs, bookmarks, referer headers, cached HTML, and the URL bar. URL rewriting should not be used as a session tracking mechanism where cookies or SSL sessions are supported and suitable.
Notice that it's a path parameter, not a query parameter. Your query params will follow that, like this:
http://www.myserver.com/catalog/index.html;jsessionid=1234?param1=value1&param2=value2&...
This mechanism is supported automatically by the server to track sessions, but it becomes pretty obvious that you need to give the server a helping hand. And you do that by making sure that all your links include the jsessionid otherwise your server won't identify your request with a session.
You can use encodeURL in your Java code:
Encodes the specified URL by including the session ID, or, if encoding is not needed, returns the URL unchanged. The implementation of this method includes the logic to determine whether the session ID needs to be encoded in the URL. For example, if the browser supports cookies, or session tracking is turned off, URL encoding is unnecessary.
For robust session tracking, all URLs emitted by a servlet should be run through this method. Otherwise, URL rewriting cannot be used with browsers which do not support cookies.
You need to do the same inside your JSP files. That's usually done with something like <c:url> instead of writing URLs directly into the file:
[...] You can use the url tag to rewrite URLs returned from a JSP page. The tag includes the session ID in the URL only if cookies are disabled; otherwise, it returns the URL unchanged. Note that this feature requires that the URL be relative. [...]
Here is something you can do. This is used to append your JSESSIONID to a url you would retrieve to the user, so they can navigate in it and maintain the information of who they are. About understanding how it works internally is simple as you see, it just append the id to a url you pass, how the user information are stored, is your function on the server side, you will use the id given by the user in the next requests to retrieve information to them.
/**
* Almost minimal processing for a servlet.
*
* #param nextUrl The url the caller would like to go to next. If
* supplied, put an encoded url into the returned
* html page as a hyperlink.
*/
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
PrintWriter out = resp.getWriter();
out.print("OK");
String param = req.getParameter("nextUrl");
if (param!=null) {
// append an encoded url to carry the sessionids
String targetUrl = resp.encodeURL(param);
out.print(". You want to go <a href=\"");
out.print(targetUrl);
out.print("\">here next</a>.");
}
}
Edit:
You put it in this part of your code
pw.print("<a href=\'" + resp.encodeURL("index.html") + "\'>Home</a>");

Servlet filter redirect, url not changed

I'm having a problem with my authentication filter. When the filter redirects to the login page, all the previous page (main page) is displayed in the login page.
If I go to the login page manually it works fine.
Here is my filter:
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
HttpSession session = req.getSession(false);
String loginURL = req.getContextPath() + SiteMap.LOGIN_CONTROLLER;
boolean sessionCreated = session != null && !session.isNew();
if (sessionCreated) {
chain.doFilter(request, response);
} else {
res.sendRedirect(loginURL);
}
I also noted that when filter redirects to the login page the URL in browser bar stays the same. The main problem is that I get content from other page in the login page. I don't know where is the problem.
Changing the URl in browser did not depends on filter but it depends on how you are calling the page/servlet. You can call your servlet/jsp in two ways
RequestDispatcher : Transfers the control to other under the same request (Same URL)
Send Redirect : Initiates a new request (New Url)
Note : All that filter will do is a validation for the request
what is the value of loginURL that you are passing . Send a relative path.
I was having the exact same problem.
The real problem is that I and you both forgot to add the following line:
response.setContentType("text/html");
After adding this line my redirect works fine. Before this my servlet will stay on the
same page with a blank page (because nothing has been written to the response oputput stream).
Hope this may help others who is having this problem. It took me some painful minutes before test this possibility.

Servlet: Cannot forward after response has been committed

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.

URL mapping after logging in

I have a login servlet with the URL mapping "/Login", which manages the user input and the login procedure. However, when the user logs in, the web site is directed to the URL:
http://localhost:8080/pilot_1/Login
instead of
http://localhost:8080/pilot_1/checklistallitem
It is to mention that the first URL works fine, it shows all the data, but I am not sure why the URL does not show up as desired. Here's my doPost method of the Login Servlet.
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String username = req.getParameter("j_username");
String password = req.getParameter("j_password");
if (users.containsKey(username)){
if ( users.get(username).equals(password)){
req.getSession().setAttribute("active_window", "Checklist");
req.getSession().setAttribute("current_team", "allteams");
getServletContext().getRequestDispatcher("/checklistallteam").forward(req, resp);
}else{
JOptionPane.showMessageDialog(null, "Invalid ID or Password");
}
}else{
JOptionPane.showMessageDialog(null, "Invalid ID or Password");
}
}
This is the difference between a redirect and a forward. When you forward a request with a dispatcher the resolution and processing happens server side, then the final response is returned to the calling client. On the other hand, when you issue a redirect, there is an intermediate response to the client that basically tells it - 'Call this other URL to fulfill the request'. As a side effect the client will be aware of the URL of the new resource and will update the location bar to reflect it.
Because a forward is processed entirely on the server side the URL in the clients location bar does not change.

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