Raise and handle NoResourceFoundException
See gh-29491
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -50,6 +50,7 @@ import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
@@ -121,6 +122,7 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa
|
||||
ServletRequestBindingException.class,
|
||||
MethodArgumentNotValidException.class,
|
||||
NoHandlerFoundException.class,
|
||||
NoResourceFoundException.class,
|
||||
AsyncRequestTimeoutException.class,
|
||||
ErrorResponseException.class,
|
||||
ConversionNotSupportedException.class,
|
||||
@@ -158,6 +160,9 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa
|
||||
else if (ex instanceof NoHandlerFoundException subEx) {
|
||||
return handleNoHandlerFoundException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
|
||||
}
|
||||
else if (ex instanceof NoResourceFoundException subEx) {
|
||||
return handleNoResourceFoundException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
|
||||
}
|
||||
else if (ex instanceof AsyncRequestTimeoutException subEx) {
|
||||
return handleAsyncRequestTimeoutException(subEx, subEx.getHeaders(), subEx.getStatusCode(), request);
|
||||
}
|
||||
@@ -348,6 +353,24 @@ public abstract class ResponseEntityExceptionHandler implements MessageSourceAwa
|
||||
return handleExceptionInternal(ex, null, headers, status, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the handling of {@link NoResourceFoundException}.
|
||||
* <p>This method delegates to {@link #handleExceptionInternal}.
|
||||
* @param ex the exception to handle
|
||||
* @param headers the headers to use for the response
|
||||
* @param status the status code to use for the response
|
||||
* @param request the current request
|
||||
* @return a {@code ResponseEntity} for the response to use, possibly
|
||||
* {@code null} when the response is already committed
|
||||
* @since 6.1
|
||||
*/
|
||||
@Nullable
|
||||
protected ResponseEntity<Object> handleNoResourceFoundException(
|
||||
NoResourceFoundException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
|
||||
|
||||
return handleExceptionInternal(ex, null, headers, status, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the handling of {@link AsyncRequestTimeoutException}.
|
||||
* <p>This method delegates to {@link #handleExceptionInternal}.
|
||||
|
||||
@@ -50,6 +50,7 @@ import org.springframework.web.multipart.support.MissingServletRequestPartExcept
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
@@ -125,6 +126,10 @@ import org.springframework.web.util.WebUtils;
|
||||
* <td><p>NoHandlerFoundException</p></td>
|
||||
* <td><p>404 (SC_NOT_FOUND)</p></td>
|
||||
* </tr>
|
||||
* <tr class="rowColor">
|
||||
* <td><p>NoResourceFoundException</p></td>
|
||||
* <td><p>404 (SC_NOT_FOUND)</p></td>
|
||||
* </tr>
|
||||
* <tr class="altColor">
|
||||
* <td><p>AsyncRequestTimeoutException</p></td>
|
||||
* <td><p>503 (SC_SERVICE_UNAVAILABLE)</p></td>
|
||||
@@ -198,6 +203,9 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
|
||||
else if (ex instanceof NoHandlerFoundException theEx) {
|
||||
mav = handleNoHandlerFoundException(theEx, request, response, handler);
|
||||
}
|
||||
else if (ex instanceof NoResourceFoundException theEx) {
|
||||
mav = handleNoResourceFoundException(theEx, request, response, handler);
|
||||
}
|
||||
else if (ex instanceof AsyncRequestTimeoutException theEx) {
|
||||
mav = handleAsyncRequestTimeoutException(theEx, request, response, handler);
|
||||
}
|
||||
@@ -413,6 +421,26 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case where no static resource was found.
|
||||
* <p>The default implementation returns {@code null} in which case the
|
||||
* exception is handled in {@link #handleErrorResponse}.
|
||||
* @param ex the {@link NoResourceFoundException} to be handled
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler the resource handler
|
||||
* @return an empty {@code ModelAndView} indicating the exception was handled, or
|
||||
* {@code null} indicating the exception should be handled in {@link #handleErrorResponse}
|
||||
* @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
|
||||
* @since 6.1
|
||||
*/
|
||||
@Nullable
|
||||
protected ModelAndView handleNoResourceFoundException(NoResourceFoundException ex,
|
||||
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case where an async request timed out.
|
||||
* <p>The default implementation returns {@code null} in which case the
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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
|
||||
*
|
||||
* https://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.resource;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.ProblemDetail;
|
||||
import org.springframework.web.ErrorResponse;
|
||||
|
||||
/**
|
||||
* Raised when {@link ResourceHttpRequestHandler} can not find a resource.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 6.1
|
||||
* @see org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
|
||||
* @see org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class NoResourceFoundException extends ServletException implements ErrorResponse {
|
||||
|
||||
private final HttpMethod httpMethod;
|
||||
|
||||
private final String resourcePath;
|
||||
|
||||
private final ProblemDetail body;
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance.
|
||||
*/
|
||||
public NoResourceFoundException(HttpMethod httpMethod, String resourcePath) {
|
||||
super("No static resource " + resourcePath + ".");
|
||||
this.httpMethod = httpMethod;
|
||||
this.resourcePath = resourcePath;
|
||||
this.body = ProblemDetail.forStatusAndDetail(getStatusCode(), getMessage());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the HTTP method for the request.
|
||||
*/
|
||||
public HttpMethod getHttpMethod() {
|
||||
return this.httpMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path used to locate the resource.
|
||||
* @see org.springframework.web.servlet.HandlerMapping#PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
|
||||
*/
|
||||
public String getResourcePath() {
|
||||
return this.resourcePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatusCode getStatusCode() {
|
||||
return HttpStatus.NOT_FOUND;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProblemDetail getBody() {
|
||||
return this.body;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -537,8 +537,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
|
||||
/**
|
||||
* Processes a resource request.
|
||||
* <p>Checks for the existence of the requested resource in the configured list of locations.
|
||||
* If the resource does not exist, a {@code 404} response will be returned to the client.
|
||||
* <p>Finds the requested resource under one of the configured locations.
|
||||
* If the resource does not exist, {@link NoResourceFoundException} is raised.
|
||||
* If the resource exists, the request will be checked for the presence of the
|
||||
* {@code Last-Modified} header, and its value will be compared against the last-modified
|
||||
* timestamp of the given resource, returning a {@code 304} status code if the
|
||||
@@ -555,8 +555,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
Resource resource = getResource(request);
|
||||
if (resource == null) {
|
||||
logger.debug("Resource not found");
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
throw new NoResourceFoundException(HttpMethod.valueOf(request.getMethod()), getPath(request));
|
||||
}
|
||||
|
||||
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
|
||||
@@ -611,12 +610,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
|
||||
@Nullable
|
||||
protected Resource getResource(HttpServletRequest request) throws IOException {
|
||||
String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
|
||||
if (path == null) {
|
||||
throw new IllegalStateException("Required request attribute '" +
|
||||
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE + "' is not set");
|
||||
}
|
||||
|
||||
String path = getPath(request);
|
||||
path = processPath(path);
|
||||
if (!StringUtils.hasText(path) || isInvalidPath(path)) {
|
||||
return null;
|
||||
@@ -635,6 +629,15 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
return resource;
|
||||
}
|
||||
|
||||
private static String getPath(HttpServletRequest request) {
|
||||
String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
|
||||
if (path == null) {
|
||||
throw new IllegalStateException("Required request attribute '" +
|
||||
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE + "' is not set");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the given resource path.
|
||||
* <p>The default implementation replaces:
|
||||
|
||||
Reference in New Issue
Block a user