springrequest-mappingcontent-negotiation

Controller returning inputstream - content negotiation and media types


Introduction

I have a question about a RestController and a Test .

I have the following PostMapping:

@PostMapping(path = "/download/as/zip/{zipFileName}" )
    @ResponseBody
    public ResponseEntity<InputStreamResource> downloadDocumentZip(@RequestHeader(required=false,name="X-Application") String appName, @RequestBody ZipFileModel zipFileModel, @PathVariable("zipFileName") String zipFileName)

And I have the following Test:

Response response = given(this.requestSpecification).port(port)
                .filter(document("downloadAsZip",
                        preprocessRequest(prettyPrint()),
                        requestHeaders(headerWithName("X-Application").description("Owner application")),
                        pathParameters(parameterWithName("zipFileName").description("The name of the resulting zip file. Mostly not needed/optional.")))
                )
                .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
                .header(new Header(HEADER, "themis"))
                .body(jsonContent)
                .when()
                .post("/download/as/zip/{zipFileName}", "resultFile.zip");

This works and 200 is returned.

First Question

Now I am a bit confused about the meaning of .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE) within the Test.

Content-type is the header on the returned response. But in this test it is being included while making the test request ? Or is it signifying in this case that we are sending JSON in the request body?

Second Question

I know that my controller method should consume JSON, and returns Bytes. So hence, I make the following change:

@PostMapping(path = "/download/as/zip/{zipFileName}", consumes = MediaType.APPLICATION_JSON_VALUE)

This works so far.

So then I add the following:

@PostMapping(path = "/download/as/zip/{zipFileName}", consumes = MediaType.APPLICATION_JSON_VALUE, 
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)

And it fails:

java.lang.AssertionError: 
Expected :200
Actual   :406
 <Click to see difference>

So I changed my test to be the following:

.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
                .accept(MediaType.APPLICATION_OCTET_STREAM_VALUE)

This fails once again.

Expected :200
Actual   :406

So even though the client is signalling the same accept header as what the controller produces, we have an error.

Questions:

  1. So should we or should we not have the produces= on the request mapping?
  2. Why is it failing now? Is there a conflict in consuming JSON and Producing Bytes? Or the ContentType within the test?

Solution

  • The problem is that spring changes the return content-type if the end of a URL has an extension.

    So seeing .zip at the end, was causing spring to over-ride the type to application/zip .