Add mutate() to RequestMappingInfo
Closes gh-26428
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -89,29 +89,27 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
|
||||
private final int hashCode;
|
||||
|
||||
private final BuilderConfiguration options;
|
||||
|
||||
|
||||
/**
|
||||
* Full constructor with a mapping name.
|
||||
* @deprecated as of 5.3.4 in favor using {@link RequestMappingInfo.Builder} via {@link #paths(String...)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public RequestMappingInfo(@Nullable String name, @Nullable PatternsRequestCondition patterns,
|
||||
@Nullable RequestMethodsRequestCondition methods, @Nullable ParamsRequestCondition params,
|
||||
@Nullable HeadersRequestCondition headers, @Nullable ConsumesRequestCondition consumes,
|
||||
@Nullable ProducesRequestCondition produces, @Nullable RequestCondition<?> custom) {
|
||||
|
||||
this.name = (StringUtils.hasText(name) ? name : null);
|
||||
this.patternsCondition = (patterns != null ? patterns : EMPTY_PATTERNS);
|
||||
this.methodsCondition = (methods != null ? methods : EMPTY_REQUEST_METHODS);
|
||||
this.paramsCondition = (params != null ? params : EMPTY_PARAMS);
|
||||
this.headersCondition = (headers != null ? headers : EMPTY_HEADERS);
|
||||
this.consumesCondition = (consumes != null ? consumes : EMPTY_CONSUMES);
|
||||
this.producesCondition = (produces != null ? produces : EMPTY_PRODUCES);
|
||||
this.customConditionHolder = (custom != null ? new RequestConditionHolder(custom) : EMPTY_CUSTOM);
|
||||
|
||||
this.hashCode = calculateHashCode(
|
||||
this.patternsCondition, this.methodsCondition, this.paramsCondition, this.headersCondition,
|
||||
this.consumesCondition, this.producesCondition, this.customConditionHolder);
|
||||
this(name, patterns, methods, params, headers, consumes, produces, custom, new BuilderConfiguration());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given request conditions.
|
||||
* Create an instance with the given conditions.
|
||||
* @deprecated as of 5.3.4 in favor using {@link RequestMappingInfo.Builder} via {@link #paths(String...)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public RequestMappingInfo(@Nullable PatternsRequestCondition patterns,
|
||||
@Nullable RequestMethodsRequestCondition methods, @Nullable ParamsRequestCondition params,
|
||||
@Nullable HeadersRequestCondition headers, @Nullable ConsumesRequestCondition consumes,
|
||||
@@ -122,12 +120,35 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
|
||||
/**
|
||||
* Re-create a RequestMappingInfo with the given custom request condition.
|
||||
* @deprecated since 5.3.4 in favor of using a {@link Builder} via {@link #mutate()}.
|
||||
*/
|
||||
@Deprecated
|
||||
public RequestMappingInfo(RequestMappingInfo info, @Nullable RequestCondition<?> customRequestCondition) {
|
||||
this(info.name, info.patternsCondition, info.methodsCondition, info.paramsCondition, info.headersCondition,
|
||||
info.consumesCondition, info.producesCondition, customRequestCondition);
|
||||
}
|
||||
|
||||
private RequestMappingInfo(@Nullable String name, @Nullable PatternsRequestCondition patterns,
|
||||
@Nullable RequestMethodsRequestCondition methods, @Nullable ParamsRequestCondition params,
|
||||
@Nullable HeadersRequestCondition headers, @Nullable ConsumesRequestCondition consumes,
|
||||
@Nullable ProducesRequestCondition produces, @Nullable RequestCondition<?> custom,
|
||||
BuilderConfiguration options) {
|
||||
|
||||
this.name = (StringUtils.hasText(name) ? name : null);
|
||||
this.patternsCondition = (patterns != null ? patterns : EMPTY_PATTERNS);
|
||||
this.methodsCondition = (methods != null ? methods : EMPTY_REQUEST_METHODS);
|
||||
this.paramsCondition = (params != null ? params : EMPTY_PARAMS);
|
||||
this.headersCondition = (headers != null ? headers : EMPTY_HEADERS);
|
||||
this.consumesCondition = (consumes != null ? consumes : EMPTY_CONSUMES);
|
||||
this.producesCondition = (produces != null ? produces : EMPTY_PRODUCES);
|
||||
this.customConditionHolder = (custom != null ? new RequestConditionHolder(custom) : EMPTY_CUSTOM);
|
||||
this.options = options;
|
||||
|
||||
this.hashCode = calculateHashCode(
|
||||
this.patternsCondition, this.methodsCondition, this.paramsCondition, this.headersCondition,
|
||||
this.consumesCondition, this.producesCondition, this.customConditionHolder);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the name for this mapping, or {@code null}.
|
||||
@@ -219,7 +240,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder);
|
||||
|
||||
return new RequestMappingInfo(name, patterns,
|
||||
methods, params, headers, consumes, produces, custom.getCondition());
|
||||
methods, params, headers, consumes, produces, custom.getCondition(), this.options);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -275,7 +296,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
return null;
|
||||
}
|
||||
return new RequestMappingInfo(this.name, patterns,
|
||||
methods, params, headers, consumes, produces, custom.getCondition());
|
||||
methods, params, headers, consumes, produces, custom.getCondition(), this.options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,6 +402,15 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a builder to create a new RequestMappingInfo by modifying this one.
|
||||
* @return a builder to create a new, modified instance
|
||||
* @since 5.3.4
|
||||
*/
|
||||
public Builder mutate() {
|
||||
return new MutateBuilder(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code RequestMappingInfo.Builder} with the given paths.
|
||||
* @param paths the paths to use
|
||||
@@ -546,7 +576,6 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
|
||||
@Override
|
||||
public RequestMappingInfo build() {
|
||||
|
||||
PathPatternParser parser = (this.options.getPatternParser() != null ?
|
||||
this.options.getPatternParser() : PathPatternParser.defaultInstance);
|
||||
|
||||
@@ -564,10 +593,11 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
null : new ConsumesRequestCondition(this.consumes, this.headers),
|
||||
ObjectUtils.isEmpty(this.produces) && !this.hasAccept ?
|
||||
null : new ProducesRequestCondition(this.produces, this.headers, contentTypeResolver),
|
||||
this.customCondition);
|
||||
this.customCondition,
|
||||
this.options);
|
||||
}
|
||||
|
||||
private static List<PathPattern> parse(String[] patterns, PathPatternParser parser) {
|
||||
static List<PathPattern> parse(String[] patterns, PathPatternParser parser) {
|
||||
if (isEmpty(patterns)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -581,7 +611,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isEmpty(String[] patterns) {
|
||||
static boolean isEmpty(String[] patterns) {
|
||||
if (!ObjectUtils.isEmpty(patterns)) {
|
||||
for (String pattern : patterns) {
|
||||
if (StringUtils.hasText(pattern)) {
|
||||
@@ -594,6 +624,113 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
}
|
||||
|
||||
|
||||
private static class MutateBuilder implements Builder {
|
||||
|
||||
@Nullable
|
||||
private String name;
|
||||
|
||||
@Nullable
|
||||
private PatternsRequestCondition patternsCondition;
|
||||
|
||||
private RequestMethodsRequestCondition methodsCondition;
|
||||
|
||||
private ParamsRequestCondition paramsCondition;
|
||||
|
||||
private HeadersRequestCondition headersCondition;
|
||||
|
||||
private ConsumesRequestCondition consumesCondition;
|
||||
|
||||
private ProducesRequestCondition producesCondition;
|
||||
|
||||
private RequestConditionHolder customConditionHolder;
|
||||
|
||||
private BuilderConfiguration options;
|
||||
|
||||
public MutateBuilder(RequestMappingInfo other) {
|
||||
this.name = other.name;
|
||||
this.patternsCondition = other.patternsCondition;
|
||||
this.methodsCondition = other.methodsCondition;
|
||||
this.paramsCondition = other.paramsCondition;
|
||||
this.headersCondition = other.headersCondition;
|
||||
this.consumesCondition = other.consumesCondition;
|
||||
this.producesCondition = other.producesCondition;
|
||||
this.customConditionHolder = other.customConditionHolder;
|
||||
this.options = other.options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder paths(String... paths) {
|
||||
PathPatternParser parser = (this.options.getPatternParser() != null ?
|
||||
this.options.getPatternParser() : PathPatternParser.defaultInstance);
|
||||
this.patternsCondition = (DefaultBuilder.isEmpty(paths) ?
|
||||
null : new PatternsRequestCondition(DefaultBuilder.parse(paths, parser)));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder methods(RequestMethod... methods) {
|
||||
this.methodsCondition = (ObjectUtils.isEmpty(methods) ?
|
||||
EMPTY_REQUEST_METHODS : new RequestMethodsRequestCondition(methods));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder params(String... params) {
|
||||
this.paramsCondition = (ObjectUtils.isEmpty(params) ?
|
||||
EMPTY_PARAMS : new ParamsRequestCondition(params));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder headers(String... headers) {
|
||||
this.headersCondition = (ObjectUtils.isEmpty(headers) ?
|
||||
EMPTY_HEADERS : new HeadersRequestCondition(headers));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder consumes(String... consumes) {
|
||||
this.consumesCondition = (ObjectUtils.isEmpty(consumes) ?
|
||||
EMPTY_CONSUMES : new ConsumesRequestCondition(consumes));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder produces(String... produces) {
|
||||
this.producesCondition = (ObjectUtils.isEmpty(produces) ?
|
||||
EMPTY_PRODUCES :
|
||||
new ProducesRequestCondition(produces, null, this.options.getContentTypeResolver()));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder mappingName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder customCondition(RequestCondition<?> condition) {
|
||||
this.customConditionHolder = new RequestConditionHolder(condition);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder options(BuilderConfiguration options) {
|
||||
this.options = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestMappingInfo build() {
|
||||
return new RequestMappingInfo(this.name, this.patternsCondition,
|
||||
this.methodsCondition, this.paramsCondition, this.headersCondition,
|
||||
this.consumesCondition, this.producesCondition,
|
||||
this.customConditionHolder, this.options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Container for configuration options used for request mapping purposes.
|
||||
* Such configuration is required to create RequestMappingInfo instances but
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -38,6 +38,7 @@ import org.springframework.web.util.pattern.PatternParseException;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||
import static org.springframework.web.reactive.result.method.RequestMappingInfo.paths;
|
||||
|
||||
/**
|
||||
@@ -292,11 +293,10 @@ public class RequestMappingInfoTests {
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void preFlightRequest() throws Exception {
|
||||
public void preFlightRequest() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.options("/foo")
|
||||
.header("Origin", "https://domain.com")
|
||||
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "POST")
|
||||
);
|
||||
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "POST"));
|
||||
|
||||
RequestMappingInfo info = paths("/foo").methods(RequestMethod.POST).build();
|
||||
RequestMappingInfo match = info.getMatchingCondition(exchange);
|
||||
@@ -307,4 +307,26 @@ public class RequestMappingInfoTests {
|
||||
assertThat(match).as("Pre-flight should match the ACCESS_CONTROL_REQUEST_METHOD").isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void mutate() {
|
||||
RequestMappingInfo.BuilderConfiguration options = new RequestMappingInfo.BuilderConfiguration();
|
||||
options.setPatternParser(new PathPatternParser());
|
||||
|
||||
RequestMappingInfo info1 = RequestMappingInfo.paths("/foo")
|
||||
.methods(GET).headers("h1=hv1").params("q1=qv1")
|
||||
.consumes("application/json").produces("application/json")
|
||||
.mappingName("testMapping").options(options)
|
||||
.build();
|
||||
|
||||
RequestMappingInfo info2 = info1.mutate().produces("application/hal+json").build();
|
||||
|
||||
assertThat(info2.getName()).isEqualTo(info1.getName());
|
||||
assertThat(info2.getPatternsCondition()).isEqualTo(info1.getPatternsCondition());
|
||||
assertThat(info2.getHeadersCondition()).isEqualTo(info1.getHeadersCondition());
|
||||
assertThat(info2.getParamsCondition()).isEqualTo(info1.getParamsCondition());
|
||||
assertThat(info2.getConsumesCondition()).isEqualTo(info1.getConsumesCondition());
|
||||
assertThat(info2.getProducesCondition().getProducibleMediaTypes())
|
||||
.containsOnly(MediaType.parseMediaType("application/hal+json"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user