From 535bea24f915e87892d213eaa3f0223dddef0392 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 28 Sep 2015 15:16:20 +0100 Subject: [PATCH] Improve handling of Host header so a preprocessor can remove it Previously, the Host header was treated specially in the HTTP request snippet. If no Host header was specified, one would always be added prior to producing the snippet. This ensured that the snippet was valid (an HTTP 1.1 request must include a Host header), but came at the cost of some confusion about why a preprocessor could not remove it. This commit updates the special treatment of the Host header so that it's now performed in a central location so that all of the snippets can benefit from a Host header being added if one isn't provided. The handling of the Content-Length header has also been reworked so that it's performed in the same location. The curl request snippet has been updated so that it doesn't include setting the Host header on the command line; it's unnecessary as curl will automatically include a Host header in the request. The documentation of the Host header's special treatment (made in 2fc0420) that noted that it was unaffected by preprocessing has been reverted. Closes gh-134 --- .../customizing-requests-and-responses.adoc | 4 - .../docs/asciidoc/documenting-your-api.adoc | 3 +- .../restdocs/curl/CurlRequestSnippet.java | 86 +++++++++++++---- .../restdocs/http/HttpRequestSnippet.java | 7 -- .../operation/AbstractOperationMessage.java | 13 +-- .../restdocs/operation/HttpHeadersHelper.java | 66 +++++++++++++ .../operation/OperationRequestFactory.java | 96 +++++++++++++++++++ .../OperationRequestPartFactory.java | 49 ++++++++++ .../operation/OperationResponseFactory.java | 85 ++++++++++++++++ .../operation/StandardOperationRequest.java | 4 +- .../StandardOperationRequestPart.java | 6 +- .../operation/StandardOperationResponse.java | 5 +- ...ContentModifyingOperationPreprocessor.java | 29 ++---- .../HeaderRemovingOperationPreprocessor.java | 17 ++-- .../http/HttpRequestSnippetTests.java | 15 ++- .../ContentTypeLinkExtractorTests.java | 11 ++- .../LinkExtractorsPayloadTests.java | 6 +- ...ntModifyingOperationPreprocessorTests.java | 14 ++- ...derRemovingOperationPreprocessorTests.java | 16 +++- .../restdocs/test/OperationBuilder.java | 13 +-- .../MockMvcOperationRequestFactory.java | 28 +++--- .../MockMvcOperationResponseFactory.java | 6 +- ...kMvcRestDocumentationIntegrationTests.java | 26 +++-- 23 files changed, 466 insertions(+), 139 deletions(-) create mode 100644 spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/HttpHeadersHelper.java create mode 100644 spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestFactory.java create mode 100644 spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestPartFactory.java create mode 100644 spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseFactory.java diff --git a/docs/src/docs/asciidoc/customizing-requests-and-responses.adoc b/docs/src/docs/asciidoc/customizing-requests-and-responses.adoc index d88fb06d..f88a5879 100644 --- a/docs/src/docs/asciidoc/customizing-requests-and-responses.adoc +++ b/docs/src/docs/asciidoc/customizing-requests-and-responses.adoc @@ -77,10 +77,6 @@ different replacement can also be specified if you wish. `removeHeaders` on `Preprocessors` removes any occurrences of the named headers from the request or response. -NOTE: For an HTTP 1.1 request to be valid it must contain a `Host` header. Therefore, -irrespective of any preprocessing, the default HTTP request snippet will always contain a -`Host` header. - [[customizing-requests-and-responses-preprocessors-replace-patterns]] diff --git a/docs/src/docs/asciidoc/documenting-your-api.adoc b/docs/src/docs/asciidoc/documenting-your-api.adoc index e0e477f3..7fd84cdf 100644 --- a/docs/src/docs/asciidoc/documenting-your-api.adoc +++ b/docs/src/docs/asciidoc/documenting-your-api.adoc @@ -388,8 +388,7 @@ call that is being documented | `http-request.adoc` | Contains the HTTP request that is equivalent to the `MockMvc` call that is being - documented. HTTP 1.1 requires a `Host` header. If you do not provide one via the - `MockMvc` API the snippet will add one automatically. +documented | `http-response.adoc` | Contains the HTTP response that was returned diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/curl/CurlRequestSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/curl/CurlRequestSnippet.java index 4d1ce446..73829f24 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/curl/CurlRequestSnippet.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/curl/CurlRequestSnippet.java @@ -18,10 +18,13 @@ package org.springframework.restdocs.curl; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -43,6 +46,15 @@ import org.springframework.util.StringUtils; */ public class CurlRequestSnippet extends TemplatedSnippet { + private static final Set HEADER_FILTERS; + static { + Set headerFilters = new HashSet(); + headerFilters.add(new NamedHeaderFilter(HttpHeaders.HOST)); + headerFilters.add(new NamedHeaderFilter(HttpHeaders.CONTENT_LENGTH)); + headerFilters.add(new BasicAuthHeaderFilter()); + HEADER_FILTERS = Collections.unmodifiableSet(headerFilters); + } + /** * Creates a new {@code CurlRequestSnippet} with no additional attributes. */ @@ -76,9 +88,9 @@ public class CurlRequestSnippet extends TemplatedSnippet { StringWriter command = new StringWriter(); PrintWriter printer = new PrintWriter(command); writeIncludeHeadersInOutputOption(printer); - HttpHeaders headers = writeUserOptionIfNecessary(operation.getRequest(), printer); + writeUserOptionIfNecessary(operation.getRequest(), printer); writeHttpMethodIfNecessary(operation.getRequest(), printer); - writeHeaders(headers, printer); + writeHeaders(operation.getRequest().getHeaders(), printer); writePartsIfNecessary(operation.getRequest(), printer); writeContent(operation.getRequest(), printer); @@ -89,22 +101,12 @@ public class CurlRequestSnippet extends TemplatedSnippet { writer.print("-i"); } - private HttpHeaders writeUserOptionIfNecessary(OperationRequest request, - PrintWriter writer) { - HttpHeaders headers = new HttpHeaders(); - headers.putAll(request.getHeaders()); - String authorization = headers.getFirst(HttpHeaders.AUTHORIZATION); - if (isAuthorizationBasicHeader(authorization)) { - String credentials = new String(Base64Utils.decodeFromString(authorization - .substring(5).trim())); + private void writeUserOptionIfNecessary(OperationRequest request, PrintWriter writer) { + List headerValue = request.getHeaders().get(HttpHeaders.AUTHORIZATION); + if (BasicAuthHeaderFilter.isBasicAuthHeader(headerValue)) { + String credentials = BasicAuthHeaderFilter.decodeBasicAuthHeader(headerValue); writer.print(String.format(" -u '%s'", credentials)); - headers.remove(HttpHeaders.AUTHORIZATION); } - return headers; - } - - private boolean isAuthorizationBasicHeader(String header) { - return header != null && header.startsWith("Basic"); } private void writeHttpMethodIfNecessary(OperationRequest request, PrintWriter writer) { @@ -115,7 +117,7 @@ public class CurlRequestSnippet extends TemplatedSnippet { private void writeHeaders(HttpHeaders headers, PrintWriter writer) { for (Entry> entry : headers.entrySet()) { - if (!HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(entry.getKey())) { + if (allowedHeader(entry)) { for (String header : entry.getValue()) { writer.print(String.format(" -H '%s: %s'", entry.getKey(), header)); } @@ -123,6 +125,15 @@ public class CurlRequestSnippet extends TemplatedSnippet { } } + private boolean allowedHeader(Entry> header) { + for (HeaderFilter headerFilter : HEADER_FILTERS) { + if (!headerFilter.allow(header.getKey(), header.getValue())) { + return false; + } + } + return true; + } + private void writePartsIfNecessary(OperationRequest request, PrintWriter writer) { for (OperationRequestPart part : request.getParts()) { writer.printf(" -F '%s=", part.getName()); @@ -166,4 +177,45 @@ public class CurlRequestSnippet extends TemplatedSnippet { || HttpMethod.POST.equals(request.getMethod()); } + private interface HeaderFilter { + + boolean allow(String name, List value); + } + + private static final class BasicAuthHeaderFilter implements HeaderFilter { + + @Override + public boolean allow(String name, List value) { + if (HttpHeaders.AUTHORIZATION.equals(name) && isBasicAuthHeader(value)) { + return false; + } + return true; + } + + static boolean isBasicAuthHeader(List value) { + return value != null && (!value.isEmpty()) + && value.get(0).startsWith("Basic "); + } + + static String decodeBasicAuthHeader(List value) { + return new String(Base64Utils.decodeFromString(value.get(0).substring(6))); + } + + } + + private static final class NamedHeaderFilter implements HeaderFilter { + + private final String name; + + private NamedHeaderFilter(String name) { + this.name = name; + } + + @Override + public boolean allow(String name, List value) { + return !this.name.equalsIgnoreCase(name); + } + + } + } diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpRequestSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpRequestSnippet.java index 9b73fde4..9060ada9 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpRequestSnippet.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/http/HttpRequestSnippet.java @@ -79,9 +79,6 @@ public class HttpRequestSnippet extends TemplatedSnippet { private List> getHeaders(OperationRequest request) { List> headers = new ArrayList<>(); - if (requiresHostHeader(request)) { - headers.add(header(HttpHeaders.HOST, request.getUri().getHost())); - } for (Entry> header : request.getHeaders().entrySet()) { for (String value : header.getValue()) { @@ -170,10 +167,6 @@ public class HttpRequestSnippet extends TemplatedSnippet { writer.printf("--%s--", MULTIPART_BOUNDARY); } - private boolean requiresHostHeader(OperationRequest request) { - return request.getHeaders().get(HttpHeaders.HOST) == null; - } - private boolean requiresFormEncodingContentTypeHeader(OperationRequest request) { return request.getHeaders().get(HttpHeaders.CONTENT_TYPE) == null && isPutOrPost(request) && !request.getParameters().isEmpty(); diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/AbstractOperationMessage.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/AbstractOperationMessage.java index d111f321..c5ede617 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/AbstractOperationMessage.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/AbstractOperationMessage.java @@ -35,18 +35,7 @@ abstract class AbstractOperationMessage { AbstractOperationMessage(byte[] content, HttpHeaders headers) { this.content = content == null ? new byte[0] : content; - this.headers = createHeaders(content, headers); - } - - private static HttpHeaders createHeaders(byte[] content, HttpHeaders input) { - HttpHeaders headers = new HttpHeaders(); - if (input != null) { - headers.putAll(input); - } - if (content != null && content.length > 0 && headers.getContentLength() == -1) { - headers.setContentLength(content.length); - } - return headers; + this.headers = headers; } public byte[] getContent() { diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/HttpHeadersHelper.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/HttpHeadersHelper.java new file mode 100644 index 00000000..a9cb7c5a --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/HttpHeadersHelper.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014-2015 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 + * + * http://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.restdocs.operation; + +import org.springframework.http.HttpHeaders; + +/** + * Helper for working with {@link HttpHeaders}. + * + * @author Andy Wilkinson + */ +class HttpHeadersHelper { + + private final HttpHeaders httpHeaders; + + HttpHeadersHelper(HttpHeaders httpHeaders) { + HttpHeaders headers = new HttpHeaders(); + if (httpHeaders != null) { + headers.putAll(httpHeaders); + } + this.httpHeaders = headers; + } + + HttpHeadersHelper addIfAbsent(String name, String value) { + if (this.httpHeaders.get(name) == null) { + this.httpHeaders.add(name, value); + } + return this; + } + + HttpHeadersHelper updateContentLengthHeaderIfPresent(byte[] content) { + if (this.httpHeaders.getContentLength() != -1) { + setContentLengthHeader(content); + } + return this; + } + + HttpHeadersHelper setContentLengthHeader(byte[] content) { + if (content == null || content.length == 0) { + this.httpHeaders.remove(HttpHeaders.CONTENT_LENGTH); + } + else { + this.httpHeaders.setContentLength(content.length); + } + return this; + } + + HttpHeaders getHeaders() { + return HttpHeaders.readOnlyHttpHeaders(this.httpHeaders); + } + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestFactory.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestFactory.java new file mode 100644 index 00000000..693be1dd --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestFactory.java @@ -0,0 +1,96 @@ +/* + * Copyright 2014-2015 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 + * + * http://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.restdocs.operation; + +import java.net.URI; +import java.util.Collection; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; + +/** + * A factory for creating {@link OperationRequest OperationRequests}. + * + * @author Andy Wilkinson + */ +public class OperationRequestFactory { + + /** + * Creates a new {@link OperationRequest}. The given {@code headers} will be augmented + * to ensure that they always include a {@code Content-Length} header if the request + * has any content and a {@code Host} header. + * + * @param uri the request's uri + * @param method the request method + * @param content the content of the request + * @param headers the request's headers + * @param parameters the request's parameters + * @param parts the request's parts + * @return the {@code OperationRequest} + */ + public OperationRequest create(URI uri, HttpMethod method, byte[] content, + HttpHeaders headers, Parameters parameters, + Collection parts) { + return new StandardOperationRequest(uri, method, content, augmentHeaders(headers, + uri, content), parameters, parts); + } + + /** + * Creates a new {@code OperationRequest} based on the given {@code original} but with + * the given {@code newContent}. If the original request had a {@code Content-Length} + * header it will be modified to match the length of the new content. + * + * @param original The original request + * @param newContent The new content + * + * @return The new request with the new content + */ + public OperationRequest createFrom(OperationRequest original, byte[] newContent) { + return new StandardOperationRequest(original.getUri(), original.getMethod(), + newContent, getUpdatedHeaders(original.getHeaders(), newContent), + original.getParameters(), original.getParts()); + } + + /** + * Creates a new {@code OperationRequest} based on the given {@code original} but with + * the given {@code newHeaders}. + * + * @param original The original request + * @param newHeaders The new headers + * + * @return The new request with the new content + */ + public OperationRequest createFrom(OperationRequest original, HttpHeaders newHeaders) { + return new StandardOperationRequest(original.getUri(), original.getMethod(), + original.getContent(), newHeaders, original.getParameters(), + original.getParts()); + } + + private HttpHeaders augmentHeaders(HttpHeaders originalHeaders, URI uri, + byte[] content) { + return new HttpHeadersHelper(originalHeaders) + .addIfAbsent(HttpHeaders.HOST, uri.getHost()) + .setContentLengthHeader(content).getHeaders(); + } + + private HttpHeaders getUpdatedHeaders(HttpHeaders originalHeaders, + byte[] updatedContent) { + return new HttpHeadersHelper(originalHeaders).updateContentLengthHeaderIfPresent( + updatedContent).getHeaders(); + } + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestPartFactory.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestPartFactory.java new file mode 100644 index 00000000..47aa7e43 --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestPartFactory.java @@ -0,0 +1,49 @@ +/* + * Copyright 2014-2015 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 + * + * http://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.restdocs.operation; + +import org.springframework.http.HttpHeaders; + +/** + * A factory for creating {@link OperationRequestPart OperationRequestParts}. + * + * @author Andy Wilkinson + */ +public class OperationRequestPartFactory { + + /** + * Creates a new {@link OperationRequestPart}. The given {@code headers} will be + * augmented to ensure that they always include a {@code Content-Length} header if the + * part has any content. + * + * @param name the name of the part + * @param submittedFileName the name of the file being submitted by the part + * @param content the content of the part + * @param headers the headers of the part + * @return the {@code OperationRequestPart} + */ + public OperationRequestPart create(String name, String submittedFileName, + byte[] content, HttpHeaders headers) { + return new StandardOperationRequestPart(name, submittedFileName, content, + augmentHeaders(headers, content)); + } + + private HttpHeaders augmentHeaders(HttpHeaders input, byte[] content) { + return new HttpHeadersHelper(input).setContentLengthHeader(content).getHeaders(); + } + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseFactory.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseFactory.java new file mode 100644 index 00000000..c3417c9f --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseFactory.java @@ -0,0 +1,85 @@ +/* + * Copyright 2014-2015 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 + * + * http://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.restdocs.operation; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; + +/** + * A factory for creating {@link OperationResponse OperationResponses}. + * + * @author Andy Wilkinson + */ +public class OperationResponseFactory { + + /** + * Creates a new {@link OperationResponse}. If the response has any content, the given + * {@code headers} will be augmented to ensure that they include a + * {@code Content-Length} header. + * + * @param status the status of the response + * @param headers the request's headers + * @param content the content of the request + * @return the {@code OperationResponse} + */ + public OperationResponse create(HttpStatus status, HttpHeaders headers, byte[] content) { + return new StandardOperationResponse(status, augmentHeaders(headers, content), + content); + } + + /** + * Creates a new {@code OperationResponse} based on the given {@code original} but + * with the given {@code newContent}. If the original response had a + * {@code Content-Length} header it will be modified to match the length of the new + * content. + * + * @param original The original response + * @param newContent The new content + * + * @return The new response with the new content + */ + public OperationResponse createFrom(OperationResponse original, byte[] newContent) { + return new StandardOperationResponse(original.getStatus(), getUpdatedHeaders( + original.getHeaders(), newContent), newContent); + } + + /** + * Creates a new {@code OperationResponse} based on the given {@code original} but + * with the given {@code newHeaders}. + * + * @param original The original response + * @param newHeaders The new headers + * + * @return The new response with the new headers + */ + public OperationResponse createFrom(OperationResponse original, HttpHeaders newHeaders) { + return new StandardOperationResponse(original.getStatus(), newHeaders, + original.getContent()); + } + + private HttpHeaders augmentHeaders(HttpHeaders originalHeaders, byte[] content) { + return new HttpHeadersHelper(originalHeaders).setContentLengthHeader(content) + .getHeaders(); + } + + private HttpHeaders getUpdatedHeaders(HttpHeaders originalHeaders, + byte[] updatedContent) { + return new HttpHeadersHelper(originalHeaders).updateContentLengthHeaderIfPresent( + updatedContent).getHeaders(); + } + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequest.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequest.java index c869100b..305d7b2a 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequest.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequest.java @@ -28,7 +28,7 @@ import org.springframework.http.HttpMethod; * * @author Andy Wilkinson */ -public class StandardOperationRequest extends AbstractOperationMessage implements +class StandardOperationRequest extends AbstractOperationMessage implements OperationRequest { private HttpMethod method; @@ -50,7 +50,7 @@ public class StandardOperationRequest extends AbstractOperationMessage implement * @param parameters the parameters * @param parts the parts */ - public StandardOperationRequest(URI uri, HttpMethod method, byte[] content, + StandardOperationRequest(URI uri, HttpMethod method, byte[] content, HttpHeaders headers, Parameters parameters, Collection parts) { super(content, headers); diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequestPart.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequestPart.java index c0f6e891..8ea231a4 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequestPart.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationRequestPart.java @@ -23,7 +23,7 @@ import org.springframework.http.HttpHeaders; * * @author Andy Wilkinson */ -public class StandardOperationRequestPart extends AbstractOperationMessage implements +class StandardOperationRequestPart extends AbstractOperationMessage implements OperationRequestPart { private final String name; @@ -38,8 +38,8 @@ public class StandardOperationRequestPart extends AbstractOperationMessage imple * @param content the contents of the part * @param headers the headers of the part */ - public StandardOperationRequestPart(String name, String submittedFileName, - byte[] content, HttpHeaders headers) { + StandardOperationRequestPart(String name, String submittedFileName, byte[] content, + HttpHeaders headers) { super(content, headers); this.name = name; this.submittedFileName = submittedFileName; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationResponse.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationResponse.java index 1e42beb2..32dfa57f 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationResponse.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/StandardOperationResponse.java @@ -24,7 +24,7 @@ import org.springframework.http.HttpStatus; * * @author Andy Wilkinson */ -public class StandardOperationResponse extends AbstractOperationMessage implements +class StandardOperationResponse extends AbstractOperationMessage implements OperationResponse { private final HttpStatus status; @@ -37,8 +37,7 @@ public class StandardOperationResponse extends AbstractOperationMessage implemen * @param headers the headers of the response * @param content the content of the response */ - public StandardOperationResponse(HttpStatus status, HttpHeaders headers, - byte[] content) { + StandardOperationResponse(HttpStatus status, HttpHeaders headers, byte[] content) { super(content, headers); this.status = status; } diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessor.java index 5cc31561..60c704aa 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessor.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessor.java @@ -16,11 +16,10 @@ package org.springframework.restdocs.operation.preprocess; -import org.springframework.http.HttpHeaders; import org.springframework.restdocs.operation.OperationRequest; +import org.springframework.restdocs.operation.OperationRequestFactory; import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.StandardOperationRequest; -import org.springframework.restdocs.operation.StandardOperationResponse; +import org.springframework.restdocs.operation.OperationResponseFactory; /** * An {@link OperationPreprocessor} that applies a {@link ContentModifier} to the content @@ -30,6 +29,10 @@ import org.springframework.restdocs.operation.StandardOperationResponse; */ public class ContentModifyingOperationPreprocessor implements OperationPreprocessor { + private final OperationRequestFactory requestFactory = new OperationRequestFactory(); + + private final OperationResponseFactory responseFactory = new OperationResponseFactory(); + private final ContentModifier contentModifier; /** @@ -46,30 +49,14 @@ public class ContentModifyingOperationPreprocessor implements OperationPreproces public OperationRequest preprocess(OperationRequest request) { byte[] modifiedContent = this.contentModifier.modifyContent(request.getContent(), request.getHeaders().getContentType()); - return new StandardOperationRequest(request.getUri(), request.getMethod(), - modifiedContent, - getUpdatedHeaders(request.getHeaders(), modifiedContent), - request.getParameters(), request.getParts()); + return this.requestFactory.createFrom(request, modifiedContent); } @Override public OperationResponse preprocess(OperationResponse response) { byte[] modifiedContent = this.contentModifier.modifyContent( response.getContent(), response.getHeaders().getContentType()); - return new StandardOperationResponse(response.getStatus(), getUpdatedHeaders( - response.getHeaders(), modifiedContent), modifiedContent); - } - - private HttpHeaders getUpdatedHeaders(HttpHeaders headers, byte[] updatedContent) { - HttpHeaders updatedHeaders = new HttpHeaders(); - updatedHeaders.putAll(headers); - if (updatedContent.length > 0) { - updatedHeaders.setContentLength(updatedContent.length); - } - else { - updatedHeaders.remove(HttpHeaders.CONTENT_LENGTH); - } - return updatedHeaders; + return this.responseFactory.createFrom(response, modifiedContent); } } diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeaderRemovingOperationPreprocessor.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeaderRemovingOperationPreprocessor.java index 36eba207..7766af57 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeaderRemovingOperationPreprocessor.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/preprocess/HeaderRemovingOperationPreprocessor.java @@ -22,9 +22,9 @@ import java.util.Set; import org.springframework.http.HttpHeaders; import org.springframework.restdocs.operation.OperationRequest; +import org.springframework.restdocs.operation.OperationRequestFactory; import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.StandardOperationRequest; -import org.springframework.restdocs.operation.StandardOperationResponse; +import org.springframework.restdocs.operation.OperationResponseFactory; /** * An {@link OperationPreprocessor} that removes headers. @@ -33,6 +33,10 @@ import org.springframework.restdocs.operation.StandardOperationResponse; */ class HeaderRemovingOperationPreprocessor implements OperationPreprocessor { + private final OperationRequestFactory requestFactory = new OperationRequestFactory(); + + private final OperationResponseFactory responseFactory = new OperationResponseFactory(); + private final Set headersToRemove; HeaderRemovingOperationPreprocessor(String... headersToRemove) { @@ -41,15 +45,14 @@ class HeaderRemovingOperationPreprocessor implements OperationPreprocessor { @Override public OperationResponse preprocess(OperationResponse response) { - return new StandardOperationResponse(response.getStatus(), - removeHeaders(response.getHeaders()), response.getContent()); + return this.responseFactory.createFrom(response, + removeHeaders(response.getHeaders())); } @Override public OperationRequest preprocess(OperationRequest request) { - return new StandardOperationRequest(request.getUri(), request.getMethod(), - request.getContent(), removeHeaders(request.getHeaders()), - request.getParameters(), request.getParts()); + return this.requestFactory.createFrom(request, + removeHeaders(request.getHeaders())); } private HttpHeaders removeHeaders(HttpHeaders originalHeaders) { diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java index 9ccb89f1..f948fb10 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/http/HttpRequestSnippetTests.java @@ -54,8 +54,8 @@ public class HttpRequestSnippetTests { @Test public void getRequest() throws IOException { this.snippet.expectHttpRequest("get-request").withContents( - httpRequest(RequestMethod.GET, "/foo").header(HttpHeaders.HOST, - "localhost").header("Alpha", "a")); + httpRequest(RequestMethod.GET, "/foo").header("Alpha", "a").header( + HttpHeaders.HOST, "localhost")); new HttpRequestSnippet().document(new OperationBuilder("get-request", this.snippet.getOutputDirectory()).request("http://localhost/foo") @@ -92,8 +92,8 @@ public class HttpRequestSnippetTests { byte[] contentBytes = japaneseContent.getBytes("UTF-8"); this.snippet.expectHttpRequest("post-request-with-charset").withContents( httpRequest(RequestMethod.POST, "/foo") - .header(HttpHeaders.HOST, "localhost") .header("Content-Type", "text/plain;charset=UTF-8") + .header(HttpHeaders.HOST, "localhost") .header(HttpHeaders.CONTENT_LENGTH, contentBytes.length) .content(japaneseContent)); @@ -151,10 +151,9 @@ public class HttpRequestSnippetTests { + "form-data; " + "name=image%n%n<< data >>")); this.snippet.expectHttpRequest("multipart-post").withContents( httpRequest(RequestMethod.POST, "/upload") - .header(HttpHeaders.HOST, "localhost") .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) - .content(expectedContent)); + .header(HttpHeaders.HOST, "localhost").content(expectedContent)); new HttpRequestSnippet().document(new OperationBuilder("multipart-post", this.snippet.getOutputDirectory()).request("http://localhost/upload") .method("POST") @@ -175,10 +174,9 @@ public class HttpRequestSnippetTests { String expectedContent = param1Part + param2Part + param3Part + filePart; this.snippet.expectHttpRequest("multipart-post-with-parameters").withContents( httpRequest(RequestMethod.POST, "/upload") - .header(HttpHeaders.HOST, "localhost") .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) - .content(expectedContent)); + .header(HttpHeaders.HOST, "localhost").content(expectedContent)); new HttpRequestSnippet().document(new OperationBuilder( "multipart-post-with-parameters", this.snippet.getOutputDirectory()) .request("http://localhost/upload").method("POST") @@ -194,10 +192,9 @@ public class HttpRequestSnippetTests { + "image/png%n%n<< data >>")); this.snippet.expectHttpRequest("multipart-post-with-content-type").withContents( httpRequest(RequestMethod.POST, "/upload") - .header(HttpHeaders.HOST, "localhost") .header("Content-Type", "multipart/form-data; boundary=" + BOUNDARY) - .content(expectedContent)); + .header(HttpHeaders.HOST, "localhost").content(expectedContent)); new HttpRequestSnippet().document(new OperationBuilder( "multipart-post-with-content-type", this.snippet.getOutputDirectory()) .request("http://localhost/upload").method("POST") diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java index 5157e07b..4ff3dbb4 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/ContentTypeLinkExtractorTests.java @@ -26,7 +26,8 @@ import org.junit.rules.ExpectedException; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.restdocs.operation.StandardOperationResponse; +import org.springframework.restdocs.operation.OperationResponse; +import org.springframework.restdocs.operation.OperationResponseFactory; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -38,13 +39,15 @@ import static org.mockito.Mockito.verify; */ public class ContentTypeLinkExtractorTests { + private final OperationResponseFactory responseFactory = new OperationResponseFactory(); + @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void extractionFailsWithNullContentType() throws IOException { this.thrown.expect(IllegalStateException.class); - new ContentTypeLinkExtractor().extractLinks(new StandardOperationResponse( + new ContentTypeLinkExtractor().extractLinks(this.responseFactory.create( HttpStatus.OK, new HttpHeaders(), null)); } @@ -55,7 +58,7 @@ public class ContentTypeLinkExtractorTests { extractors.put(MediaType.APPLICATION_JSON, extractor); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_JSON); - StandardOperationResponse response = new StandardOperationResponse(HttpStatus.OK, + OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, null); new ContentTypeLinkExtractor(extractors).extractLinks(response); verify(extractor).extractLinks(response); @@ -68,7 +71,7 @@ public class ContentTypeLinkExtractorTests { extractors.put(MediaType.APPLICATION_JSON, extractor); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.parseMediaType("application/json;foo=bar")); - StandardOperationResponse response = new StandardOperationResponse(HttpStatus.OK, + OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, null); new ContentTypeLinkExtractor(extractors).extractLinks(response); verify(extractor).extractLinks(response); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java index 02adf5cb..47a05424 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/hypermedia/LinkExtractorsPayloadTests.java @@ -30,7 +30,7 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.springframework.http.HttpStatus; import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.StandardOperationResponse; +import org.springframework.restdocs.operation.OperationResponseFactory; import org.springframework.util.FileCopyUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -46,6 +46,8 @@ import static org.junit.Assert.assertEquals; @RunWith(Parameterized.class) public class LinkExtractorsPayloadTests { + private final OperationResponseFactory responseFactory = new OperationResponseFactory(); + private final LinkExtractor linkExtractor; private final String linkType; @@ -107,7 +109,7 @@ public class LinkExtractorsPayloadTests { } private OperationResponse createResponse(String contentName) throws IOException { - return new StandardOperationResponse(HttpStatus.OK, null, + return this.responseFactory.create(HttpStatus.OK, null, FileCopyUtils.copyToByteArray(getPayloadFile(contentName))); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java index 2f38bbb3..330381f6 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/ContentModifyingOperationPreprocessorTests.java @@ -25,11 +25,11 @@ import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.restdocs.operation.OperationRequest; +import org.springframework.restdocs.operation.OperationRequestFactory; import org.springframework.restdocs.operation.OperationRequestPart; import org.springframework.restdocs.operation.OperationResponse; +import org.springframework.restdocs.operation.OperationResponseFactory; import org.springframework.restdocs.operation.Parameters; -import org.springframework.restdocs.operation.StandardOperationRequest; -import org.springframework.restdocs.operation.StandardOperationResponse; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; @@ -43,6 +43,10 @@ import static org.junit.Assert.assertThat; */ public class ContentModifyingOperationPreprocessorTests { + private final OperationRequestFactory requestFactory = new OperationRequestFactory(); + + private final OperationResponseFactory responseFactory = new OperationResponseFactory(); + private final ContentModifyingOperationPreprocessor preprocessor = new ContentModifyingOperationPreprocessor( new ContentModifier() { @@ -55,7 +59,7 @@ public class ContentModifyingOperationPreprocessorTests { @Test public void modifyRequestContent() { - StandardOperationRequest request = new StandardOperationRequest( + OperationRequest request = this.requestFactory.create( URI.create("http://localhost"), HttpMethod.GET, "content".getBytes(), new HttpHeaders(), new Parameters(), Collections.emptyList()); @@ -65,7 +69,7 @@ public class ContentModifyingOperationPreprocessorTests { @Test public void modifyResponseContent() { - StandardOperationResponse response = new StandardOperationResponse(HttpStatus.OK, + OperationResponse response = this.responseFactory.create(HttpStatus.OK, new HttpHeaders(), "content".getBytes()); OperationResponse preprocessed = this.preprocessor.preprocess(response); assertThat(preprocessed.getContent(), is(equalTo("modified".getBytes()))); @@ -75,7 +79,7 @@ public class ContentModifyingOperationPreprocessorTests { public void contentLengthIsUpdated() { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentLength(7); - StandardOperationRequest request = new StandardOperationRequest( + OperationRequest request = this.requestFactory.create( URI.create("http://localhost"), HttpMethod.GET, "content".getBytes(), httpHeaders, new Parameters(), Collections.emptyList()); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeaderRemovingOperationPreprocessorTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeaderRemovingOperationPreprocessorTests.java index 808b9ab9..634ef616 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeaderRemovingOperationPreprocessorTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/operation/preprocess/HeaderRemovingOperationPreprocessorTests.java @@ -25,11 +25,11 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.restdocs.operation.OperationRequest; +import org.springframework.restdocs.operation.OperationRequestFactory; import org.springframework.restdocs.operation.OperationRequestPart; import org.springframework.restdocs.operation.OperationResponse; +import org.springframework.restdocs.operation.OperationResponseFactory; import org.springframework.restdocs.operation.Parameters; -import org.springframework.restdocs.operation.StandardOperationRequest; -import org.springframework.restdocs.operation.StandardOperationResponse; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; @@ -44,23 +44,29 @@ import static org.junit.Assert.assertThat; */ public class HeaderRemovingOperationPreprocessorTests { + private final OperationRequestFactory requestFactory = new OperationRequestFactory(); + + private final OperationResponseFactory responseFactory = new OperationResponseFactory(); + private final HeaderRemovingOperationPreprocessor preprocessor = new HeaderRemovingOperationPreprocessor( "b"); @Test public void modifyRequestHeaders() { - StandardOperationRequest request = new StandardOperationRequest( + OperationRequest request = this.requestFactory.create( URI.create("http://localhost"), HttpMethod.GET, new byte[0], getHttpHeaders(), new Parameters(), Collections.emptyList()); OperationRequest preprocessed = this.preprocessor.preprocess(request); - assertThat(preprocessed.getHeaders().size(), is(equalTo(1))); + assertThat(preprocessed.getHeaders().size(), is(equalTo(2))); assertThat(preprocessed.getHeaders(), hasEntry("a", Arrays.asList("alpha"))); + assertThat(preprocessed.getHeaders(), + hasEntry("Host", Arrays.asList("localhost"))); } @Test public void modifyResponseHeaders() { - StandardOperationResponse response = new StandardOperationResponse(HttpStatus.OK, + OperationResponse response = this.responseFactory.create(HttpStatus.OK, getHttpHeaders(), new byte[0]); OperationResponse preprocessed = this.preprocessor.preprocess(response); assertThat(preprocessed.getHeaders().size(), is(equalTo(1))); diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/OperationBuilder.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/OperationBuilder.java index 51d66504..e5be8166 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/OperationBuilder.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/test/OperationBuilder.java @@ -29,13 +29,13 @@ import org.springframework.http.HttpStatus; import org.springframework.restdocs.RestDocumentationContext; import org.springframework.restdocs.operation.Operation; import org.springframework.restdocs.operation.OperationRequest; +import org.springframework.restdocs.operation.OperationRequestFactory; import org.springframework.restdocs.operation.OperationRequestPart; +import org.springframework.restdocs.operation.OperationRequestPartFactory; import org.springframework.restdocs.operation.OperationResponse; +import org.springframework.restdocs.operation.OperationResponseFactory; import org.springframework.restdocs.operation.Parameters; import org.springframework.restdocs.operation.StandardOperation; -import org.springframework.restdocs.operation.StandardOperationRequest; -import org.springframework.restdocs.operation.StandardOperationRequestPart; -import org.springframework.restdocs.operation.StandardOperationResponse; import org.springframework.restdocs.snippet.RestDocumentationContextPlaceholderResolver; import org.springframework.restdocs.snippet.StandardWriterResolver; import org.springframework.restdocs.snippet.WriterResolver; @@ -122,7 +122,7 @@ public class OperationBuilder { for (OperationRequestPartBuilder builder : this.partBuilders) { parts.add(builder.buildPart()); } - return new StandardOperationRequest(this.requestUri, this.method, + return new OperationRequestFactory().create(this.requestUri, this.method, this.content, this.headers, this.parameters, parts); } @@ -196,7 +196,7 @@ public class OperationBuilder { } private OperationRequestPart buildPart() { - return new StandardOperationRequestPart(this.name, + return new OperationRequestPartFactory().create(this.name, this.submittedFileName, this.content, this.headers); } @@ -219,7 +219,8 @@ public class OperationBuilder { private byte[] content = new byte[0]; private OperationResponse buildResponse() { - return new StandardOperationResponse(this.status, this.headers, this.content); + return new OperationResponseFactory().create(this.status, this.headers, + this.content); } public OperationResponseBuilder status(int status) { diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationRequestFactory.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationRequestFactory.java index 417f29e8..9449354a 100644 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationRequestFactory.java +++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationRequestFactory.java @@ -33,10 +33,10 @@ import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockMultipartHttpServletRequest; import org.springframework.restdocs.operation.OperationRequest; +import org.springframework.restdocs.operation.OperationRequestFactory; import org.springframework.restdocs.operation.OperationRequestPart; +import org.springframework.restdocs.operation.OperationRequestPartFactory; import org.springframework.restdocs.operation.Parameters; -import org.springframework.restdocs.operation.StandardOperationRequest; -import org.springframework.restdocs.operation.StandardOperationRequestPart; import org.springframework.util.FileCopyUtils; import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; @@ -67,7 +67,7 @@ class MockMvcOperationRequestFactory { * @return the {@code OperationRequest} * @throws Exception if the request could not be created */ - public OperationRequest createOperationRequest(MockHttpServletRequest mockRequest) + OperationRequest createOperationRequest(MockHttpServletRequest mockRequest) throws Exception { HttpHeaders headers = extractHeaders(mockRequest); Parameters parameters = extractParameters(mockRequest); @@ -76,8 +76,9 @@ class MockMvcOperationRequestFactory { if (!StringUtils.hasText(queryString) && "GET".equals(mockRequest.getMethod())) { queryString = parameters.toQueryString(); } - return new StandardOperationRequest(URI.create(getRequestUri(mockRequest) - + (StringUtils.hasText(queryString) ? "?" + queryString : "")), + return new OperationRequestFactory().create( + URI.create(getRequestUri(mockRequest) + + (StringUtils.hasText(queryString) ? "?" + queryString : "")), HttpMethod.valueOf(mockRequest.getMethod()), FileCopyUtils.copyToByteArray(mockRequest.getInputStream()), headers, parameters, parts); @@ -102,16 +103,17 @@ class MockMvcOperationRequestFactory { return parts; } - private StandardOperationRequestPart createOperationRequestPart(Part part) - throws IOException { + private OperationRequestPart createOperationRequestPart(Part part) throws IOException { HttpHeaders partHeaders = extractHeaders(part); List contentTypeHeader = partHeaders.get(HttpHeaders.CONTENT_TYPE); if (part.getContentType() != null && contentTypeHeader == null) { partHeaders.setContentType(MediaType.parseMediaType(part.getContentType())); } - return new StandardOperationRequestPart(part.getName(), StringUtils.hasText(part - .getSubmittedFileName()) ? part.getSubmittedFileName() : null, - FileCopyUtils.copyToByteArray(part.getInputStream()), partHeaders); + return new OperationRequestPartFactory() + .create(part.getName(), + StringUtils.hasText(part.getSubmittedFileName()) ? part + .getSubmittedFileName() : null, FileCopyUtils + .copyToByteArray(part.getInputStream()), partHeaders); } private List extractMultipartRequestParts( @@ -126,14 +128,14 @@ class MockMvcOperationRequestFactory { return parts; } - private StandardOperationRequestPart createOperationRequestPart(MultipartFile file) + private OperationRequestPart createOperationRequestPart(MultipartFile file) throws IOException { HttpHeaders partHeaders = new HttpHeaders(); if (StringUtils.hasText(file.getContentType())) { partHeaders.setContentType(MediaType.parseMediaType(file.getContentType())); } - return new StandardOperationRequestPart(file.getName(), StringUtils.hasText(file - .getOriginalFilename()) ? file.getOriginalFilename() : null, + return new OperationRequestPartFactory().create(file.getName(), StringUtils + .hasText(file.getOriginalFilename()) ? file.getOriginalFilename() : null, file.getBytes(), partHeaders); } diff --git a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationResponseFactory.java b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationResponseFactory.java index 54915697..4e4f765f 100644 --- a/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationResponseFactory.java +++ b/spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/MockMvcOperationResponseFactory.java @@ -20,7 +20,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.restdocs.operation.OperationResponse; -import org.springframework.restdocs.operation.StandardOperationResponse; +import org.springframework.restdocs.operation.OperationResponseFactory; /** * A factory for creating an {@link OperationResponse} derived from a @@ -36,8 +36,8 @@ class MockMvcOperationResponseFactory { * @param mockResponse the response * @return the {@code OperationResponse} */ - public OperationResponse createOperationResponse(MockHttpServletResponse mockResponse) { - return new StandardOperationResponse( + OperationResponse createOperationResponse(MockHttpServletResponse mockResponse) { + return new OperationResponseFactory().create( HttpStatus.valueOf(mockResponse.getStatus()), extractHeaders(mockResponse), mockResponse.getContentAsByteArray()); } diff --git a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java index ad6f3453..a7422f94 100644 --- a/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java +++ b/spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java @@ -269,33 +269,31 @@ public class MockMvcRestDocumentationIntegrationTests { .andDo(document("original-request")) .andDo(document( "preprocessed-request", - preprocessRequest(prettyPrint(), removeHeaders("a"), + preprocessRequest( + prettyPrint(), + removeHeaders("a", HttpHeaders.HOST, + HttpHeaders.CONTENT_LENGTH), replacePattern(pattern, "\"<>\"")))); assertThat( new File("build/generated-snippets/original-request/http-request.adoc"), is(snippet().withContents( - httpRequest(RequestMethod.GET, "/").header("Host", "localhost") - .header("a", "alpha").header("b", "bravo") + httpRequest(RequestMethod.GET, "/").header("a", "alpha") + .header("b", "bravo") .header("Content-Type", "application/json") .header("Accept", MediaType.APPLICATION_JSON_VALUE) + .header("Host", "localhost") .header("Content-Length", "13") .content("{\"a\":\"alpha\"}")))); String prettyPrinted = String.format("{%n \"a\" : \"<>\"%n}"); assertThat( new File( "build/generated-snippets/preprocessed-request/http-request.adoc"), - is(snippet() - .withContents( - httpRequest(RequestMethod.GET, "/") - .header("Host", "localhost") - .header("b", "bravo") - .header("Content-Type", "application/json") - .header("Accept", - MediaType.APPLICATION_JSON_VALUE) - .header("Content-Length", - Integer.toString(prettyPrinted.getBytes().length)) - .content(prettyPrinted)))); + is(snippet().withContents( + httpRequest(RequestMethod.GET, "/").header("b", "bravo") + .header("Content-Type", "application/json") + .header("Accept", MediaType.APPLICATION_JSON_VALUE) + .content(prettyPrinted)))); } @Test