Skip to content

Commit ae20a15

Browse files
committed
Provide a preprocessor that can modify existing headers
1 parent 0ad6330 commit ae20a15

File tree

5 files changed

+405
-0
lines changed

5 files changed

+405
-0
lines changed

docs/src/docs/asciidoc/customizing-requests-and-responses.adoc

+7
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ Any occurrences that match a regular expression are replaced.
125125
126126
127127
128+
[[customizing-requests-and-responses-preprocessors-modify-headers]]
129+
==== Modifying Headers
130+
131+
You can use `modifyHeaders` on `Preprocessors` to add, set, and remove request or response headers.
132+
133+
134+
128135
[[customizing-requests-and-responses-preprocessors-modify-request-parameters]]
129136
==== Modifying Request Parameters
130137

spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeaderRemovingOperationPreprocessor.java

+2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
* against the headers found
3232
*
3333
* @author Andy Wilkinson
34+
* @deprecated Use {@link HeadersModifyingOperationPreprocessor} instead
3435
*/
36+
@Deprecated
3537
class HeaderRemovingOperationPreprocessor implements OperationPreprocessor {
3638

3739
private final OperationRequestFactory requestFactory = new OperationRequestFactory();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
/*
2+
* Copyright 2014-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.restdocs.operation.preprocess;
18+
19+
import java.util.ArrayList;
20+
import java.util.Arrays;
21+
import java.util.List;
22+
import java.util.regex.Matcher;
23+
import java.util.regex.Pattern;
24+
25+
import org.springframework.http.HttpHeaders;
26+
import org.springframework.restdocs.operation.OperationRequest;
27+
import org.springframework.restdocs.operation.OperationRequestFactory;
28+
import org.springframework.restdocs.operation.OperationResponse;
29+
import org.springframework.restdocs.operation.OperationResponseFactory;
30+
import org.springframework.util.Assert;
31+
32+
/**
33+
* An {@link OperationPreprocessor} that can be used to modify a request's
34+
* {@link OperationRequest#getHeaders()} by adding, setting, and removing headers.
35+
*
36+
* @author Jihoon Cha
37+
*/
38+
public class HeadersModifyingOperationPreprocessor implements OperationPreprocessor {
39+
40+
private final OperationRequestFactory requestFactory = new OperationRequestFactory();
41+
42+
private final OperationResponseFactory responseFactory = new OperationResponseFactory();
43+
44+
private final List<Modification> modifications = new ArrayList<>();
45+
46+
@Override
47+
public OperationRequest preprocess(OperationRequest request) {
48+
HttpHeaders headers = copyHttpHeaders(request.getHeaders());
49+
for (Modification modification : this.modifications) {
50+
modification.applyTo(headers);
51+
}
52+
return this.requestFactory.createFrom(request, headers);
53+
}
54+
55+
@Override
56+
public OperationResponse preprocess(OperationResponse response) {
57+
HttpHeaders headers = copyHttpHeaders(response.getHeaders());
58+
for (Modification modification : this.modifications) {
59+
modification.applyTo(headers);
60+
}
61+
return this.responseFactory.createFrom(response, headers);
62+
}
63+
64+
private HttpHeaders copyHttpHeaders(HttpHeaders headers) {
65+
HttpHeaders copy = new HttpHeaders();
66+
for (String name : headers.keySet()) {
67+
List<String> values = headers.get(name);
68+
if (values == null) {
69+
continue;
70+
}
71+
copy.put(name, new ArrayList<>(values));
72+
}
73+
return copy;
74+
}
75+
76+
/**
77+
* Adds a header with the given {@code name} and {@code value}.
78+
* @param name the name
79+
* @param value the value
80+
* @return {@code this}
81+
*/
82+
public HeadersModifyingOperationPreprocessor add(String name, String value) {
83+
this.modifications.add(new AddHeaderModification(name, value));
84+
return this;
85+
}
86+
87+
/**
88+
* Sets the header with the given {@code name} to have the given {@code values}.
89+
* @param name the name
90+
* @param values the values
91+
* @return {@code this}
92+
*/
93+
public HeadersModifyingOperationPreprocessor set(String name, String... values) {
94+
Assert.notEmpty(values, "At least one value must be provided");
95+
this.modifications.add(new SetHeaderModification(name, Arrays.asList(values)));
96+
return this;
97+
}
98+
99+
/**
100+
* Removes the header with the given {@code name}.
101+
* @param name the name of the parameter
102+
* @return {@code this}
103+
*/
104+
public HeadersModifyingOperationPreprocessor remove(String name) {
105+
this.modifications.add(new RemoveHeaderModification(name));
106+
return this;
107+
}
108+
109+
/**
110+
* Removes the given {@code value} from the header with the given {@code name}.
111+
* @param name the name
112+
* @param value the value
113+
* @return {@code this}
114+
*/
115+
public HeadersModifyingOperationPreprocessor remove(String name, String value) {
116+
this.modifications.add(new RemoveValueHeaderModification(name, value));
117+
return this;
118+
}
119+
120+
/**
121+
* Remove headers that match the given {@code namePattern} regular expression.
122+
* @param namePattern the name pattern
123+
* @return {@code this}
124+
* @see Matcher#matches()
125+
*/
126+
public HeadersModifyingOperationPreprocessor remove(Pattern namePattern) {
127+
this.modifications.add(new RemoveHeadersByNamePatternModification(namePattern));
128+
return this;
129+
}
130+
131+
private interface Modification {
132+
133+
void applyTo(HttpHeaders headers);
134+
135+
}
136+
137+
private static final class AddHeaderModification implements Modification {
138+
139+
private final String name;
140+
141+
private final String value;
142+
143+
private AddHeaderModification(String name, String value) {
144+
this.name = name;
145+
this.value = value;
146+
}
147+
148+
@Override
149+
public void applyTo(HttpHeaders headers) {
150+
headers.add(this.name, this.value);
151+
}
152+
153+
}
154+
155+
private static final class SetHeaderModification implements Modification {
156+
157+
private final String name;
158+
159+
private final List<String> values;
160+
161+
private SetHeaderModification(String name, List<String> values) {
162+
this.name = name;
163+
this.values = values;
164+
}
165+
166+
@Override
167+
public void applyTo(HttpHeaders headers) {
168+
headers.put(this.name, this.values);
169+
}
170+
171+
}
172+
173+
private static final class RemoveHeaderModification implements Modification {
174+
175+
private final String name;
176+
177+
private RemoveHeaderModification(String name) {
178+
this.name = name;
179+
}
180+
181+
@Override
182+
public void applyTo(HttpHeaders headers) {
183+
headers.remove(this.name);
184+
}
185+
186+
}
187+
188+
private static final class RemoveValueHeaderModification implements Modification {
189+
190+
private final String name;
191+
192+
private final String value;
193+
194+
private RemoveValueHeaderModification(String name, String value) {
195+
this.name = name;
196+
this.value = value;
197+
}
198+
199+
@Override
200+
public void applyTo(HttpHeaders headers) {
201+
List<String> values = headers.get(this.name);
202+
if (values != null) {
203+
values.remove(this.value);
204+
if (values.isEmpty()) {
205+
headers.remove(this.name);
206+
}
207+
}
208+
}
209+
210+
}
211+
212+
private static final class RemoveHeadersByNamePatternModification implements Modification {
213+
214+
private final Pattern namePattern;
215+
216+
private RemoveHeadersByNamePatternModification(Pattern namePattern) {
217+
this.namePattern = namePattern;
218+
}
219+
220+
@Override
221+
public void applyTo(HttpHeaders headers) {
222+
headers.keySet().removeIf((name) -> this.namePattern.matcher(name).matches());
223+
}
224+
225+
}
226+
227+
}

spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/Preprocessors.java

+14
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
*
3232
* @author Andy Wilkinson
3333
* @author Roland Huss
34+
* @author Jihoon Cha
3435
*/
3536
public final class Preprocessors {
3637

@@ -73,8 +74,10 @@ public static OperationPreprocessor prettyPrint() {
7374
* {@code headersToRemove}.
7475
* @param headerNames the header names
7576
* @return the preprocessor
77+
* @deprecated Use {@link #modifyHeaders()} instead
7678
* @see String#equals(Object)
7779
*/
80+
@Deprecated
7881
public static OperationPreprocessor removeHeaders(String... headerNames) {
7982
return new HeaderRemovingOperationPreprocessor(new ExactMatchHeaderFilter(headerNames));
8083
}
@@ -85,8 +88,10 @@ public static OperationPreprocessor removeHeaders(String... headerNames) {
8588
* {@code headerNamePatterns} regular expressions.
8689
* @param headerNamePatterns the header name patterns
8790
* @return the preprocessor
91+
* @deprecated Use {@link #modifyHeaders()} instead
8892
* @see java.util.regex.Matcher#matches()
8993
*/
94+
@Deprecated
9095
public static OperationPreprocessor removeMatchingHeaders(String... headerNamePatterns) {
9196
return new HeaderRemovingOperationPreprocessor(new PatternMatchHeaderFilter(headerNamePatterns));
9297
}
@@ -132,6 +137,15 @@ public static ParametersModifyingOperationPreprocessor modifyParameters() {
132137
return new ParametersModifyingOperationPreprocessor();
133138
}
134139

140+
/**
141+
* Returns a {@code HeadersModifyingOperationPreprocessor} that can then be configured
142+
* to modify the headers of the request.
143+
* @return the preprocessor
144+
*/
145+
public static HeadersModifyingOperationPreprocessor modifyHeaders() {
146+
return new HeadersModifyingOperationPreprocessor();
147+
}
148+
135149
/**
136150
* Returns a {@code UriModifyingOperationPreprocessor} that will modify URIs in the
137151
* request or response by changing one or more of their host, scheme, and port.

0 commit comments

Comments
 (0)