javaexcelspring-bootcontent-negotiationmedia-type

Unsuccessful content negotation with excel file


I have a Spring Boot application with an already working endpoint that produces an xlsx file. Now, I want to implement content negotation on this endpoint but I always receive 406 Not Acceptable.

{
    "timestamp": "2021-03-09T18:44:56.997+0000",
    "status": 406,
    "error": "Not Acceptable",
    "message": "Could not find acceptable representation",
    "path": "/students/excel"
}

And I am using URL parameters and I am calling it like that

localhost:8080/students/excel/excel?format=xlsx

The implementation

Endpoint

    @PostMapping(path = "/excel", produces = {"application/vnd.ms-excel"})
    public byte[] generateExcel(HttpServletResponse response, final @RequestBody @NonNull Criteria criteria) {
            
        response.setContentType("application/vnd.ms-excel");

        response.setHeader("Content-Disposition", "attachment; filename=Students.xlsx");

        return studentService.generateExcelReport(response, criteria);
    }

The method that is finalizing the excel file.

   public static byte[] write(HttpServletResponse response, final @NonNull XSSFWorkbook workbook) {
        
        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            os.writeTo(response.getOutputStream());
            workbook.write(os);
            workbook.close();
            return os.toByteArray();
        } catch (final IOException ex) {
            throw new RuntimeException("Error generating excel", ex);
        }
    }

And the relevant method on WebConfiguration that implements WebMvcConfigurer

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {

        configurer.favorPathExtension(false).
                        favorParameter(true).
                        parameterName("format").
                        ignoreAcceptHeader(true).
                        useJaf(false).
                defaultContentType(MediaType.APPLICATION_JSON).
                mediaType("xlsx", MediaType.MULTIPART_FORM_DATA);
    }

I tried a lot of combinations with MediaType and on WebConfiguration and the attribute of produces like application/csv to check if there is a possibility that it could work due to formating of the excel file and others. But I couldn't overcome this status. When setting it to application/json and text/plain it works but it's not the wanted functionality or the correct one.

When I am not using content negotiation, the generation of the excel works as I mentioned.

Edit: Based on suggestions, I changed the content type to application/vnd.openxmlformats-officedocument.spreadsheetml.sheet and changed the header of both Accept and Content-Type on Postman and still receive 406.

This is how the request on Postman looks enter image description here

I also debugged the application and it doesn't enter the method of the endpoint, it seems to fail instantly because of the produces value.

Ι want to add that this is a POST request that accepts a JSON. So, using any other content-type on Postman will break it.

Update

It works by using the accept header instead of parameters and changing WebConfigurer method. But, I wanted to to use URL parameters and to understand why they don't work.

@Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(true).
                favorParameter(false).
                ignoreAcceptHeader(false).
                useJaf(false);
    }

Solution

  • I found a solution in order to make it work with URL parameters, as it was my first intention. I added a new Media Type of vnd.ms-excel on WebConfiguration as following.

        @Override
        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            configurer.favorPathExtension(false).
                            favorParameter(true).
                            parameterName("format").
                            ignoreAcceptHeader(true).
                            useJaf(false).
                    defaultContentType(MediaType.APPLICATION_JSON).
                    mediaType("xlsx", new MediaType("application","vnd.ms-excel"));
        }
    

    On Accept header of the request I added the value application/vnd.ms-excel.

    Finally, by calling the excel endpoint now with the needed format it generates the excel file properly.

    localhost:8080/students/excel/excel?format=xlsx