Support HTTP OPTIONS
Issue: SPR-13130
This commit is contained in:
@@ -347,6 +347,7 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
*/
|
||||
public DispatcherServlet() {
|
||||
super();
|
||||
this.setDispatchOptionsRequest(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -390,6 +391,7 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
*/
|
||||
public DispatcherServlet(WebApplicationContext webApplicationContext) {
|
||||
super(webApplicationContext);
|
||||
this.setDispatchOptionsRequest(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -428,9 +428,11 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic
|
||||
/**
|
||||
* Set whether this servlet should dispatch an HTTP OPTIONS request to
|
||||
* the {@link #doService} method.
|
||||
* <p>Default is "false", applying {@link javax.servlet.http.HttpServlet}'s
|
||||
* default behavior (i.e. enumerating all standard HTTP request methods
|
||||
* as a response to the OPTIONS request).
|
||||
* <p>Default in the {@code FrameworkServlet} is "false", applying
|
||||
* {@link javax.servlet.http.HttpServlet}'s default behavior (i.e.enumerating
|
||||
* all standard HTTP request methods as a response to the OPTIONS request).
|
||||
* Note however that as of 4.3 the {@code DispatcherServlet} sets this
|
||||
* property to "true" by default due to its built-in support for OPTIONS.
|
||||
* <p>Turn this flag on if you prefer OPTIONS requests to go through the
|
||||
* regular dispatching chain, just like other HTTP requests. This usually
|
||||
* means that your controllers will receive those requests; make sure
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -30,6 +30,7 @@ import org.springframework.web.util.WebUtils;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 2.5.2
|
||||
* @deprecated as of Spring 3.2, together with {@link DefaultAnnotationHandlerMapping},
|
||||
* {@link AnnotationMethodHandlerAdapter}, and {@link AnnotationMethodHandlerExceptionResolver}.
|
||||
@@ -43,11 +44,12 @@ abstract class ServletAnnotationMappingUtils {
|
||||
* @param request the current HTTP request to check
|
||||
*/
|
||||
public static boolean checkRequestMethod(RequestMethod[] methods, HttpServletRequest request) {
|
||||
if (ObjectUtils.isEmpty(methods)) {
|
||||
String inputMethod = request.getMethod();
|
||||
if (ObjectUtils.isEmpty(methods) && !RequestMethod.OPTIONS.name().equals(inputMethod)) {
|
||||
return true;
|
||||
}
|
||||
for (RequestMethod method : methods) {
|
||||
if (method.name().equals(request.getMethod())) {
|
||||
if (method.name().equals(inputMethod)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,27 +99,24 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi
|
||||
*/
|
||||
@Override
|
||||
public RequestMethodsRequestCondition getMatchingCondition(HttpServletRequest request) {
|
||||
if (this.methods.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
RequestMethod requestMethod = getRequestMethod(request);
|
||||
if (requestMethod != null) {
|
||||
for (RequestMethod method : this.methods) {
|
||||
if (method.equals(requestMethod)) {
|
||||
return new RequestMethodsRequestCondition(method);
|
||||
}
|
||||
}
|
||||
if (isHeadRequest(requestMethod) && getMethods().contains(RequestMethod.GET)) {
|
||||
return HEAD_CONDITION;
|
||||
if (requestMethod == null) {
|
||||
return null;
|
||||
}
|
||||
if (this.methods.isEmpty()) {
|
||||
return (RequestMethod.OPTIONS.equals(requestMethod) ? null : this);
|
||||
}
|
||||
for (RequestMethod method : this.methods) {
|
||||
if (method.equals(requestMethod)) {
|
||||
return new RequestMethodsRequestCondition(method);
|
||||
}
|
||||
}
|
||||
if (RequestMethod.HEAD.equals(requestMethod) && getMethods().contains(RequestMethod.GET)) {
|
||||
return HEAD_CONDITION;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isHeadRequest(RequestMethod requestMethod) {
|
||||
return (requestMethod != null && RequestMethod.HEAD.equals(requestMethod));
|
||||
}
|
||||
|
||||
private RequestMethod getRequestMethod(HttpServletRequest request) {
|
||||
try {
|
||||
return RequestMethod.valueOf(request.getMethod());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.web.servlet.mvc.method;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -29,6 +30,8 @@ import java.util.Set;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.InvalidMediaTypeException;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -56,6 +59,19 @@ import org.springframework.web.util.WebUtils;
|
||||
*/
|
||||
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
|
||||
|
||||
private static final Method HTTP_OPTIONS_HANDLE_METHOD;
|
||||
|
||||
static {
|
||||
try {
|
||||
HTTP_OPTIONS_HANDLE_METHOD = HttpOptionsHandler.class.getMethod("handle");
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
// Should never happen
|
||||
throw new IllegalStateException("No handler for HTTP OPTIONS", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected RequestMappingInfoHandlerMapping() {
|
||||
setHandlerMethodMappingNamingStrategy(new RequestMappingInfoHandlerMethodMappingNamingStrategy());
|
||||
}
|
||||
@@ -200,8 +216,14 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
if (patternMatches.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
else if (patternAndMethodMatches.isEmpty() && !allowedMethods.isEmpty()) {
|
||||
throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods);
|
||||
else if (patternAndMethodMatches.isEmpty()) {
|
||||
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
|
||||
HttpOptionsHandler handler = new HttpOptionsHandler(allowedMethods);
|
||||
return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD);
|
||||
}
|
||||
else if (!allowedMethods.isEmpty()) {
|
||||
throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods);
|
||||
}
|
||||
}
|
||||
|
||||
Set<MediaType> consumableMediaTypes;
|
||||
@@ -279,4 +301,40 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Default handler for HTTP OPTIONS.
|
||||
*/
|
||||
private static class HttpOptionsHandler {
|
||||
|
||||
private final HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
|
||||
public HttpOptionsHandler(Set<String> declaredMethods) {
|
||||
this.headers.setAllow(initAllowedHttpMethods(declaredMethods));
|
||||
}
|
||||
|
||||
private static Set<HttpMethod> initAllowedHttpMethods(Set<String> declaredMethods) {
|
||||
Set<HttpMethod> result = new LinkedHashSet<HttpMethod>(declaredMethods.size());
|
||||
if (declaredMethods.isEmpty()) {
|
||||
result.add(HttpMethod.GET);
|
||||
result.add(HttpMethod.HEAD);
|
||||
}
|
||||
else {
|
||||
boolean hasHead = declaredMethods.contains("HEAD");
|
||||
for (String method : declaredMethods) {
|
||||
result.add(HttpMethod.valueOf(method));
|
||||
if (!hasHead && "GET".equals(method)) {
|
||||
result.add(HttpMethod.HEAD);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public HttpHeaders handle() {
|
||||
return this.headers;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRange;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
@@ -111,7 +112,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
|
||||
|
||||
public ResourceHttpRequestHandler() {
|
||||
super(METHOD_GET, METHOD_HEAD);
|
||||
super(HttpMethod.GET.name(), HttpMethod.HEAD.name(), HttpMethod.OPTIONS.name());
|
||||
this.resourceResolvers.add(new PathResourceResolver());
|
||||
}
|
||||
|
||||
@@ -236,6 +237,11 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
return;
|
||||
}
|
||||
|
||||
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
|
||||
response.setHeader("Allow", "GET,HEAD");
|
||||
return;
|
||||
}
|
||||
|
||||
// Header phase
|
||||
if (new ServletWebRequest(request, response).checkNotModified(resource.lastModified())) {
|
||||
logger.trace("Resource not modified - returning 304");
|
||||
|
||||
Reference in New Issue
Block a user