温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

使用RestTemplate时报错RestClientException怎么解决

发布时间:2021-08-19 11:24:39 来源:亿速云 阅读:881 作者:chen 栏目:开发技术

本篇内容主要讲解“使用RestTemplate时报错RestClientException怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“使用RestTemplate时报错RestClientException怎么解决”吧!

目录
  • 使用RestTemplate时报错RestClientException

    • 这是自己封装的一个发送请求的方法

    • 这是自定义的一个http信息Converter

  • RestTemplate的错误处理

    • 问题描述

    • ErrorHandler

    • 解决办法

使用RestTemplate时报错RestClientException

这是自己封装的一个发送请求的方法

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格式发送到服务器

  • multipart/form-data:需要在表单中进行文件上传时,就需要使用该格式

RestTemplate的错误处理

我们的项目属于微服务架构,两个基础的服务分别是网关和认证鉴权服务。

在前端访问后台服务的时候,都经过网关转发,转发之前会进行鉴权认证,根据鉴权结果判断是否可以进行相应的请求转发。

问题描述

认证鉴权服务中,判断Token对应的人员是否有相应的权限,如果没有权限,返回401状态码并在响应体中传回错误信息。

网关与鉴权服务之间的服务调用通过RestTemplate进行(可以考虑转为Feign做声明式的服务调用),然而如果鉴权服务返回401的情况下,网关服务直接报出HttpClientErrorException,让人一头雾水。

实际上,答案都在源码中,看一下RestTemplate的源码就知晓了。

ErrorHandler

在RestTemplate中,有一个成员变量ResponseErrorHandler。

ResponseErrorHandler是一个接口,包括两个方法:

public interface ResponseErrorHandler {
    boolean hasError(ClientHttpResponse var1) throws IOException;
    void handleError(ClientHttpResponse var1) throws IOException;
}

这个接口有一个默认实现DefaultResponseErrorHandler。该方法中,判断是否发生error的方法hasError最终调用的方法如下:

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

很明显,根据响应状态吗为4xx或者5xx来认定发生了错误。而错误处理在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的状态吗会抛出HttpClientErrorException;5xx的状态码会抛出HttpServerErrorException。这也就是我们一开始遇到的问题的原因所在了。而在handleError中,执行了response.getBody(),这就导致我们后续获取不到响应体了,如果要获取的话,需要进行自定义相关处理。

解决办法

如果RestTemplate的应用场景比较统一,可以自定义ResponseErorHandler(派生自DefaultResponseErrorHandler)来接管错误处理,进行自己想要的处理。

而我们的网关中,对于页面跳转类的请求和Rest API类的请求,处理办法显然是不一样的。所以最终处理是catch异常,然后进行重定向的处理操作。

到此,相信大家对“使用RestTemplate时报错RestClientException怎么解决”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI