I have a JSP page (in Tomcat) which uses JSP Tags to retrieve some data. But these JSP Tags can throw exceptions (For example when parameter values are invalid). Now I want to implement a nicer error handling for these situation. I failed to find a way to GLOBALLY specify an exception handler (error-page definitions in web.xml don't work for exceptions thrown in a JSP). The only way I found so far is specifiying an errorPage attribute in the page header of ALL JSP-files.
<% page errorPage="/WEB-INF/jsp/errors/500.jsp" %>
Quite annoying to do this for ALL JSPs, but acceptable. But not acceptable is the fact that the error page is always delivered with a HTTP status code of 200. I want a 500 instead. I tried using a servlet as errorPage instead of a JSP and tried to set response.setStatus(500) and also response.sendError(500) but both calls seems to be ignored. So this code prints "200" two times and I have no idea why:
System.out.println(response.getStatus());
response.setStatus(500);
System.out.println(response.getStatus());
So the question is: How can I set the HTTP status code in JSP error handlers?
You can configure your error pages in web.xml.
<error-page>
<error-code>
500
</error-code>
<location>
/500.jsp
</location>
</error-page>
in your 500.jsp, set the directive as <%# page isErrorPage="true" %>
Instead of setStatus(500), you'd better use sendError(500) - there are some differences.
The config in web.xml works fine with sendError, however, if you don't want the config in web.xml, error-page from page directive worked just for exceptions for me, not for HTTP error codes.
Related
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 think the title above is a bit confusing.
What I'm trying to achieve:
I have a jps page(located in WEB-INF) with a hyperlink in it that will call another jsp (in WEB-INF) via servlet.
I understand that this can be achieved using the following:
Go to this page
But because there will be lots of hyperlinks, my idea was to have a general servlet(OpenPagesServlet) to handle all those pages.
Something like this:
JSP page:
<% request.setAttribute("page", "page1.jsp");%>
Page 1
OpenPagesServlet in doGet method:
String page = (String) request.getAttribute("page");
request.getRequestDispatcher("/WEB-INF/" + page).forward(request, response);
I tried the code above and I get:
HTTP Status 404 - Not Found
type Status report
messageNot Found
descriptionThe requested resource is not available.
But if I try with session.setAttribute / sesion.getAttribute the code works fine, but I don't want to have sessions on each time I click on hyperlinks.
The other approach I found was to use:
Page 1
and inside the servlet:
String page = (String)request.getParameter("value");
request.getRequestDispatcher("/WEB-INF/" + page).forward(request, response);
It worked, but this approach is not good because the page can then be accessed directly using the url:
http://localhost:8080/WebApp/OpenPagesServlet?value=page1
So...my question is why request.setAttribute/request.getAttribute is returning 404?
Is there a different approach to achieve what I'm trying to do?
An HttpServletRequest and its attributes only live for the duration of one HTTP request/response cycle. After yo've set the attribute in the JSP, the JSP is rendered and sent as part of the HTTP response body. The Servlet container considers the request handled and clears its attributes. The attribute is now gone.
It is therefore no longer available in the next request that arrives after the user clicks the link.
The session attribute or request parameter is fine. Consider looking into the Front Controller pattern.
Also, consider using the core tag library (in particular the url tag) instead of scriptlets for constructing your links.
I've got a jsp file, which includes another jsp file to check some values and such:
<jsp:include page="setup.jsp" />
Inside the setup.jsp I've got some conditional code which determines if some needed values are set in the session and if not redirects them to a different page. Or at least it is supposed to, but the redirect seems to be getting ignored.
System.err.println("Redirecting!");
response.sendRedirect("http://www.google.com");
return;
I see "Redirecting!" get logged to the console, but the page continues on and renders normally. I had curl dump the headers for me and saw that the response is HTTP/1.1 200 OK so it definitely isn't sending a 302 redirect.
Any idea what the problem is and how I can fix this?
EDIT: I have verified that my response is not yet committed. response.isCommitted() returns false meaning the status code and headers have not been sent yet.
EDIT 2: I've tried calling response.sendRedirect() in many other places and find that I can successfully redirect before the . The redirect inside the JSP seems to be ignored and if I try to redirect right AFTER the jsp then I get an illegal state exception because the response has already been committed.
The <jsp:include> uses under the covers RequestDispatcher#include(). Click the link to see the javadoc. Here's an extract of relevance (emphasis mine):
...
The ServletResponse object has its path elements and parameters remain unchanged from the caller's. The included servlet cannot change the response status code or set headers; any attempt to make a change is ignored.
...
The HttpServletResponse#sendRedirect() basically sets the HTTP response status to 302 and the HTTP Location header to the target URL. It is thus totally being ignored.
The deeper problem is that you're abusing JSP as a page controller/filter. You should actually be using a normal servlet or filter class for this which runs far before JSP.
The redirect header (I believe) needs to be at the top of the page. View the HTML spec for reference.
Have you tried placing it at the very top of the page? A code sample would help us debug...
I've defined <%# page errorPage="/error.jsp" %> in the header that all JSP files include, to catch any unhandled exceptions and redirect to that error page instead of printing them. This works fine with one caveat - if error.jsp itself throws an exception, it will continuously redirect to itself in an infinite loop. I want to erase the errorPage value for just error.jsp so that it'll just print the exception as normal. I tried just redefining the errorPage property to be blank but I get the following error:
Page directive: illegal to have multiple occurrences of errorPage with different values (old: /error.jsp, new: )
Is there any way for me to overwrite that property? Or any other suggestions on how to prevent this issue?
It is indeed illegal to have multiple page declarations with the same attribute. Your choices are:
Not include your header into your error page.
Ensure that your error page doesn't throw any exception on its own. It should really be rather simple and straightforward - error page is no place for business logic. If you want to do something complicated there, consider redirecting to another page instead.
Why don't you just have a different include header for the error page which do not include it himself?!!
So, instead of having:
header.jsp
==========
a
b
c
errorPage=error.jsp
You could have:
commonHeader.jsp
===========
a
b
c
Without the errorPage directive
And modify the header to include the new one.
header.jsp
===========
include=commonHeader.jsp
errorPage=error.jsp
That way you don't need to change anything in the rest of your jsp's
You just need to change your errorPage from:
include="header.jsp"
to
include="commonHeader.jsp"
And the errorPage won't have an error page anymore ....
I ended up taking care of it simply by surrounding the page with < c:catch > tags to avoid making the redirect in the first place, and printing a barebones message with the exception to make certain it can't break.
I've defined an error-page in my web.xml:
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/error.jsp</location>
</error-page>
In that error page, I have a custom tag that I created. The tag handler for this tag e-mails me the stacktrace of whatever error occurred. For the most part this works great.
Where it doesn't work great is if the output has already begun being sent to the client at the time the error occurs. In that case, we get this:
SEVERE: Exception Processing ErrorPage[exceptionType=java.lang.Exception, location=/error.jsp]
java.lang.IllegalStateException
I believe this error happens because we can't redirect a request to the error page after output has already started. The work-around I've used is to increase the buffer size on particularly large JSP pages. But I'm trying to write a generic error handler that I can apply to existing applications, and I'm not sure it's feasible to go through hundreds of JSP pages making sure their buffers are big enough.
Is there a way to still allow my stack trace e-mail code to execute in this case, even if I can't actually display the error page to the client?
The errorPage isn't going to be used if you've already started sending data to the client.
What I do is use a JavaScript callback to check for an incomplete page and then redirect to the error page. At the beginning of your page in an includes header or something, initialize a boolean javascript variable to false, and register an onload handler to check the state and redirect to an error page.
<script type="text/javascript">
var pageLoadSuccessful = false;//set to true in footer.jsp
dojo.addOnLoad(function(){
if (!pageLoadSuccessful) window.location = "<c:url value="/error.do" />";
});
</script>
Then in a footer jsp, be sure to set this variable to true:
<script type="text/javascript">
pageLoadSuccessful = true;//declared in header.jsp
</script>
Have you tried using the <%# page errorPage="/myerrorpage.jsp" %> directive?
You also need to use <% page isErrorPage="true" $> in myerrorpage.jsp, then.
I think that may solve your problem. The only problem with that is that you need to include it in every JSP somehow.
In fact, this particular problem indicates that you were using scriptlets in JSP. This is a bad practice and this particular problem is one of the major reasons for that. You need to move all your business logic to a real java class so that you end up with only taglibs/EL in JSP. In a servlet/filter you can perfectly handle exceptions before forwarding the request to a JSP.