Spring 中 RestTemplate使用详解
Spring 中 RestTemplate使用详解
1、简介
RestTemplate
是一个执行HTTP
请求的同步阻塞式工具类,它仅仅只是在 HTTP
客户端库(例如 JDK HttpURLConnection
,Apache HttpComponents
,okHttp
等)基础上,封装了更加简单易用的模板方法 API,方便程序员利用已提供的模板方法发起网络请求和处理,能很大程度上提升我们的开发效率。RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。2、SpringBoot 环境下使用 RestTemplate
2.1、maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
同时,将RestTemplate
配置初始化为一个Bean
@Configuration
public class RestTemplateConfig {
/**
* 没有实例化RestTemplate时,初始化RestTemplate
*/
@ConditionalOnMissingBean(RestTemplate.class)
@Bean
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
}
注意,这种初始化方法,是使用了JDK
自带的 HttpURLConnection
作为底层HTTP
客户端实现。当然,我们还可以修改RestTemplate
默认的客户端,例如将其改成HttpClient
客户端,方式如下:
@Configuration
public class RestTemplateConfig {
@ConditionalOnMissingBean(RestTemplate.class)
@Bean
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());
return restTemplate;
}
/**
* 使用HttpClient作为底层客户端
*/
private ClientHttpRequestFactory getClientHttpRequestFactory() {
int timeout = 5000;
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout)
.setConnectionRequestTimeout(timeout)
.setSocketTimeout(timeout)
.build();
CloseableHttpClient client = HttpClientBuilder
.create()
.setDefaultRequestConfig(config)
.build();
return new HttpComponentsClientHttpRequestFactory(client);
}
}
在需要使用RestTemplate
的位置,注入并使用即可!
@Autowired
private RestTemplate restTemplate;
网上的各种HTTP
客户端性能以及易用程度评测来看,OkHttp
优于 Apache的HttpClient
、Apache的HttpClient
优于HttpURLConnection
。
因此,我们还可以通过如下方式,将底层的http
客户端换成OkHttp
/**
* 使用OkHttpClient作为底层客户端
* @return
*/
private ClientHttpRequestFactory getClientHttpRequestFactory(){
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.build();
return new OkHttp3ClientHttpRequestFactory(okHttpClient);
}
3、API 实践
RestTemplate
最大的特色就是对各种网络请求方式做了包装,能极大的简化开发人员的工作量,下面我们以GET、POST、PUT、DELETE、文件上传与下载
为例,分别介绍各个API的使用方式。
3.1 GET请求
通过RestTemplate
发送HTTP GET
协议请求,经常使用到的方法有两个:
getForObject()
:返回值是HTTP
协议的响应体getForEntity()
:返回的是ResponseEntity
,ResponseEntity
是对HTTP
响应的封装,除了包含响应体,还包含HTTP
状态码、contentType、contentLength、Header
等信息
在Spring Boot
环境下写一个单元测试用例,首先创建一个Api
接口,然后编写单元测试进行服务测试。
3.1.1 不带参请求
接口提供方:
@RestController
public class TestController {
/**
* 不带参的get请求
* @return
*/
@RequestMapping(value = "testGet", method = RequestMethod.GET)
public ResponseBean testGet(){
ResponseBean result = new ResponseBean();
result.setCode("200");
result.setMsg("请求成功,方法:testGet");
return result;
}
}
public class ResponseBean {
private String code;
private String msg;
省去getset方法
}
调用方:
@Autowired
private RestTemplate restTemplate;
/**
* 单元测试(不带参的get请求)
*/
@Test
public void testGet(){
//请求地址
String url = "http://localhost:8080/testGet";
//发起请求,直接返回对象
ResponseBean responseBean = restTemplate.getForObject(url, ResponseBean.class);
System.out.println(responseBean.toString());
}
3.1.2 带参的get请求(使用占位符号传参)
接口提供方:
@RestController
public class TestController {
/**
* 带参的get请求(restful风格)
* @return
*/
@RequestMapping(value = "testGetByRestFul/{id}/{name}", method = RequestMethod.GET)
public ResponseBean testGetByRestFul(@PathVariable(value = "id") String id, @PathVariable(value = "name") String name){
ResponseBean result = new ResponseBean();
result.setCode("200");
result.setMsg("请求成功,方法:testGetByRestFul,请求参数id:" + id + "请求参数name:" + name);
return result;
}
}
接口调用方:
@Autowired
private RestTemplate restTemplate;
/**
* 单元测试(带参的get请求)
*/
@Test
public void testGetByRestFul(){
//请求地址
String url = "http://localhost:8080/testGetByRestFul/{1}/{2}";
//发起请求,直接返回对象(restful风格)
ResponseBean responseBean = restTemplate.getForObject(url, ResponseBean.class, "001", "张三");
System.out.println(responseBean.toString());
}
3.1.3 带参的get请求(restful风格)
接口提供方:
@RestController
public class TestController {
/**
* 带参的get请求(使用占位符号传参)
* @return
*/
@RequestMapping(value = "testGetByParam", method = RequestMethod.GET)
public ResponseBean testGetByParam(@RequestParam("userName") String userName, @RequestParam("userPwd") String userPwd){
ResponseBean result = new ResponseBean();
result.setCode("200");
result.setMsg("请求成功,方法:testGetByParam,请求参数userName:" + userName + ",userPwd:" + userPwd);
return result;
}
}
接口调用方:
@Autowired
private RestTemplate restTemplate;
/**
* 单元测试(带参的get请求)
*/
@Test
public void testGetByParam(){
//请求地址
String url = "http://localhost:8080/testGetByParam?userName={userName}&userPwd={userPwd}";
//请求参数
Map<String, String> uriVariables = new HashMap<>();
uriVariables.put("userName", "唐三藏");
uriVariables.put("userPwd", "123456");
//发起请求,直接返回对象(带参数请求)
ResponseBean responseBean = restTemplate.getForObject(url, ResponseBean.class, uriVariables);
System.out.println(responseBean.toString());
}
3.1.4 getForEntity使用示例
上面的所有的getForObject
请求传参方法,getForEntity
都可以使用,使用方法上也几乎是一致的,只是在返回结果接收的时候略有差别。使用ResponseEntity<T> responseEntity
来接收响应结果。用responseEntity.getBody()
获取响应体。
/**
* 单元测试
*/
@Test
public void testAllGet(){
//请求地址
String url = "http://localhost:8080/testGet";
//发起请求,返回全部信息
ResponseEntity<ResponseBean> response = restTemplate.getForEntity(url, ResponseBean.class);
// 获取响应体
System.out.println("HTTP 响应body:" + response.getBody().toString());
// 以下是getForEntity 比 getForObject多出来的内容
HttpStatus statusCode = response.getStatusCode();
int statusCodeValue = response.getStatusCodeValue();
HttpHeaders headers = response.getHeaders();
System.out.println("HTTP 响应状态:" + statusCode);
System.out.println("HTTP 响应状态码:" + statusCodeValue);
System.out.println("HTTP Headers信息:" + headers);
}
3.2、 POST请求
其实POST
请求方法和GET
请求方法上大同小异,RestTemplate
的POST
请求也包含两个主要方法:
postForObject()
:返回body
对象postForEntity()
:返回全部的信息
3.2.1 模拟表单请求
接口提供方:
@RestController
public class TestController {
/**
* 模拟表单请求,post方法测试
* @return
*/
@RequestMapping(value = "testPostByForm", method = RequestMethod.POST)
public ResponseBean testPostByForm(@RequestParam("userName") String userName,
@RequestParam("userPwd") String userPwd){
ResponseBean result = new ResponseBean();
result.setCode("200");
result.setMsg("请求成功,方法:testPostByForm,请求参数userName:" + userName + ",userPwd:" + userPwd);
return result;
}
}
接口调用方:
@Autowired
private RestTemplate restTemplate;
/**
* 模拟表单提交,post请求
*/
@Test
public void testPostByForm(){
//请求地址
String url = "http://localhost:8080/testPostByForm";
// 请求头设置,x-www-form-urlencoded格式的数据
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//提交参数设置
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("userName", "唐三藏");
map.add("userPwd", "123456");
// 组装请求体
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
//发起请求
ResponseBean responseBean = restTemplate.postForObject(url, request, ResponseBean.class);
System.out.println(responseBean.toString());
}
3.2.2 模拟表单请求(传递对象)
接口提供方:
@RestController
public class TestController {
/**
* 模拟表单请求,post方法测试
* @param request
* @return
*/
@RequestMapping(value = "testPostByFormAndObj", method = RequestMethod.POST)
public ResponseBean testPostByForm(RequestBean request){
ResponseBean result = new ResponseBean();
result.setCode("200");
result.setMsg("请求成功,方法:testPostByFormAndObj,请求参数:" + JSON.toJSONString(request));
return result;
}
}
public class RequestBean {
private String userName;
private String userPwd;
省去getset方法
}
接口调用方:
@Autowired
private RestTemplate restTemplate;
/**
* 模拟表单提交,post请求
*/
@Test
public void testPostByForm(){
//请求地址
String url = "http://localhost:8080/testPostByFormAndObj";
// 请求头设置,x-www-form-urlencoded格式的数据
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//提交参数设置
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("userName", "唐三藏");
map.add("userPwd", "123456");
// 组装请求体
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
//发起请求
ResponseBean responseBean = restTemplate.postForObject(url, request, ResponseBean.class);
System.out.println(responseBean.toString());
}
3.2.3 、模拟JSON请求
接口提供方:
@RestController
public class TestController {
/**
* 模拟JSON请求,post方法测试
* @param request
* @return
*/
@RequestMapping(value = "testPostByJson", method = RequestMethod.POST)
public ResponseBean testPostByJson(@RequestBody RequestBean request){
ResponseBean result = new ResponseBean();
result.setCode("200");
result.setMsg("请求成功,方法:testPostByJson,请求参数:" + JSON.toJSONString(request));
return result;
}
}
接口调用方:
@Autowired
private RestTemplate restTemplate;
/**
* 模拟JSON提交,post请求
*/
@Test
public void testPostByJson(){
//请求地址
String url = "http://localhost:8080/testPostByJson";
//入参
RequestBean request = new RequestBean();
request.setUserName("唐三藏");
request.setUserPwd("123456789");
//发送post请求,并打印结果,以String类型接收响应结果JSON字符串
ResponseBean responseBean = restTemplate.postForObject(url, request, ResponseBean.class);
System.out.println(responseBean.toString());
}
3.2.4 模拟页面重定向
模拟页面重定向,post
请求服务提供方:
@Controller
public class LoginController {
/**
* 重定向
* @param request
* @return
*/
@RequestMapping(value = "testPostByLocation", method = RequestMethod.POST)
public String testPostByLocation(@RequestBody RequestBean request){
return "redirect:index.html";
}
}
服务调用方:
@Autowired
private RestTemplate restTemplate;
/**
* 重定向,post请求
*/
@Test
public void testPostByLocation(){
//请求地址
String url = "http://localhost:8080/testPostByLocation";
//入参
RequestBean request = new RequestBean();
request.setUserName("唐三藏");
request.setUserPwd("123456789");
//用于提交完成数据之后的页面跳转,返回跳转url
URI uri = restTemplate.postForLocation(url, request);
System.out.println(uri.toString());
}
输出结果如下:
http://localhost:8080/index.html
3.3、 PUT请求
put
请求方法,可能很多人都没用过,它指的是修改一个已经存在的资源或者插入资源,该方法会向URL
代表的资源发送一个HTTP PUT
方法请求,示例如下:
@RestController
public class TestController {
/**
* 模拟JSON请求,put方法测试
* @param request
* @return
*/
@RequestMapping(value = "testPutByJson", method = RequestMethod.PUT)
public void testPutByJson(@RequestBody RequestBean request){
System.out.println("请求成功,方法:testPutByJson,请求参数:" + JSON.toJSONString(request));
}
}
调用方:
@Autowired
private RestTemplate restTemplate;
/**
* 模拟JSON提交,put请求
*/
@Test
public void testPutByJson(){
//请求地址
String url = "http://localhost:8080/testPutByJson";
//入参
RequestBean request = new RequestBean();
request.setUserName("唐三藏");
request.setUserPwd("123456789");
//模拟JSON提交,put请求
restTemplate.put(url, request);
}
3.4 、DELETE请求
与之对应的还有delete
方法协议,表示删除一个已经存在的资源,该方法会向URL
代表的资源发送一个HTTP DELETE
方法请求。
@RestController
public class TestController {
/**
* 模拟JSON请求,delete方法测试
* @return
*/
@RequestMapping(value = "testDeleteByJson", method = RequestMethod.DELETE)
public void testDeleteByJson(){
System.out.println("请求成功,方法:testDeleteByJson");
}
}
调用方:
@Autowired
private RestTemplate restTemplate;
/**
* 模拟JSON提交,delete请求
*/
@Test
public void testDeleteByJson(){
//请求地址
String url = "http://localhost:8080/testDeleteByJson";
//模拟JSON提交,delete请求
restTemplate.delete(url);
}
3.5 通用请求方法exchange方法
如果以上方法还不满足你的要求。在RestTemplate
工具类里面,还有一个exchange
通用协议请求方法,它可以发送GET、POST、DELETE、PUT、OPTIONS、PATCH等等HTTP
方法请求。
有时候,我们需要在请求中的 Head 中添加值或者将某些值通过 cookie 传给服务端,那么上面这种调用形式就不太满足要求了。
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("127.0.0.1:8080").
path("/test").build(true);
URI uri = uriComponents.toUri();
RequestEntity<JSONObject> requestEntity = RequestEntity.post(uri).
// 添加 cookie(这边有个问题,假如我们要设置 cookie 的生命周期,作用域等参数我们要怎么操作)
header(HttpHeaders.COOKIE,"key1=value1").
// 添加 header
header(("MyRequestHeader", "MyValue")
accept(MediaType.APPLICATION_JSON).
contentType(MediaType.APPLICATION_JSON).
body(requestParam);
ResponseEntity<JSONObject> responseEntity = restTemplate.exchange(requestEntity,JSONObject.class);
// 响应结果
JSONObject responseEntityBody = responseEntity.getBody();
3.6、文件上传与下载
除了经常用到的get
和post
请求以外,还有一个经常会碰到的场景,那就是文件的上传与下载,如果采用RestTemplate
,该怎么使用呢?
3.6.1 、文件上传
@RestController
public class FileUploadController {
private static final String UPLOAD_PATH = "/springboot-frame-example/springboot-example-resttemplate/";
/**
* 文件上传
* @param uploadFile
* @return
*/
@RequestMapping(value = "upload", method = RequestMethod.POST)
public ResponseBean upload(@RequestParam("uploadFile") MultipartFile uploadFile,
@RequestParam("userName") String userName) {
// 在 uploadPath 文件夹中通过用户名对上传的文件归类保存
File folder = new File(UPLOAD_PATH + userName);
if (!folder.isDirectory()) {
folder.mkdirs();
}
// 对上传的文件重命名,避免文件重名
String oldName = uploadFile.getOriginalFilename();
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
//定义返回视图
ResponseBean result = new ResponseBean();
try {
// 文件保存
uploadFile.transferTo(new File(folder, newName));
result.setCode("200");
result.setMsg("文件上传成功,方法:upload,文件名:" + newName);
} catch (IOException e) {
e.printStackTrace();
result.setCode("500");
result.setMsg("文件上传失败,方法:upload,请求文件:" + oldName);
}
return result;
}
}
调用方:
@Autowired
private RestTemplate restTemplate;
/**
* 文件上传,post请求
*/
@Test
public void upload(){
//需要上传的文件
String filePath = "/Users/panzhi/Desktop/Jietu20220205-194655.jpg";
//请求地址
String url = "http://localhost:8080/upload";
// 请求头设置,multipart/form-data格式的数据
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
//提交参数设置
MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
param.add("uploadFile", new FileSystemResource(new File(filePath)));
//服务端如果接受额外参数,可以传递
param.add("userName", "张三");
// 组装请求体
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(param, headers);
//发起请求
ResponseBean responseBean = restTemplate.postForObject(url, request, ResponseBean.class);
System.out.println(responseBean.toString());
}
但是我们发现上面的上传代码中,上传文件的类必须使用 FileSystemResource。有时我们会碰到这种情况:文件我们会从文件服务下载到内存中一个 InputStream 的形式存在,那此时在使用 FileSystemResource 就不行了。
// 自己实现了一个 Resource
public class InMemoryResource extends ByteArrayResource {
private final String filename;
private final long lastModified;
public InMemoryResource(String filename, String description, byte[] content, long lastModified) {
super(content, description);
this.lastModified = lastModified;
this.filename = filename;
}
@Override
public long lastModified() throws IOException {
return this.lastModified;
}
@Override
public String getFilename() {
return this.filename;
}
}
调整后的:
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
// 设置请求体,注意是 LinkedMultiValueMap
// 下面两个流从文件服务下载,这边省略(注意最后关闭流)
InputStream fis1 =
InMemoryResource resource = new InMemoryResource("file1.jpg","description1", FileCopyUtils.copyToByteArray(fis1),
System.currentTimeMillis());
MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
form.add("file", resource);
form.add("param1","value1");
HttpEntity<MultiValueMap<String, Object>> files = new HttpEntity<>(form, headers);
JSONObject s = restTemplate.postForObject(url, files, JSONObject.class);
3.6.2、 文件下载
提供方:@RestController
public class FileUploadController {
private static final String UPLOAD_PATH = "springboot-frame-example/springboot-example-resttemplate/";
/**
* 带参的get请求(restful风格)
* @return
*/
@RequestMapping(value = "downloadFile/{userName}/{fileName}", method = RequestMethod.GET)
public void downloadFile(@PathVariable(value = "userName") String userName,
@PathVariable(value = "fileName") String fileName,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
File file = new File(UPLOAD_PATH + userName + File.separator + fileName);
if (file.exists()) {
//获取文件流
FileInputStream fis = new FileInputStream(file);
//获取文件后缀(.png)
String extendFileName = fileName.substring(fileName.lastIndexOf('.'));
//动态设置响应类型,根据前台传递文件类型设置响应类型
response.setContentType(request.getSession().getServletContext().getMimeType(extendFileName));
//设置响应头,attachment表示以附件的形式下载,inline表示在线打开
response.setHeader("content-disposition","attachment;fileName=" + URLEncoder.encode(fileName,"UTF-8"));
//获取输出流对象(用于写文件)
OutputStream os = response.getOutputStream();
//下载文件,使用spring框架中的FileCopyUtils工具
FileCopyUtils.copy(fis,os);
}
}
}
调用方:
@Autowired
private RestTemplate restTemplate;
/**
* 小文件下载
* @throws IOException
*/
@Test
public void downloadFile() throws IOException {
String userName = "张三";
String fileName = "c98b677c-0948-46ef-84d2-3742a2b821b0.jpg";
//请求地址
String url = "http://localhost:8080/downloadFile/{1}/{2}";
//发起请求,直接返回对象(restful风格)
ResponseEntity<byte[]> rsp = restTemplate.getForEntity(url, byte[].class, userName, fileName);
System.out.println("文件下载请求结果状态码:" + rsp.getStatusCode());
// 将下载下来的文件内容保存到本地
String targetPath = "/Users/panzhi/Desktop/" + fileName;
Files.write(Paths.get(targetPath), Objects.requireNonNull(rsp.getBody(), "未获取到下载文件"));
}
@Autowired
private RestTemplate restTemplate;
/**
* 大文件下载
* @throws IOException
*/
@Test
public void downloadBigFile() throws IOException {
String userName = "张三";
String fileName = "c98b677c-0948-46ef-84d2-3742a2b821b0.jpg";
//请求地址
String url = "http://localhost:8080/downloadFile/{1}/{2}";
//定义请求头的接收类型
RequestCallback requestCallback = request -> request.getHeaders()
.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
//对响应进行流式处理而不是将其全部加载到内存中
String targetPath = "/Users/panzhi/Desktop/" + fileName;
restTemplate.execute(url, HttpMethod.GET, requestCallback, clientHttpResponse -> {
Files.copy(clientHttpResponse.getBody(), Paths.get(targetPath));
return null;
}, userName, fileName);
}
4、其他设置
1. 拦截器配置
RestTemplate 也可以设置拦截器做一些统一处理。这个功能感觉和 Spring MVC 的拦截器类似。配置也很简单:
class MyInterceptor implements ClientHttpRequestInterceptor{
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
logger.info("enter interceptor...");
return execution.execute(request,body);
}
}
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
MyInterceptor myInterceptor = new MyInterceptor();
List<ClientHttpRequestInterceptor> list = new ArrayList<>();
list.add(myInterceptor);
restTemplate.setInterceptors(list);
return restTemplate;
}
例如: 我们可以设置统一的请求头 :
//初始化RestTemplate,并加入spring的Bean工厂,由spring统一管理
@Bean(name = "restTemplate", value = "restTemplate")
@ConditionalOnMissingBean(RestTemplate.class)
public RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate(this.createFactory());
// 借助拦截器的方式来实现塞统一的请求头
ClientHttpRequestInterceptor interceptor = (httpRequest, bytes, execution) -> {
httpRequest.getHeaders().set("user-agent",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/83.0.4103.97 Safari/537.36");
return execution.execute(httpRequest, bytes);
};
restTemplate.getInterceptors().add(interceptor);
List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
//重新设置StringHttpMessageConverter字符集为UTF-8,解决中文乱码问题
HttpMessageConverter<?> converterTarget = null;
for (HttpMessageConverter<?> item : converterList) {
if (StringHttpMessageConverter.class == item.getClass()) {
converterTarget = item;
break;
}
}
if (null != converterTarget) {
converterList.remove(converterTarget);
}
converterList.add(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
//加入FastJson转换器
converterList.add(new FastJsonHttpMessageConverter());
return restTemplate;
}
2、ErrorHandler 配置
ErrorHandler 用来对调用错误对统一处理。
public class MyResponseErrorHandler extends DefaultResponseErrorHandler {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return super.hasError(response);
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
if (statusCode == null) {
throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), response.getStatusText(),
response.getHeaders(), getResponseBody(response), getCharset(response));
}
handleError(response, statusCode);
}
@Override
protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
switch (statusCode.series()) {
case CLIENT_ERROR:
HttpClientErrorException exp1 = new HttpClientErrorException(statusCode, response.getStatusText(),
response.getHeaders(), getResponseBody(response), getCharset(response));
logger.error("客户端调用异常",exp1);
throw exp1;
case SERVER_ERROR:
HttpServerErrorException exp2 = new HttpServerErrorException(statusCode, response.getStatusText(),
response.getHeaders(), getResponseBody(response), getCharset(response));
logger.error("服务端调用异常",exp2);
throw exp2;
default:
UnknownHttpStatusCodeException exp3 = new UnknownHttpStatusCodeException(statusCode.value(), response.getStatusText(),
response.getHeaders(), getResponseBody(response), getCharset(response));
logger.error("网络调用未知异常");
throw exp3;
}
}
}
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
MyResponseErrorHandler errorHandler = new MyResponseErrorHandler();
restTemplate.setErrorHandler(errorHandler);
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
// 通过下面代码可以添加新的 HttpMessageConverter
//messageConverters.add(new );
return restTemplate;
}
3、RestTemplate调用请求中文乱码解决办法
出现问题的原因:
RestTemplate在请求调用过程中,字符串利用的StringHttpMessageConverter类来进行报文转换。该Converter默认的字符集是ISO8859-1, 所以在包含中文请求时会出现乱码问题。
解决办法
在项目中注入RestTemplate的地方获取到要修改的转换器,并设置中文字符集即可,如
@Bean("restTemplate")
@ConditionalOnMissingBean(RestTemplate.class)
public RestTemplate restTemplateHasNoLoadBalanced() {
return createRestTemplate();
}
private RestTemplate createRestTemplate() {
RestTemplate restTemplate = new RestTemplateBuilder()
.setConnectTimeout(Duration.ofSeconds(5)).setReadTimeout(Duration.ofSeconds(30)).build();
List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
Iterator<HttpMessageConverter<?>> iterator = converters.iterator();
while (iterator.hasNext()) {
HttpMessageConverter<?> converter = iterator.next();
if (converter instanceof MappingJackson2XmlHttpMessageConverter){
// 删除xml序列化,内部微服务之间用json交互
iterator.remove();
}else if (converter instanceof StringHttpMessageConverter){
// 解决使用restTemplate中文乱码的问题
((StringHttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8);
}
}
return restTemplate;
}