解決使用RestTemplate時報錯RestClientException的問題
使用RestTemplate時報錯RestClientException
這是自己封裝的一個發(fā)送請求的方法
public Map<String, Object> sendRequest(Map<String, Object> body,String sessionId,String url) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new BdMappingJackson2HttpMessageConverter());
Map<String, Object> map = new HashMap<>();
try {
ParameterizedTypeReference<Map<String, Object>> typeRef = new ParameterizedTypeReference<Map<String, Object>>() {
};
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.set("cookie", "SESSION="+sessionId);
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(body,headers);
ResponseEntity<Map<String, Object>> responseEntity = restTemplate.exchange(url, HttpMethod.POST,
requestEntity,typeRef);
map = responseEntity.getBody();
log.info(map.toString());
} catch (HttpStatusCodeException e) {
log.error(e.getResponseBodyAsString(), e);
map = JsonUtil.toMap(e.getResponseBodyAsString());
}
return map;
}
這是自定義的一個http信息Converter
public class BdMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
public BdMappingJackson2HttpMessageConverter(){
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.TEXT_HTML);
mediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
setSupportedMediaTypes(mediaTypes);
}
}
我遇到的第一個問題是這樣的
Could not write request: no suitable HttpMessageConverter found for request type [java.util.HashMap] and content type [application/octet-stream]
意思大概是無法寫入請求:找不到適用于請求類型[java.util.HashMap]和內容類型[application/octet stream]的HttpMessageConverter
它默認只處理application/json:JSON數據格式,這個二進制數據流格式不支持,所以我在那個自定義的Converter加上去了。
緊接著又遇到第二個問題
Could not extract response: no suitable HttpMessageConverter found for response type [java.util.Map<java.lang.String, java.lang.Object>] and content type [text/xml;charset=UTF-8]
無法提取響應:找不到適合于響應類型[java.util.Map<java.lang.String,java.lang.Object>]和內容類型[textml/charset=UTF-8]的HttpMessageConverter
這是那邊回調過來的Content-Type類型是text/xml它同樣解析不了,所以我把這個也加上去了,至此就ok了。
常見的Content-Type類型:
text/html:HTML格式text/plain:純文本格式image/png:png圖片格式application/json:JSON數據格式application/octet-stream:二進制流數據application/x-www-form-urlencoded:表單中默認的encType,表單數據被編碼為key/value格式發(fā)送到服務器multipart/form-data:需要在表單中進行文件上傳時,就需要使用該格式
RestTemplate的錯誤處理
我們的項目屬于微服務架構,兩個基礎的服務分別是網關和認證鑒權服務。
在前端訪問后臺服務的時候,都經過網關轉發(fā),轉發(fā)之前會進行鑒權認證,根據鑒權結果判斷是否可以進行相應的請求轉發(fā)。
問題描述
認證鑒權服務中,判斷Token對應的人員是否有相應的權限,如果沒有權限,返回401狀態(tài)碼并在響應體中傳回錯誤信息。
網關與鑒權服務之間的服務調用通過RestTemplate進行(可以考慮轉為Feign做聲明式的服務調用),然而如果鑒權服務返回401的情況下,網關服務直接報出HttpClientErrorException,讓人一頭霧水。
實際上,答案都在源碼中,看一下RestTemplate的源碼就知曉了。
ErrorHandler
在RestTemplate中,有一個成員變量ResponseErrorHandler。
ResponseErrorHandler是一個接口,包括兩個方法:
public interface ResponseErrorHandler {
boolean hasError(ClientHttpResponse var1) throws IOException;
void handleError(ClientHttpResponse var1) throws IOException;
}
這個接口有一個默認實現(xiàn)DefaultResponseErrorHandler。該方法中,判斷是否發(fā)生error的方法hasError最終調用的方法如下:
protected boolean hasError(HttpStatus statusCode) {
return statusCode.series() == Series.CLIENT_ERROR || statusCode.series() == Series.SERVER_ERROR;
}
很明顯,根據響應狀態(tài)嗎為4xx或者5xx來認定發(fā)生了錯誤。而錯誤處理在handleError中:
public void handleError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = this.getHttpStatusCode(response);
switch(null.$SwitchMap$org$springframework$http$HttpStatus$Series[statusCode.series().ordinal()]) {
case 1:
throw new HttpClientErrorException(statusCode, response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
case 2:
throw new HttpServerErrorException(statusCode, response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
default:
throw new RestClientException("Unknown status code [" + statusCode + "]");
}
}
可見,4xx的狀態(tài)嗎會拋出HttpClientErrorException;5xx的狀態(tài)碼會拋出HttpServerErrorException。這也就是我們一開始遇到的問題的原因所在了。而在handleError中,執(zhí)行了response.getBody(),這就導致我們后續(xù)獲取不到響應體了,如果要獲取的話,需要進行自定義相關處理。
解決辦法
如果RestTemplate的應用場景比較統(tǒng)一,可以自定義ResponseErorHandler(派生自DefaultResponseErrorHandler)來接管錯誤處理,進行自己想要的處理。
而我們的網關中,對于頁面跳轉類的請求和Rest API類的請求,處理辦法顯然是不一樣的。所以最終處理是catch異常,然后進行重定向的處理操作。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Java中如何給List進行排序(這7種方法輕松實現(xiàn))
在Java項目中可能會遇到給出一些條件,將List元素按照給定條件進行排序的情況,這篇文章主要給大家介紹了關于Java中如何給List進行排序的相關資料,通過文中介紹的這7種方法可以輕松實現(xiàn),需要的朋友可以參考下2023-10-10
Java DriverManager.getConnection()獲取數據庫連接
這篇文章主要介紹了Java DriverManager.getConnection()獲取數據庫連接,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-01-01
Java基于redis和mysql實現(xiàn)簡單的秒殺(附demo)
這篇文章主要介紹了Java基于redis和mysql實現(xiàn)簡單的秒殺(附demo),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-02-02

