Skip to content

Spring - setThrowExceptionIfNoHandlerFound() on the default "aws-servless-java-container" DispatcherServlet #117

@mancini0

Description

@mancini0
  • Framework version:
<dependency>
            <groupId>com.amazonaws.serverless</groupId>
            <artifactId>aws-serverless-java-container-spring</artifactId>
            <version>0.9</version>
</dependency>
  • Implementations: Spring

Scenario

If I run my Spring web service using Tomcat, I am able to configure the DispatcherServlet to throw an exception when no handler is found for the request. This allows me to use an @ControllerAdvice annotated class to catch the NoHandlerFoundException, where I can then return a json message to the user instead of Spring's default whitepage response. To do this, In my configuration class that also implements WebApplicationInitializer, I call setThrowExceptionIfNoHandlerFound(true) on my dispatcherServlet object upon startup. Note I set the name of my servlet to the same name ("aws-servless-java-container") as the default servlet provided by LambdaSpringApplicationInitializer.


@Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.setConfigLocation("com.example.api");
        servletContext.addListener(new ContextLoaderListener(context));
        DispatcherServlet ds = new DispatcherServlet(context);
        **ds.setThrowExceptionIfNoHandlerFound(true);**
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("aws-servless-java-container", ds);
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/*");
    }

When I run my app using Tomcat, without the aws-serverless-java-container dependency, it works as expected, and unhandled requests throw the NoHandlerFoundException, and my @ControllerAdvice annotated class catches this exception and returns JSON:

@ControllerAdvice
@RequestMapping(produces = "application/json")
@ResponseBody
public class RestControllerAdvice {

....
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseEntity<Map<String, Object>> unhandledPath(final NoHandlerFoundException e) {
        Map<String, Object> errorInfo = new LinkedHashMap<>();
        errorInfo.put("timestamp", new Date());
        errorInfo.put("httpCode", HttpStatus.NOT_FOUND.value());
        errorInfo.put("httpStatus", HttpStatus.NOT_FOUND.getReasonPhrase());
        errorInfo.put("errorMessage", e.getMessage());
        return new ResponseEntity<Map<String, Object>>(errorInfo, HttpStatus.NOT_FOUND);
    }
...
}

When I remove the Tomcat dependency, keeping my configuration the same:

public class ApplicationConfig implements WebMvcConfigurer, WebApplicationInitializer {

...various bean configs...
@OverRide
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation("com.example.api");
servletContext.addListener(new ContextLoaderListener(context));
DispatcherServlet ds = new DispatcherServlet(context);
ds.setThrowExceptionIfNoHandlerFound(true);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("aws-servless-java-container", ds);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");
}

, it seems the setThrowExceptionIfNoHandlerFound(true) is not being set on the dispatcherServlet, even though I am using the same dispatcher name as the default dispatcher provided by aws.

I should mention that everything works as expected when using Spring Boot and the SpringBootLambdaContainerHandler. In that enviroment, I simply need to set these two properties in my application.properties:

spring.resources.add-mappings=false
spring.mvc.throw-exception-if-no-handler-found=true

How does one achieve the same behavior using Spring and the SpringLambdaContainerHandler?

From the Spring 5.0.X JavaDocs:
public void setThrowExceptionIfNoHandlerFound(boolean throwExceptionIfNoHandlerFound)
"Set whether to throw a NoHandlerFoundException when no Handler was found for this request. This exception can then be caught with a HandlerExceptionResolver or an ExceptionHandler controller method.
Note that if DefaultServletHttpRequestHandler is used, then requests will always be forwarded to the default servlet and a NoHandlerFoundException would never be thrown in that case.
Default is "false", meaning the DispatcherServlet sends a NOT_FOUND error through the Servlet response."

public class DefaultServletHttpRequestHandler
extends java.lang.Object
implements HttpRequestHandler, ServletContextAware

"An HttpRequestHandler for serving static files using the Servlet container's "default" Servlet.
This handler is intended to be used with a "/*" mapping when the DispatcherServlet is mapped to "/", thus overriding the Servlet container's default handling of static resources. The mapping to this handler should generally be ordered as the last in the chain so that it will only execute when no other more specific mappings (i.e., to controllers) can be matched.

Requests are handled by forwarding through the RequestDispatcher obtained via the name specified through the "defaultServletName" property. In most cases, the defaultServletName does not need to be set explicitly, as the handler checks at initialization time for the presence of the default Servlet of well-known containers such as Tomcat, Jetty, Resin, WebLogic and WebSphere. However, when running in a container where the default Servlet's name is not known, or where it has been customized via server configuration, the defaultServletName will need to be set explicitly."

Thanks!

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions