From 86c31c96b93cc523f0603b00007f0bfac257db21 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 25 Jun 2018 16:11:36 +0100 Subject: [PATCH] Avoid duplication of parameters when POSTing form URL encoded bodies Previously if a request body contained form URL encoded content, parameters would be duplicated in the request body and in the request URL in the curl, HTTPie, and HTTP request snippets. This commit updates the affected snippets so that parameters are not added to the URL when they will also be represented in the request body. Closes gh-514 --- .../restdocs/cli/CurlRequestSnippet.java | 5 ++++- .../restdocs/cli/HttpieRequestSnippet.java | 13 +++++++++++-- .../restdocs/http/HttpRequestSnippet.java | 4 +++- .../restdocs/cli/CurlRequestSnippetTests.java | 14 ++++++++++++++ .../restdocs/cli/HttpieRequestSnippetTests.java | 14 ++++++++++++++ .../restdocs/http/HttpRequestSnippetTests.java | 17 +++++++++++++++++ 6 files changed, 63 insertions(+), 4 deletions(-) diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CurlRequestSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CurlRequestSnippet.java index b95b0cab..056b7194 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CurlRequestSnippet.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/CurlRequestSnippet.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Map.Entry; import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; import org.springframework.restdocs.operation.Operation; import org.springframework.restdocs.operation.OperationRequest; import org.springframework.restdocs.operation.OperationRequestPart; @@ -119,7 +120,9 @@ public class CurlRequestSnippet extends TemplatedSnippet { } private boolean includeParametersInUri(OperationRequest request) { - return request.getMethod() == HttpMethod.GET || request.getContent().length > 0; + return request.getMethod() == HttpMethod.GET || (request.getContent().length > 0 + && !MediaType.APPLICATION_FORM_URLENCODED + .isCompatibleWith(request.getHeaders().getContentType())); } private String getOptions(Operation operation) { diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/HttpieRequestSnippet.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/HttpieRequestSnippet.java index 76a18802..7cb346a2 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/HttpieRequestSnippet.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/cli/HttpieRequestSnippet.java @@ -152,13 +152,22 @@ public class HttpieRequestSnippet extends TemplatedSnippet { private void writeOptions(OperationRequest request, PrintWriter writer) { if (!request.getParts().isEmpty() || (!request.getParameters().getUniqueParameters(request.getUri()) - .isEmpty() && !includeParametersInUri(request))) { + .isEmpty() && !includeParametersInUri(request) + && includeParametersAsFormOptions(request))) { writer.print("--form "); } } private boolean includeParametersInUri(OperationRequest request) { - return request.getMethod() == HttpMethod.GET || request.getContent().length > 0; + return request.getMethod() == HttpMethod.GET || (request.getContent().length > 0 + && !MediaType.APPLICATION_FORM_URLENCODED + .isCompatibleWith(request.getHeaders().getContentType())); + } + + private boolean includeParametersAsFormOptions(OperationRequest request) { + return request.getMethod() != HttpMethod.GET && (request.getContent().length == 0 + || !MediaType.APPLICATION_FORM_URLENCODED + .isCompatibleWith(request.getHeaders().getContentType())); } private void writeUserOptionIfNecessary(CliOperationRequest request, 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 aedebdf7..236a5a46 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 @@ -94,7 +94,9 @@ public class HttpRequestSnippet extends TemplatedSnippet { } private boolean includeParametersInUri(OperationRequest request) { - return request.getMethod() == HttpMethod.GET || request.getContent().length > 0; + return request.getMethod() == HttpMethod.GET || (request.getContent().length > 0 + && !MediaType.APPLICATION_FORM_URLENCODED + .isCompatibleWith(request.getHeaders().getContentType())); } private List> getHeaders(OperationRequest request) { diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/CurlRequestSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/CurlRequestSnippetTests.java index f6d9a916..32507e3a 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/CurlRequestSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/CurlRequestSnippetTests.java @@ -213,6 +213,20 @@ public class CurlRequestSnippetTests extends AbstractSnippetTests { .method("POST").param("a", "alpha").param("b", "bravo").build()); } + @Test + public void postRequestWithOverlappingParametersAndFormUrlEncodedBody() + throws IOException { + this.snippets.expectCurlRequest().withContents( + codeBlock("bash").content("$ curl 'http://localhost/foo' -i -X POST " + + "-H 'Content-Type: application/x-www-form-urlencoded' " + + "-d 'a=alpha&b=bravo'")); + new CurlRequestSnippet(this.commandFormatter).document(this.operationBuilder + .request("http://localhost/foo").method("POST").content("a=alpha&b=bravo") + .header(HttpHeaders.CONTENT_TYPE, + MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .param("a", "alpha").param("b", "bravo").build()); + } + @Test public void putRequestWithOneParameter() throws IOException { this.snippets.expectCurlRequest().withContents(codeBlock("bash") diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/HttpieRequestSnippetTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/HttpieRequestSnippetTests.java index 5b775002..48bcf8ba 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/HttpieRequestSnippetTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/cli/HttpieRequestSnippetTests.java @@ -214,6 +214,20 @@ public class HttpieRequestSnippetTests extends AbstractSnippetTests { .method("POST").param("a", "alpha").param("b", "bravo").build()); } + @Test + public void postRequestWithOverlappingParametersAndFormUrlEncodedBody() + throws IOException { + this.snippets.expectHttpieRequest() + .withContents(codeBlock("bash").content( + "$ echo 'a=alpha&b=bravo' | http POST 'http://localhost/foo' " + + "'Content-Type:application/x-www-form-urlencoded'")); + new HttpieRequestSnippet(this.commandFormatter).document(this.operationBuilder + .request("http://localhost/foo").method("POST").content("a=alpha&b=bravo") + .header(HttpHeaders.CONTENT_TYPE, + MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .param("a", "alpha").param("b", "bravo").build()); + } + @Test public void putRequestWithOneParameter() throws IOException { this.snippets.expectHttpieRequest().withContents(codeBlock("bash") 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 2a58ae97..0de3b5ae 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 @@ -202,6 +202,23 @@ public class HttpRequestSnippetTests extends AbstractSnippetTests { .param("a", "alpha").param("b", "bravo").content(content).build()); } + @Test + public void postRequestWithOverlappingParametersAndFormUrlEncodedBody() + throws IOException { + String content = "a=alpha&b=bravo"; + this.snippets.expectHttpRequest() + .withContents(httpRequest(RequestMethod.POST, "/foo") + .header(HttpHeaders.CONTENT_TYPE, + MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .header(HttpHeaders.HOST, "localhost").content(content) + .header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length)); + new HttpRequestSnippet().document(this.operationBuilder + .request("http://localhost/foo").method("POST").content("a=alpha&b=bravo") + .header(HttpHeaders.CONTENT_TYPE, + MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .param("a", "alpha").param("b", "bravo").build()); + } + @Test public void postRequestWithCharset() throws IOException { String japaneseContent = "\u30b3\u30f3\u30c6\u30f3\u30c4";