Search code examples
javaspringspring-mvcspring-restcontrollermockmvc

Cant have RestController to accept application/octet-stream


I have a spring boot application with rest controller that has to accept binary stream at post endpoint and do the things with it.

So i have:

    @PostMapping(path="/parse", consumes = {MediaType.APPLICATION_OCTET_STREAM_VALUE})
    public String parse(RequestEntity<InputStream> entity) {
        return service.parse(entity.getBody());
    }

When i try to test it with MockMvc i get org.springframework.web.HttpMediaTypeNotSupportedException. I see in log: Request:

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /parse
       Parameters = {}
          Headers = [Content-Type:"application/octet-stream;charset=UTF-8", Content-Length:"2449"]
             Body = ...skipped unreadable binary data...
    Session Attrs = {}

Response:

MockHttpServletResponse:
           Status = 415
    Error message = null
          Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Accept:"application/json, application/*+json", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

I tried to add explicit header:

    @PostMapping(path="/parse", consumes = {MediaType.APPLICATION_OCTET_STREAM_VALUE},
            headers = "Accept=application/octet-stream")

Does not help. The testing call is:

        mvc.perform(post("/parse")                       
                        .contentType(MediaType.APPLICATION_OCTET_STREAM)
                        .content(bytes)
                ).andDo(print())
                .andExpect(status().isOk());

How can i make it work without going to multipart-form?


Solution

  • I have analysed this issue and got to know that issue is in this method.

      @PostMapping(path="/parse", consumes = {MediaType.APPLICATION_OCTET_STREAM_VALUE})
        public String parse(RequestEntity<InputStream> entity) {
            return service.parse(entity.getBody());
        }
    

    Here method parameter is of type RequestEntity<InputStream> it should be HttpServletRequest

    So here is the fix.

        @PostMapping(value = "/upload",
                consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
        public String demo(HttpServletRequest httpServletRequest) {
    
            try (ServletInputStream inputStream = httpServletRequest.getInputStream()) {
                new BufferedReader(new InputStreamReader(inputStream, UTF_8))
                        .lines().forEach(System.out::println);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
    
            return "Hello World";
        }
    

    Test Case

        @Autowired
        private MockMvc mockMvc;
    
        @Test
        public void shouldTestBinaryFileUpload() throws Exception {
            mockMvc
                    .perform(MockMvcRequestBuilders
                            .post("/upload")
                            .content("Hello".getBytes())
                            .contentType(MediaType.APPLICATION_OCTET_STREAM))
                    .andExpect(MockMvcResultMatchers
                            .status()
                            .isOk())
                    .andExpect(MockMvcResultMatchers
                            .content()
                            .bytes("Hello World".getBytes()));
        }