Merge pull request #359 from bclozel/SPR-10481
* bclozel-SPR-10481: Add NoHandlerFoundException to DispatcherServlet
This commit is contained in:
@@ -45,6 +45,7 @@ import org.springframework.context.i18n.LocaleContext;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.ui.context.ThemeSource;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -277,6 +278,9 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
/** Perform cleanup of request attributes after include request? */
|
||||
private boolean cleanupAfterInclude = true;
|
||||
|
||||
/** Throw a NoHandlerFoundException if no Handler was found to process this request? **/
|
||||
private boolean throwExceptionIfNoHandlerFound = false;
|
||||
|
||||
/** MultipartResolver used by this servlet */
|
||||
private MultipartResolver multipartResolver;
|
||||
|
||||
@@ -408,6 +412,24 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
this.detectAllViewResolvers = detectAllViewResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@code @ExceptionHandler} controller method.
|
||||
*
|
||||
* <p>Note that if
|
||||
* {@link org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler}
|
||||
* is used, then requests will always be forwarded to the default servlet and
|
||||
* a NoHandlerFoundException would never be thrown in that case.
|
||||
*
|
||||
* <p>Default is "false", meaning the DispatcherServlet sends a NOT_FOUND error
|
||||
* through the Servlet response.
|
||||
* @since 4.0
|
||||
*/
|
||||
public void setThrowExceptionIfNoHandlerFound(boolean throwExceptionIfNoHandlerFound) {
|
||||
this.throwExceptionIfNoHandlerFound = throwExceptionIfNoHandlerFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to perform cleanup of request attributes after an include request, that is,
|
||||
* whether to reset the original state of all request attributes after the DispatcherServlet
|
||||
@@ -1097,7 +1119,13 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + requestUri +
|
||||
"] in DispatcherServlet with name '" + getServletName() + "'");
|
||||
}
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
if(throwExceptionIfNoHandlerFound) {
|
||||
ServletServerHttpRequest req = new ServletServerHttpRequest(request);
|
||||
throw new NoHandlerFoundException(req.getMethod().name(),
|
||||
req.getServletRequest().getRequestURI(),req.getHeaders());
|
||||
} else {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.web.servlet;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
/**
|
||||
* Exception to be thrown if DispatcherServlet is unable to determine
|
||||
* a corresponding handler for an incoming HTTP request.
|
||||
* The DispatcherServlet throws this exception only if its
|
||||
* throwExceptionIfNoHandlerFound property is set to "true".
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 4.0
|
||||
* @see org.springframework.web.servlet.DispatcherServlet
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class NoHandlerFoundException extends ServletException {
|
||||
|
||||
private final String httpMethod;
|
||||
|
||||
private final String requestURL;
|
||||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for NoHandlerFoundException.
|
||||
* @param httpMethod the HTTP method
|
||||
* @param requestURL the HTTP request URL
|
||||
* @param headers the HTTP request headers
|
||||
*/
|
||||
public NoHandlerFoundException(String httpMethod, String requestURL, HttpHeaders headers) {
|
||||
super("No handler found for " + httpMethod + " " + requestURL + ", headers=" + headers);
|
||||
this.httpMethod = httpMethod;
|
||||
this.requestURL = requestURL;
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
public String getHttpMethod() {
|
||||
return this.httpMethod;
|
||||
}
|
||||
|
||||
public String getRequestURL() {
|
||||
return this.requestURL;
|
||||
}
|
||||
|
||||
public HttpHeaders getHeaders() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -42,6 +42,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
|
||||
|
||||
/**
|
||||
@@ -102,7 +103,8 @@ public abstract class ResponseEntityExceptionHandler {
|
||||
HttpMessageNotWritableException.class,
|
||||
MethodArgumentNotValidException.class,
|
||||
MissingServletRequestPartException.class,
|
||||
BindException.class
|
||||
BindException.class,
|
||||
NoHandlerFoundException.class
|
||||
})
|
||||
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
|
||||
|
||||
@@ -160,6 +162,10 @@ public abstract class ResponseEntityExceptionHandler {
|
||||
HttpStatus status = HttpStatus.BAD_REQUEST;
|
||||
return handleBindException((BindException) ex, headers, status, request);
|
||||
}
|
||||
else if (ex instanceof NoHandlerFoundException) {
|
||||
HttpStatus status = HttpStatus.NOT_FOUND;
|
||||
return handleNoHandlerFoundException((NoHandlerFoundException) ex, headers, status, request);
|
||||
}
|
||||
else {
|
||||
logger.warn("Unknown exception type: " + ex.getClass().getName());
|
||||
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
@@ -398,4 +404,20 @@ public abstract class ResponseEntityExceptionHandler {
|
||||
return handleExceptionInternal(ex, null, headers, status, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the response for NoHandlerFoundException.
|
||||
* This method delegates to {@link #handleExceptionInternal(Exception, Object, HttpHeaders, HttpStatus, WebRequest)}.
|
||||
* @param ex the exception
|
||||
* @param headers the headers to be written to the response
|
||||
* @param status the selected response status
|
||||
* @param request the current request
|
||||
* @return a {@code ResponseEntity} instance
|
||||
* @since 4.0
|
||||
*/
|
||||
protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers,
|
||||
HttpStatus status, WebRequest request) {
|
||||
|
||||
return handleExceptionInternal(ex, null, headers, status, request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,6 +46,7 @@ import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
|
||||
|
||||
@@ -147,6 +148,9 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
|
||||
else if (ex instanceof BindException) {
|
||||
return handleBindException((BindException) ex, request, response, handler);
|
||||
}
|
||||
else if (ex instanceof NoHandlerFoundException) {
|
||||
return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler);
|
||||
}
|
||||
}
|
||||
catch (Exception handlerException) {
|
||||
logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
|
||||
@@ -416,4 +420,24 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
|
||||
return new ModelAndView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case where no handler was found during the dispatch.
|
||||
* <p>The default sends an HTTP 404 error, and returns
|
||||
* an empty {@code ModelAndView}. Alternatively, a fallback view could be chosen,
|
||||
* or the NoHandlerFoundException could be rethrown as-is.
|
||||
* @param ex the NoHandlerFoundException to be handled
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the executed handler, or {@code null} if none chosen
|
||||
* at the time of the exception (for example, if multipart resolution failed)
|
||||
* @return an empty ModelAndView indicating the exception was handled
|
||||
* @throws IOException potentially thrown from response.sendError()
|
||||
* @since 4.0
|
||||
*/
|
||||
protected ModelAndView handleNoHandlerFoundException(NoHandlerFoundException ex, HttpServletRequest request,
|
||||
HttpServletResponse response, Object handler) throws IOException {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
return new ModelAndView();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -540,6 +540,20 @@ public class DispatcherServletTests extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testThrowExceptionIfNoHandlerFound() throws ServletException, IOException {
|
||||
DispatcherServlet complexDispatcherServlet = new DispatcherServlet();
|
||||
complexDispatcherServlet.setContextClass(SimpleWebApplicationContext.class);
|
||||
complexDispatcherServlet.setNamespace("test");
|
||||
complexDispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
|
||||
complexDispatcherServlet.init(new MockServletConfig(getServletContext(), "complex"));
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(getServletContext(), "GET", "/unknown");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
complexDispatcherServlet.service(request, response);
|
||||
assertTrue("correct error code", response.getStatus() == HttpServletResponse.SC_NOT_FOUND);
|
||||
}
|
||||
|
||||
public void testCleanupAfterIncludeWithRemove() throws ServletException, IOException {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(getServletContext(), "GET", "/main.do");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,6 +37,7 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.test.MockHttpServletResponse;
|
||||
import org.springframework.validation.BindException;
|
||||
@@ -52,6 +53,7 @@ import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
|
||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||
|
||||
@@ -184,6 +186,15 @@ public class ResponseEntityExceptionHandlerTests {
|
||||
testException(ex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noHandlerFoundException() {
|
||||
ServletServerHttpRequest req = new ServletServerHttpRequest(
|
||||
new MockHttpServletRequest("GET","/resource"));
|
||||
Exception ex = new NoHandlerFoundException(req.getMethod().toString(),
|
||||
req.getServletRequest().getRequestURI(),req.getHeaders());
|
||||
testException(ex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void controllerAdvice() throws Exception {
|
||||
StaticWebApplicationContext cxt = new StaticWebApplicationContext();
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.Collections;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.ConversionNotSupportedException;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.tests.sample.beans.TestBean;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -43,6 +44,7 @@ import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.ServletRequestBindingException;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
|
||||
|
||||
/** @author Arjen Poutsma */
|
||||
@@ -171,6 +173,18 @@ public class DefaultHandlerExceptionResolverTests {
|
||||
assertEquals("Invalid status code", 400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleNoHandlerFoundException() throws Exception {
|
||||
ServletServerHttpRequest req = new ServletServerHttpRequest(
|
||||
new MockHttpServletRequest("GET","/resource"));
|
||||
NoHandlerFoundException ex = new NoHandlerFoundException(req.getMethod().name(),
|
||||
req.getServletRequest().getRequestURI(),req.getHeaders());
|
||||
ModelAndView mav = exceptionResolver.resolveException(request, response, null, ex);
|
||||
assertNotNull("No ModelAndView returned", mav);
|
||||
assertTrue("No Empty ModelAndView returned", mav.isEmpty());
|
||||
assertEquals("Invalid status code", 404, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleConversionNotSupportedException() throws Exception {
|
||||
ConversionNotSupportedException ex =
|
||||
|
||||
@@ -4244,6 +4244,12 @@ public class SimpleController {
|
||||
<entry>400 (Bad Request)</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><classname>NoHandlerFoundException</classname></entry>
|
||||
|
||||
<entry>404 (Not Found)</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><classname>NoSuchRequestHandlingMethodException</classname></entry>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user