Skip to content

Commit

Permalink
Print only printable request/response body in MVC Test
Browse files Browse the repository at this point in the history
Prior to this commit, PrintingResultHandler always printed the request
or response body regardless of its content type. For binary content,
however, the output was unreadable and therefore useless.

This commit addresses this issue by only printing the request or
response body if it is "printable" (i.e., if its content type is known
to be text-based such as plain text, HTML, XHTML, XML, JSON, etc.). If
the content type is unknown (e.g., unspecified for the HTTP request in
the test), it is assumed that the body is printable.

Issue: SPR-14776
  • Loading branch information
sbrannen committed Oct 11, 2016
1 parent 7ae7294 commit e65a1a4
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 3 deletions.
Expand Up @@ -16,7 +16,9 @@

package org.springframework.test.web.servlet.result;

import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

import javax.servlet.http.Cookie;
Expand All @@ -29,6 +31,8 @@
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultHandler;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.BindingResult;
Expand All @@ -54,6 +58,14 @@
*/
public class PrintingResultHandler implements ResultHandler {

private static final String NOT_PRINTABLE = "<content type is not printable text>";

private static final List<MimeType> printableMimeTypes = Arrays.asList(
MimeTypeUtils.APPLICATION_JSON, MimeTypeUtils.APPLICATION_XML,
new MimeType("text", "*"), new MimeType("application", "*+json"),
new MimeType("application", "*+xml"));


private final ResultValuePrinter printer;


Expand Down Expand Up @@ -103,11 +115,14 @@ public final void handle(MvcResult result) throws Exception {
* Print the request.
*/
protected void printRequest(MockHttpServletRequest request) throws Exception {
String body = (isPrintableContentType(request.getContentType()) ?
request.getContentAsString() : NOT_PRINTABLE);

this.printer.printValue("HTTP Method", request.getMethod());
this.printer.printValue("Request URI", request.getRequestURI());
this.printer.printValue("Parameters", getParamsMultiValueMap(request));
this.printer.printValue("Headers", getRequestHeaders(request));
this.printer.printValue("Body", request.getContentAsString());
this.printer.printValue("Body", body);
}

protected final HttpHeaders getRequestHeaders(MockHttpServletRequest request) {
Expand Down Expand Up @@ -223,11 +238,14 @@ protected void printFlashMap(FlashMap flashMap) throws Exception {
* Print the response.
*/
protected void printResponse(MockHttpServletResponse response) throws Exception {
String body = (isPrintableContentType(response.getContentType()) ?
response.getContentAsString() : NOT_PRINTABLE);

this.printer.printValue("Status", response.getStatus());
this.printer.printValue("Error message", response.getErrorMessage());
this.printer.printValue("Headers", getResponseHeaders(response));
this.printer.printValue("Content type", response.getContentType());
this.printer.printValue("Body", response.getContentAsString());
this.printer.printValue("Body", body);
this.printer.printValue("Forwarded URL", response.getForwardedUrl());
this.printer.printValue("Redirected URL", response.getRedirectedUrl());
printCookies(response.getCookies());
Expand Down Expand Up @@ -266,6 +284,23 @@ protected final HttpHeaders getResponseHeaders(MockHttpServletResponse response)
}


/**
* Determine if the supplied content type is <em>printable</em> (i.e., text-based).
* <p>If the supplied content type is {@code null} (i.e., unknown), this method
* assumes that the content is printable by default and returns {@code true}.
* @param contentType the content type to check; {@code null} if unknown
* @return {@code true} if the content type is known to be or assumed to be printable
* @since 5.0
*/
private static boolean isPrintableContentType(String contentType) {
if (contentType == null) {
return true;
}
MimeType mimeType = MimeType.valueOf(contentType);
return printableMimeTypes.stream().anyMatch(printable -> printable.includes(mimeType));
}


/**
* A contract for how to actually write result information.
*/
Expand Down
Expand Up @@ -17,7 +17,9 @@
package org.springframework.test.web.servlet.result;

import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.Cookie;
Expand All @@ -31,6 +33,7 @@
import org.springframework.test.web.servlet.StubMvcResult;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MimeTypeUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
Expand All @@ -50,6 +53,11 @@
*/
public class PrintingResultHandlerTests {

private static final List<String> textContentTypes = Arrays.asList(MimeTypeUtils.APPLICATION_JSON_VALUE,
MimeTypeUtils.APPLICATION_XML_VALUE, MimeTypeUtils.APPLICATION_XHTML_XML_VALUE,
MimeTypeUtils.TEXT_HTML_VALUE, MimeTypeUtils.TEXT_PLAIN_VALUE);


private final TestPrintingResultHandler handler = new TestPrintingResultHandler();

private final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/") {
Expand Down Expand Up @@ -82,7 +90,6 @@ public void printRequest() throws Exception {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("param", "paramValue");


assertValue("MockHttpServletRequest", "HTTP Method", this.request.getMethod());
assertValue("MockHttpServletRequest", "Request URI", this.request.getRequestURI());
assertValue("MockHttpServletRequest", "Parameters", params);
Expand Down Expand Up @@ -139,6 +146,46 @@ public void printResponse() throws Exception {
assertTrue(cookie2.endsWith("]"));
}

@Test
public void printRequestWithTextContentTypes() throws Exception {
this.request.setContent("text".getBytes());

for (String contentType: textContentTypes) {
this.request.setContentType(contentType);
this.handler.handle(this.mvcResult);
assertValue("MockHttpServletRequest", "Body", "text");
}
}

@Test
public void printResponseWithTextContentTypes() throws Exception {
this.response.getWriter().print("text");

for (String contentType: textContentTypes) {
this.response.setContentType(contentType);
this.handler.handle(this.mvcResult);
assertValue("MockHttpServletResponse", "Body", "text");
}
}

@Test
public void printRequestWithBinaryContentType() throws Exception {
this.request.setContentType(MimeTypeUtils.IMAGE_JPEG_VALUE);

this.handler.handle(this.mvcResult);

assertValue("MockHttpServletRequest", "Body", "<content type is not printable text>");
}

@Test
public void printResponseWithBinaryContentType() throws Exception {
this.response.setContentType(MimeTypeUtils.IMAGE_JPEG_VALUE);

this.handler.handle(this.mvcResult);

assertValue("MockHttpServletResponse", "Body", "<content type is not printable text>");
}

@Test
public void printHandlerNull() throws Exception {
StubMvcResult mvcResult = new StubMvcResult(this.request, null, null, null, null, null, this.response);
Expand Down

0 comments on commit e65a1a4

Please sign in to comment.