I have a Spring MVC project where I am using controller advice to handle errors thrown in controllers. However, I also want to display a nice error page if an error occurs within JSP files (even though this really shouldn't happen!). Therefore I have added the following to my project's web.xml
file:
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/views/application/error/view-error.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/WEB-INF/views/application/error/view-error.jsp</location>
</error-page>
If I trigger an error in JSTL on purpose, the contents of view-error.jsp
is rendered fine. However, the content is appended to the output of the JSP file in which the error occurred. For instance, if an error occurs within display-users.jsp
at line 50, the result is that the output that was generated before the error occurred (line 1-50) is prepended the contents in view-error.jsp
.
This is very undesirable as it generates a funky looking error page. And since I cannot tell where an exception will be thrown (if I could, I would fix the error), then what the user sees is very likely to look bad.
I guess it's because the output is already in the buffer, and may already have been sent to the client? Is there any way I can fix this, or perhaps an alternative approach? Thanks!
Handling JSP Page Errors Any number of exceptions can arise when a JSP page is executed. To specify that the web container should forward control to an error page if an exception occurs, include the following pagedirective at the beginning of your JSP page: <%@ page errorPage="file-name" %>
Any number of exceptions can arise when a JSP page is executed. To specify that the web container should forward control to an error page if an exception occurs, include the following pagedirective at the beginning of your JSP page: <%@ page errorPage="file-name" %>
So handling exceptions is a safer side for the web developer. In JSP, there are two ways to perform exception handling: In this case, you must define and create a page to handle the exceptions, as in the error.jsp page. The pages where may occur exception, define the errorPage attribute of page directive, as in the process.jsp page.
In case of JSP, the default output stream has always been created when the JSP has been translated to servlet. Creating and associating another output stream is the reason for this problem. The following piece of code will describe the reason for this error condition: logger.error ("Exception writing ... to page output stream.");
This is a problem with large JSP generating big HTML, with scriptlet java code intermixed everywhere. As soon as enough data have been writen, the server commits the headers (sends them to client) and send the beginning of the page. At that moment, you can no longer rollback anything to get back the data that has already been received (and possibly displayed) by the browser.
That's one of the reasons why scriplet are not recommended, and if you really need to put some intelligence it the JSP, it should be at the beginning of the page before anything is actually sent to browser. But ideally, everything should have been computed in advance in a servlet and prepared data put in request attributes. That way the JSP should only contain simple conditionnal or loop tags in addition to HTML output and request attributes rendition. All that with little risk to generate an exception.
Looks like the OutputStream of the HttpServletResponse is being written to before the enitre JSP finishes rendering.
This ideally should be controllable by "autoflush" property. https://tomcat.apache.org/tomcat-5.5-doc/jspapi/javax/servlet/jsp/JspWriter.html
But just in case it isn't solvable by that:
You could intercept anything that written to HttpServletResponse by using the HttpServletResponseWrapper
approach.
The general idea there is that you create a Filter and that Filter will pass a "Response Wrapper" to the layers below. This Response Wrapper holds a reference to real Response instance. Anything that gets written to the Response, can be then manipulated by the Response Wrapper and then sent to the real Response instance.
So, for your case, you could append all the data in a StringBuilder, and when then controls returns back to the Filter, the Filter can print the entire StringBuilder to the real Response's OutputStream.
Here is an example that intercepts anything the Servlets, etc. write and then sends the GZipped version of that to the Browser:
http://tutorials.jenkov.com/java-servlets/gzip-servlet-filter.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With