I have some webservice
endpoints that should offer json
data by default. Therefore configuring as follows:
@Configuration
public class ContentNegotiationConfiguration implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
}
Problem: now I want to create an endpoint that offers a file download (thus is not json).
@RestController
public class FileServlet {
@GetMapping(value = "/docs/{filename}", consumes = MediaType.ALL_VALUE, produces = APPLICATION_OCTET_STREAM_VALUE)
public Object download(@Pathvariable filename) {
File file = fileservice.resolve(filename);
return new FileSystemResource(file);
}
}
Accessing this endpoint from the browser works fine. I can download the files.
But: when using native clients that are not setting any http headers like content-type, accept-header etc, the access fails with:
WARN o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver: Resolved
[org.springframework.web.HttpMediaTypeNotAcceptableException:
Could not find acceptable representation]
All of them result in the exception:
curl localhost:8080/docs/testfile.txt
curl -O localhost:8080/docs/testfile.txt
wget localhost:8080/docs/testfile.txt
This is probably because I set the default content type to json
above in ContentNegotiationConfiguration
. I cannot change that due to all the other endpoints that should be json by default.
Question: how can I explicit ignore that default json setting on that single endpoint, and always just offer the download stream?
With the hint from @M. Deinum, I got it working as follows:
@GetMapping(value = "/docs/{filename}")
public void download(@Pathvariable filename) {
FileSystemResource file = new FileSystemResource(fileservice.resolve(filename));
rsp.setHeader("Content-Disposition", "attachment; filename=" + file.getFilename());
ResourceHttpMessageConverter handler = new ResourceHttpMessageConverter();
handler.write(file, MediaType.APPLICATION_OCTET_STREAM, new ServletServerHttpResponse(rsp));
}
That way writing directly to the stream bypassing the content negotiation, while still relying on the Spring class ResourceHttpMessageConverter
for not having to implement the response writer myself.