Update HTTP request and response snippets to honour charset of content

Closes gh-126
This commit is contained in:
MURAKAMI Masahiko
2015-09-14 13:39:00 +09:00
committed by Andy Wilkinson
parent e04d361dcd
commit 9b31c0ce9b
8 changed files with 123 additions and 7 deletions

View File

@@ -16,6 +16,7 @@
package org.springframework.restdocs.http;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
@@ -30,6 +31,7 @@ import org.springframework.http.MediaType;
import org.springframework.restdocs.operation.Operation;
import org.springframework.restdocs.operation.OperationRequest;
import org.springframework.restdocs.operation.OperationRequestPart;
import org.springframework.restdocs.snippet.ModelCreationException;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.restdocs.snippet.TemplatedSnippet;
import org.springframework.util.StringUtils;
@@ -107,8 +109,7 @@ public class HttpRequestSnippet extends TemplatedSnippet {
StringWriter httpRequest = new StringWriter();
PrintWriter writer = new PrintWriter(httpRequest);
if (request.getContent().length > 0) {
writer.println();
writer.print(new String(request.getContent()));
writer.print(requestBody(request));
}
else if (isPutOrPost(request)) {
if (request.getParts().isEmpty()) {
@@ -125,6 +126,16 @@ public class HttpRequestSnippet extends TemplatedSnippet {
return httpRequest.toString();
}
private String requestBody(OperationRequest request) {
try {
String content = request.getContentAsString();
return content.isEmpty() ? content : String.format("%n%s", content);
}
catch (IOException e) {
throw new ModelCreationException("Failed to create response body.", e);
}
}
private boolean isPutOrPost(OperationRequest request) {
return HttpMethod.PUT.equals(request.getMethod())
|| HttpMethod.POST.equals(request.getMethod());

View File

@@ -16,6 +16,7 @@
package org.springframework.restdocs.http;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -25,6 +26,7 @@ import java.util.Map.Entry;
import org.springframework.http.HttpStatus;
import org.springframework.restdocs.operation.Operation;
import org.springframework.restdocs.operation.OperationResponse;
import org.springframework.restdocs.snippet.ModelCreationException;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.restdocs.snippet.TemplatedSnippet;
@@ -59,16 +61,23 @@ public class HttpResponseSnippet extends TemplatedSnippet {
OperationResponse response = operation.getResponse();
HttpStatus status = response.getStatus();
Map<String, Object> model = new HashMap<String, Object>();
model.put(
"responseBody",
response.getContent().length > 0 ? String.format("%n%s", new String(
response.getContent())) : "");
model.put("responseBody", responseBody(response));
model.put("statusCode", status.value());
model.put("statusReason", status.getReasonPhrase());
model.put("headers", headers(response));
return model;
}
private String responseBody(OperationResponse response) {
try {
String content = response.getContentAsString();
return content.isEmpty() ? content : String.format("%n%s", content);
}
catch (IOException e) {
throw new ModelCreationException("Failed to create response body.", e);
}
}
private List<Map<String, String>> headers(OperationResponse response) {
List<Map<String, String>> headers = new ArrayList<>();
for (Entry<String, List<String>> header : response.getHeaders().entrySet()) {

View File

@@ -16,6 +16,7 @@
package org.springframework.restdocs.operation;
import java.io.IOException;
import java.net.URI;
import java.util.Collection;
@@ -38,6 +39,14 @@ public interface OperationRequest {
*/
byte[] getContent();
/**
* Returns the contents as string of the request. If the request has no content an empty string
* is returned
* @return the contents as string, never {@code null}
* @throws IOException if an input or output exception occurred
*/
String getContentAsString() throws IOException;
/**
* Returns the headers that were included in the request.
*

View File

@@ -16,6 +16,8 @@
package org.springframework.restdocs.operation;
import java.io.IOException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@@ -49,4 +51,12 @@ public interface OperationResponse {
* @return the contents, never {@code null}
*/
byte[] getContent();
/**
* Returns the contents as string of the response. If the response has no content an empty string
* is returned
* @return the contents as string, never {@code null}
* @throws IOException if an input or output exception occurred
*/
String getContentAsString() throws IOException;
}

View File

@@ -16,6 +16,7 @@
package org.springframework.restdocs.operation;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
@@ -23,6 +24,7 @@ import java.util.Collections;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
/**
* Standard implementation of {@link OperationRequest}.
@@ -33,6 +35,8 @@ public class StandardOperationRequest implements OperationRequest {
private byte[] content;
private String characterEncoding;
private HttpHeaders headers;
private HttpMethod method;
@@ -60,6 +64,7 @@ public class StandardOperationRequest implements OperationRequest {
this.uri = uri;
this.method = method;
this.content = content;
this.characterEncoding = detectCharsetFromContentTypeHeader(headers);
this.headers = headers;
this.parameters = parameters;
this.parts = parts;
@@ -69,6 +74,17 @@ public class StandardOperationRequest implements OperationRequest {
public byte[] getContent() {
return Arrays.copyOf(this.content, this.content.length);
}
@Override
public String getContentAsString() throws UnsupportedEncodingException {
if (content.length > 0) {
return characterEncoding != null ?
new String(content, characterEncoding) : new String(content);
}
else {
return "";
}
}
@Override
public HttpHeaders getHeaders() {
@@ -95,4 +111,14 @@ public class StandardOperationRequest implements OperationRequest {
return this.uri;
}
private String detectCharsetFromContentTypeHeader(HttpHeaders headers) {
if (headers == null) {
return null;
}
MediaType contentType = headers.getContentType();
if (contentType == null) {
return null;
}
return contentType.getParameter("charset");
}
}

View File

@@ -16,8 +16,11 @@
package org.springframework.restdocs.operation;
import java.io.UnsupportedEncodingException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
/**
* Standard implementation of {@link OperationResponse}.
@@ -32,6 +35,8 @@ public class StandardOperationResponse implements OperationResponse {
private final byte[] content;
private String characterEncoding;
/**
* Creates a new response with the given {@code status}, {@code headers}, and
* {@code content}.
@@ -45,6 +50,7 @@ public class StandardOperationResponse implements OperationResponse {
this.status = status;
this.headers = headers;
this.content = content;
this.characterEncoding = detectCharsetFromContentTypeHeader(headers);
}
@Override
@@ -62,4 +68,25 @@ public class StandardOperationResponse implements OperationResponse {
return this.content;
}
@Override
public String getContentAsString() throws UnsupportedEncodingException {
if (content.length > 0) {
return characterEncoding != null ?
new String(content, characterEncoding) : new String(content);
}
else {
return "";
}
}
private String detectCharsetFromContentTypeHeader(HttpHeaders headers) {
if (headers == null) {
return null;
}
MediaType contentType = headers.getContentType();
if (contentType == null) {
return null;
}
return contentType.getParameter("charset");
}
}

View File

@@ -84,7 +84,23 @@ public class HttpRequestSnippetTests {
.request("http://localhost/foo").method("POST").content("Hello, world")
.build());
}
@Test
public void postRequestWithCharset() throws IOException {
this.snippet.expectHttpRequest("post-request-with-charset").withContents(
httpRequest(POST, "/foo")
.header(HttpHeaders.HOST, "localhost")
.header("Content-Type", "text/plain;charset=UTF-8").content(
"こんにちわ, 世界")); // Hello, World in japanese.
new HttpRequestSnippet().document(new OperationBuilder(
"post-request-with-charset", this.snippet.getOutputDirectory())
.request("http://localhost/foo").method("POST")
.header("Content-Type", "text/plain;charset=UTF-8")
.content("こんにちわ, 世界")
.build());
}
@Test
public void postRequestWithParameter() throws IOException {
this.snippet.expectHttpRequest("post-request-with-parameter").withContents(

View File

@@ -84,7 +84,15 @@ public class HttpResponseSnippetTests {
new HttpResponseSnippet().document(new OperationBuilder("response-with-content",
this.snippet.getOutputDirectory()).response().content("content").build());
}
@Test
public void responseWithCharset() throws IOException {
this.snippet.expectHttpResponse("response-with-charset").withContents(
httpResponse(OK).header("Content-Type", "text/plain;charset=UTF-8").content("コンテンツ"));
new HttpResponseSnippet().document(new OperationBuilder("response-with-charset",
this.snippet.getOutputDirectory()).response().header("Content-Type", "text/plain;charset=UTF-8").content("コンテンツ").build());
}
@Test
public void responseWithCustomSnippetAttributes() throws IOException {
this.snippet.expectHttpResponse("response-with-snippet-attributes").withContents(