Reputation: 10599
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!
Upvotes: 7
Views: 1120
Reputation: 1338
Been there, done that. Here's a quick and dirty workaround until you can redesign.
1) Place the all the JSTL code that generates output in a new JSP -- let's call it display-users-view.jsp (call it whatever you want).
2) Import display-users-view.jsp from your display-users.jsp page via a <c:import>, but make sure you dump the contents to a var(!). e.g.:
<c:import url="display-users-view.jsp" var="output"/>
3) As a final step in display-users.jsp, dump the output to the screen with a simple:
${output}
Now, if the error is thrown before the ${output} .. no harm, no foul because you haven't output anything to the browser yet. If there is no error, the ${output} will dump the HTML that was generated in the display-users-view.jsp.
Note, by using c:import you don't have to pass any querystring or form params that were submitted to display-users.jsp because you will still have them available in your display-users-view.jsp.
Upvotes: 0
Reputation: 997
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
Upvotes: 1
Reputation: 149185
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.
Upvotes: 1