Support "Accept-Patch" for OPTIONS requests

This commit introduces support in both servlet and webflux for the
"Accept-Patch" header in OPTIONS requests, as defined in section 3.1 of
RFC 5789.

See gh-26759
This commit is contained in:
Arjen Poutsma
2021-04-08 10:36:00 +02:00
parent 44e1d6d1bf
commit 97f3846971
5 changed files with 124 additions and 22 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 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.server.PathContainer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.result.condition.NameValueExpression;
@@ -173,7 +174,8 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
String httpMethod = request.getMethodValue();
Set<HttpMethod> methods = helper.getAllowedMethods();
if (HttpMethod.OPTIONS.matches(httpMethod)) {
HttpOptionsHandler handler = new HttpOptionsHandler(methods);
Set<MediaType> mediaTypes = helper.getConsumablePatchMediaTypes();
HttpOptionsHandler handler = new HttpOptionsHandler(methods, mediaTypes);
return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD);
}
throw new MethodNotAllowedException(httpMethod, methods);
@@ -301,6 +303,22 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
collect(Collectors.toList());
}
/**
* Return declared "consumable" types but only among those that have
* PATCH specified, or that have no methods at all.
*/
public Set<MediaType> getConsumablePatchMediaTypes() {
Set<MediaType> result = new LinkedHashSet<>();
for (PartialMatch match : this.partialMatches) {
Set<RequestMethod> methods = match.getInfo().getMethodsCondition().getMethods();
if (methods.isEmpty() || methods.contains(RequestMethod.PATCH)) {
result.addAll(match.getInfo().getConsumesCondition().getConsumableMediaTypes());
}
}
return result;
}
/**
* Container for a RequestMappingInfo that matches the URL path at least.
@@ -367,8 +385,9 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
private final HttpHeaders headers = new HttpHeaders();
public HttpOptionsHandler(Set<HttpMethod> declaredMethods) {
public HttpOptionsHandler(Set<HttpMethod> declaredMethods, Set<MediaType> acceptPatch) {
this.headers.setAllow(initAllowedHttpMethods(declaredMethods));
this.headers.setAcceptPatch(new ArrayList<>(acceptPatch));
}
private static Set<HttpMethod> initAllowedHttpMethods(Set<HttpMethod> declaredMethods) {