提交 62d6a43c 编写于 作者: B Brian Clozel 提交者: Rossen Stoyanchev

Add NoHandlerFoundException to DispatcherServlet

Prior to this commit, the DispatcherServlet would send an error
response using the HttpServlet API when no Handler was found to
handle this request.

Now the DispatcherServlet can be configured to throw a
NoHandlerFoundException, when the throwExceptionIfNoHandlerFound
property is set to "true".
Those exceptions can be later on caught by default or custom
HandlerExceptionResolvers/ExceptionHandlers.

Issue: SPR-10481
上级 4ef49062
......@@ -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);
}
}
/**
......
/*
* 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;
}
}
/*
* 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);
}
}
/*
* 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();
......
/*
* 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>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册