2013-04-24 14 views
12

Ho un servizio di assistenza che invia un errore 404 quando le risorse non vengono trovate. Qui la fonte del mio controller e l'eccezione che inviano Http 404.Spring MVC - RestTemplate eccezione di avvio quando avviene l'http 404

@Controller 
@RequestMapping("/site") 
public class SiteController 
{ 

    @Autowired 
    private IStoreManager storeManager; 

    @RequestMapping(value = "/stores/{pkStore}", method = RequestMethod.GET, produces = "application/json") 
    @ResponseBody 
    public StoreDto getStoreByPk(@PathVariable long pkStore) {  
     Store s = storeManager.getStore(pkStore); 
     if (null == s) { 
      throw new ResourceNotFoundException("no store with pkStore : " + pkStore); 
     } 
     return StoreDto.entityToDto(s);  

    } 
} 

@ResponseStatus(value = HttpStatus.NOT_FOUND) 
public class ResourceNotFoundException extends RuntimeException 
{  
    private static final long serialVersionUID = -6252766749487342137L;  
    public ResourceNotFoundException(String message) { 
     super(message); 
    }  
} 

Quando provo a chiamarlo con RestTemplate con questo codice:

ResponseEntity<StoreDto> r = restTemplate.getForEntity(url, StoreDto.class, m); 
System.out.println(r.getStatusCode()); 
System.out.println(r.getBody()); 

ricevo questa eccezione:

org.springframework.web.client.RestTemplate handleResponseError 
ATTENTION: GET request for "http://........./stores/99" resulted in 404 (Introuvable); invoking error handler 
org.springframework.web.client.HttpClientErrorException: 404 Introuvable 

Stavo pensando che posso esplorare il mio oggetto responseEntity e fare alcune cose con lo statusCode. Ma l'eccezione è il lancio e la mia app non funziona.

Esiste una configurazione specifica per restTemplate per non inviare eccezioni ma compilare ResponseEntity.

Grazie mille per l'aiuto.

-

Loïc

+0

sono in grado di accedere al servizio resto attraverso il browser? o è anche quello che lancia un 404? – Akshay

+0

Hai mai trovato la risposta a questo? – leojh

risposta

21

Per quanto io sappia, non è possibile ottenere un ResponseEntity reale, ma il codice di stato e il corpo (se presente) può essere ottenuto dal l'eccezione:

try { 
     ResponseEntity<StoreDto> r = restTemplate.getForEntity(url, StoreDto.class, m); 
    } 
    catch (final HttpClientErrorException e) { 
     System.out.println(e.getStatusCode()); 
     System.out.println(e.getResponseBodyAsString()); 
    } 
11

RESTTemplate è abbastanza deficiente in questa zona IMO. C'è un buon post sul blog qui su come si possa estrarre il corpo della risposta quando hai ricevuto un errore:

http://springinpractice.com/2013/10/07/handling-json-error-object-responses-with-springs-resttemplate

Ad oggi v'è una notevole richiesta di JIRA che il modello prevede la possibilità di estrarre il corpo della risposta:

https://jira.spring.io/browse/SPR-10961

il problema con la risposta di Squatting orso è che si dovrebbe interrogare il codice di stato all'interno del blocco catch per esempio se si sta solo voler affrontare 404 di

Ecco come ho risolto il problema nel mio ultimo progetto. Potrebbero esserci modi migliori e la mia soluzione non estrae affatto ResponseBody.

public class ClientErrorHandler implements ResponseErrorHandler 
{ 
    @Override 
    public void handleError(ClientHttpResponse response) throws IOException 
    { 
     if (response.getStatusCode() == HttpStatus.NOT_FOUND) 
     { 
      throw new ResourceNotFoundException(); 
     } 

     // handle other possibilities, then use the catch all... 

     throw new UnexpectedHttpException(response.getStatusCode()); 
    } 

    @Override 
    public boolean hasError(ClientHttpResponse response) throws IOException 
    { 
     if ((response.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR) 
     || (response.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR)) 
     { 
      return true; 
     } 
     return false; 
    } 

}

Il ResourceNotFoundException e UnexpectedHttpException sono mie eccezioni unchecked.

La durante la creazione del modello resto:

RestTemplate template = new RestTemplate(); 
    template.setErrorHandler(new ClientErrorHandler()); 

Ora abbiamo leggermente più ordinato costruire quando si effettua una richiesta:

try 
    { 
     HttpEntity response = template.exchange("http://localhost:8080/mywebapp/customer/100029", 
             HttpMethod.GET, requestEntity, String.class); 
     System.out.println(response.getBody()); 
    } 
    catch (ResourceNotFoundException e) 
    { 
     System.out.println("Customer not found"); 
    } 
+0

In realtà non è necessario il try ... catch part dopo aver impostato il gestore. – eosimosu

+0

Cosa provare ... parte dopo aver impostato il gestore? ResourceNotFoundException? È il mio tipo di eccezione, non ne ho bisogno, ma lo voglio. –

+0

Vero, ha senso – eosimosu

0

Recentemente ho avuto un caso d'uso per questo.La mia soluzione:

public class MyErrorHandler implements ResponseErrorHandler { 

@Override 
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException { 
    return hasError(clientHttpResponse.getStatusCode()); 
} 

@Override 
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException { 
    HttpStatus statusCode = clientHttpResponse.getStatusCode(); 
    MediaType contentType = clientHttpResponse 
     .getHeaders() 
     .getContentType(); 
    Charset charset = contentType != null ? contentType.getCharset() : null; 
    byte[] body = FileCopyUtils.copyToByteArray(clientHttpResponse.getBody()); 

    switch (statusCode.series()) { 
     case CLIENT_ERROR: 
      throw new HttpClientErrorException(statusCode, clientHttpResponse.getStatusText(), body, charset); 
     case SERVER_ERROR: 
      throw new HttpServerErrorException(statusCode, clientHttpResponse.getStatusText(), body, charset); 
     default: 
      throw new RestClientException("Unknown status code [" + statusCode + "]"); 
    } 

} 

private boolean hasError(HttpStatus statusCode) { 
    return (statusCode.series() == HttpStatus.Series.CLIENT_ERROR || 
     statusCode.series() == HttpStatus.Series.SERVER_ERROR); 
} 
0

È possibile creare il proprio wrapper RestTemplate che non genera eccezioni, ma restituisce una risposta con il codice di stato ricevuto. (Si potrebbe anche restituire il corpo, ma che sarebbe smettere di essere type-safe, quindi nel codice sotto il corpo rimane semplicemente null.)

/** 
* A Rest Template that doesn't throw exceptions if a method returns something other than 2xx 
*/ 
public class GracefulRestTemplate extends RestTemplate { 
    private final RestTemplate restTemplate; 

    public GracefulRestTemplate(RestTemplate restTemplate) { 
     super(restTemplate.getMessageConverters()); 
     this.restTemplate = restTemplate; 
    } 

    @Override 
    public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException { 
     return withExceptionHandling(() -> restTemplate.getForEntity(url, responseType)); 
    } 

    @Override 
    public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException { 
     return withExceptionHandling(() -> restTemplate.postForEntity(url, request, responseType)); 
    } 

    private <T> ResponseEntity<T> withExceptionHandling(Supplier<ResponseEntity<T>> action) { 
     try { 
      return action.get(); 
     } catch (HttpClientErrorException ex) { 
      return new ResponseEntity<>(ex.getStatusCode()); 
     } 
    } 
} 
Problemi correlati