J'ai la méthode de téléchargement d'image suivante dans mon contrôleur (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);
}
La méthode ControllerAdvice
suivante doit gérer un fichier inexistant et renvoyer une réponse d'erreur json:
@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;
}
Mon test JUnit fonctionne parfaitement
( EDIT c'est à cause de l'extension .bla: cela fonctionne aussi sur 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));
}
et donne la sortie suivante:
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 = []
Cependant, sur mon serveur d'applications, j'obtiens le message d'erreur suivant lorsque j'essaie de télécharger une image non existante:
( EDIT c'est à cause de l'extension .jpg: cela échoue également sur le test JUnit avec l'extension .jpg):
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
J'ai configuré les convertisseurs de messages dans ma configuration mvc comme suit:
@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;
}
Qu'est-ce que je rate? Et pourquoi le test JUnit fonctionne-t-il?
Vous devez décider comment le type de support de la réponse doit être déterminé par Spring. Cela peut se faire de plusieurs manières:
Par défaut, Spring regarde l'extension plutôt que l'en-tête Accept
. Ce comportement peut être modifié si vous implémentez une classe @Configuration
Qui étend WebMvcConfigurerAdapter
(ou depuis Spring 5.0 implémentez simplement WebMvcConfigurer
=. Là, vous pouvez remplacer configureContentNegotiation(ContentNegotiationConfigurer configurer)
et configurer ContentNegotiationConfigurer
selon vos besoins, par exemple en appelant
ContentNegotiationConfigurer#favorParameter
ContentNegotiationConfigurer#favorPathExtension
Si vous définissez les deux sur false
, Spring examinera l'en-tête Accept
. Étant donné que votre client peut dire Accept: image/*,application/json
Et gérer les deux, Spring devrait être en mesure de renvoyer l'image ou l'erreur JSON.
Voir ce tutoriel de printemps sur la négociation de contenu pour plus d'informations et d'exemples.
Faites attention à votre en-tête HTTP Accept. Par exemple, si votre contrôleur produit "application/octet-stream" (en réponse), votre en-tête Accept ne doit PAS être "application/json" (en demande):
@GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void download(HttpServletResponse response) {}