I have the following image download method in my controller (Spring 4.1):
@RequestMapping(value = "/get/image/{id}/{fileName}", method=RequestMethod.GET)
public @ResponseBody byte[] showImageOnId(@PathVariable("id") String id, @PathVariable("fileName") String fileName) {
setContentType(fileName); //sets contenttype based on extention of file
return getImage(id, fileName);
}
The following ControllerAdvice
method should handle a non-existing file and return a json error response:
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public @ResponseBody Map<String, String> handleResourceNotFoundException(ResourceNotFoundException e) {
Map<String, String> errorMap = new HashMap<String, String>();
errorMap.put("error", e.getMessage());
return errorMap;
}
My JUnit test works flawless
(EDIT this is because of extention .bla : this also works on appserver):
@Test
public void testResourceNotFound() throws Exception {
String fileName = "bla.bla";
mvc.perform(MockMvcRequestBuilders.get("/get/image/bla/" + fileName)
.with(httpBasic("test", "test")))
.andDo(print())
.andExpect(jsonPath("$error").value(Matchers.startsWith("Resource not found")))
.andExpect(status().is(404));
}
and gives the following output:
MockHttpServletResponse:
Status = 404
Error message = null
Headers = {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=[application/json]}
Content type = application/json
Body = {"error":"Resource not found: bla/bla.bla"}
Forwarded URL = null
Redirected URL = null
Cookies = []
However on my appserver i get the following error message when trying to donwload non existing image:
(EDIT this is because of extention .jpg : this also fails on JUnit test with .jpg extention):
ERROR org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - Failed to invoke @ExceptionHandler method: public java.util.Map<java.lang.String, java.lang.String> nl.krocket.ocr.web.controller.ExceptionController.handleResourceNotFoundException(nl.krocket.ocr.web.backing.ResourceNotFoundException)
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
I have configured messageconverters in my mvc configuration as follows:
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(mappingJackson2HttpMessageConverter());
converters.add(byteArrayHttpMessageConverter());
}
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//objectMapper.registerModule(new JSR310Module());
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper);
converter.setSupportedMediaTypes(getJsonMediaTypes());
return converter;
}
@Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
arrayHttpMessageConverter.setSupportedMediaTypes(getImageMediaTypes());
return arrayHttpMessageConverter;
}
What am i missing? And why is the JUnit test working?
Exception HandlerThe @ExceptionHandler is an annotation used to handle the specific exceptions and sending the custom responses to the client. Define a class that extends the RuntimeException class. You can define the @ExceptionHandler method to handle the exceptions as shown.
You need to decide how the media type of the response should be determined by Spring. That can be done in several ways:
By default, Spring looks at the extension rather than the Accept
header. This behaviour can be changed if you implement a @Configuration
class that extends WebMvcConfigurerAdapter
(or since Spring 5.0 simply implement WebMvcConfigurer
. There you can override configureContentNegotiation(ContentNegotiationConfigurer configurer)
and configure the ContentNegotiationConfigurer
to your needs, eg. by calling
ContentNegotiationConfigurer#favorParameter
ContentNegotiationConfigurer#favorPathExtension
If you set both to false
, then Spring will look at the Accept
header. Since your client can say Accept: image/*,application/json
and handle both, Spring should be able to return either the image or the error JSON.
See this Spring tutorial on content negotiation for more information and examples.
Pay attention to your HTTP Accept header. For example, if your controller produces "application/octet-stream" (in response), your Accept header should NOT be "application/json" (in request):
@GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void download(HttpServletResponse response) {}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With