I am handling my controlled exceptions using the following code:
#ExceptionHandler(MyException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public ModelAndView handleMyException(MyException e) {
ModelAndView mav = new ModelAndView(ERROR_PAGE);
(...)
return mav;
}
That is, I want to both use custom views for different errors AND use response status code for the HTTP response.
At the same time, for pure 404 I have the following config in web.xml
<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
<error-page>
<error-code>400</error-code>
<location>/400</location>
</error-page>
Which takes to a 404 specific view.
The problem is that when a NOT_FOUND is thrown from my #ExceptionHandled method, it is not showing my custom view, debugging shows that execution actually goes through the handleMyException method, but after it's done it also goes through the method that maps the /404 in web.xml, and that is the view that gets shown.
Also if I throw a different Response Code, I get the default behavior on Exceptions, instead of my custom view.
I can't reproduce your problem with Tomcat 6 ans Spring 2.3.4. That is correct, because accroding to Servlet specification 2.5, the deployment descriptor defines a list of error
page descriptions. The syntax allows the configuration of resources to be returned
by the container either when a servlet or filter calls sendError
on the response for specific status codes (...)
I tracked where Spring sets response code basing on #ResponseStatus(HttpStatus.NOT_FOUND)
It is here:
public class ServletInvocableHandlerMethod (...)
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
if (this.responseStatus == null) {
return;
}
if (StringUtils.hasText(this.responseReason)) {
webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason);
}
else {
webRequest.getResponse().setStatus(this.responseStatus.value());
}
// to be picked up by the RedirectView
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus);
}
In my case if error handler method is annotated
#ResponseStatus(HttpStatus.NOT_FOUND)
the following branch is selected:
else {
webRequest.getResponse().setStatus(this.responseStatus.value());
}
Because HttpServletResponse.setStatus is called and NOT HttpServletResponse.sendError, web container ignores error page defined in <error-code>404</error-code>
I hope my explanation will be useful to track the problem yourself. I suspect somewhere HttpServletResponse.sendError is called and it triggers web container to return default error page
It sounds like the problem is probably that the web container is trying to handle the 400/404's its seeing from the web application (because it doesn't understand the context of those errors). You probably need to get rid of the web.xml error page definitions and add more configuration to the Spring controllers to handle the generic 400/404 errors as well.
This guide helped me a lot when I was setting up exception handling in my app: http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
The web.xml tells the app container how to handle various response codes that are generated by the application. When you get an exception out of a controller method, it gets handled by the Spring #ExceptionHandler annotated method. At this point, the app container isn't involved so it has no idea what's going on yet.
My best understanding is that when you generate a 404 Http status from the exception handler method and return, Spring's basically done at that point, and the app container steps back in and says "I got a 404, what do I do with a 404? ah, redirect to /404". And then, control goes back to the web app itself to handle the /404 request.
Related
I'm setting up exception handling logic for a multipart project with common error page (that is hosted in other part of the project). When I tried to redirect to external URL on exception, tomcat 8.5.39 is showing default error instead. Funny thing is, this seems to work just fine in tomcat 8.5.38
I've tried many different exception handling techniques, but they all seem not to work for external redirects.
So currently, i have something like this in my web.xml file:
...
<error-page>
<error-code>404</error-code>
<location>/error/error404</location>
</error-page>
...
and for my Spring controller,
#Controller
#RequestMapping(value = "/error")
public class ErrorHandler{
...
#GetMapping(value = "error404")
public String error404(){
return "redirect:http://{myproject}/{404errorPage}";
}
...
}
I'm expecting this code to redirect the user to http://{myproject}/{404errorPage} when 404 error occurs, which works just fine in tomcat 8.5.38. But on 8.5.39, they seem to have changed error handling logic, and it will display default error page(browser default 404 page).
Any input or idea would be tremendously helpful.
This is a known regression in 8.5.39 which is fixed in the just released 8.5.40.
I created web application in spring and handled exception mappings for 404 and 500.If system doesn't find any resources for requested URL it redirects into custom 404.jsp instead of regular 404 page.Everything works fine till have decided to add a webservice in my app.I have included one more controller as a webservice there is no view for this controller and this needs to be invoke through curl command.
User may get into change the curl script.If they changed the URL it should show 404 status code.But it returns the custom 404.jsp as a html response instead of status code.Because dispatcher servlet will takes all urls with /*.
How I can solve this issue?
Please share your suggestions.
Spring 3.2 introduced the #ControllerAdvice, and as mentioned in the documentation:
It is typically used to define #ExceptionHandler
That means you can use the #ControllerAdvice to assist your #Controller like the following:
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.NOT_FOUND) // 404
#ExceptionHandler(Exception.class)
public void handleNoTFound() {
// Nothing to do
}
}
For further details please refer to this tutorial and this answer.
Is there a way to tell if Spring has loaded my #Controller?
I'm requesting a URL but I'm not hitting my controller and I can't figure out why
I'm loading controllers by doing a component scan
<context:component-scan base-package="com.example.app.web"/>
Other controllers in the same package as my failing controller are working fine.
My controller code is:
#Controller
#RequestMapping(value = "/app/administration/ecosystem")
public class AppEcosystemController {
#Autowired
EcosystemManagerService ecosystemManagerService;
#RequestMapping(value = "/Foo", method = RequestMethod.GET)
public String getEcosystem() {
/* Implementation */
}
The first thing I'd like to do is to be sure that this controller is getting picked up by the component scan.
Any suggestions?
Just enable logging for your application, you can find this information at INFO level
For example in my application I have a controller named UserController.
The following log4j.properties does the trick.
log4j.rootLogger=INFO, FILE
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=../logs/rest-json.log
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
I can see in the log that RequestMappingHandlerMapping mapped my controller (scroll all the way to the right).
07:28:36,255 INFO RequestMappingHandlerMapping:182 - Mapped "{[/rest/**/users/{id}],methods=[GET],params=[],headers=[],consumes=[],produces=[text/xml || application/json],custom=[]}" onto public org..domain.User org.ramanh.controller.UserController.getUser(java.lang.String)
07:28:36,255 INFO RequestMappingHandlerMapping:182 - Mapped "{[/rest/**/users],methods=[POST],params=[],headers=[],consumes=[],produces=[text/xml || application/json],custom=[]}" onto public void org..controller.UserController.addUser(org...domain.User)
If you are still unsure I would suggest adding a method annotated with #PostConstruct.
You could easily look up the message in the log or place a break point in this method.
#PostConstruct
protected void iamAlive(){
log.info(“Hello AppEcosystemController”)
}
If you find that your controller is initialized correctly but still the url is not accessible.I would test the following
You are getting 404 error - maybe you are not pointing to the correct
url (do not forget to add the application as prefix to the url)
You are getting 404 error - Dispatcher servlet mapping in web.xml doesn't meet
the url above
You are getting 403/401 – maybe you are using
spring security and it’s blocking the url
You are getting 406 – your
content type definition is conflicting with your request
You are getting 50x – something is buggy in your code
I made an ApplicationContextDumper. Add it into application context, it will dump all beans and their dependencies in the current context and parent contexts (if any) into log file when the application context initialization finishes. It also lists the beans which aren’t referenced.
It was inspired by this answer.
You could start out with enabling debug logging for Spring as outlined here.
I'd also recommend leveraging the MVC testing support, which you'll find in the spring-test jar. Details on how to use it can be found here and here.
I am trying to show an custom error message on any occurrence of exception or error in my business layer. I am catching the exception in my controller and I would like to display it in my JSP.
This exception or error is not associated with any field in the screen, it's a pure server exception. I am also using an Annotated Controller. I am using Prototype for making AJAX requests to my controller.
In Spring you can register a HandlerExceptionResolver which will catch exceptions thrown by your Spring MVC controllers and forward them to the view layer for rendering. These are described in the Spring docs here. Start with the SimpleMappingExceptionResolver (see javadoc) which gives a simple mechanism for mapping exception types to views.
However, if the exception occurs outside if your Spring controller, for whatever reason, then you'll need a more generic fall-back solution, which involves configuring error pages in your web.xml file. This is not Spring-specific. See here for an example of how to do it.
From a jsp is thrown a NullPointerException for example using <% null.toString(); %>
This exception is not handled by the HandlerExceptionResolver, but thrown to the web container(tomcat) and converted into a code 500 error.
How can I configure spring to get that error in my HandlerExceptionResolver ?
Details:
Spring can be configured to handle exceptions thrown inside Controllers, but not exceptions thrown by view.
Of course i can resolve the NullPointerException, but i want to design a solution that will gracefully resolve any possible problem on the web application in order to display a user friendly message to the user.
See the HandlerInterceptor interface instead. You'll want the afterCompletion method. You can then intercept the response and then set the appropriate header information to redirect to a container-configured error web page. You're right that Spring doesn't have this functionality, this is going to have to be specified by the web.xml which determines which codes map to which pages.
I have not worked with this particular bit of the spring framework, but the docs say
"Interface to be implemented by objects than can resolve exceptions thrown during handler mapping or execution, in the typical case to error views. Implementors are typically registered as beans in the application context.
Error views are analogous to the error page JSPs, but can be used with any kind of exception including any checked exception, with potentially fine-granular mappings for specific handlers."
so I'd imagine that given that NullPointer extends RuntimeException the framework isn't designed to catch it. Is there a reason the exception(s) can't be handled in the controller directly?