博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Web开发 之使用Spring提供的RestTemplate访问服务
阅读量:2455 次
发布时间:2019-05-10

本文共 21472 字,大约阅读时间需要 71 分钟。

文章目录

RestTemplate简介

RestTemplate是Spring3.0后开始提供的用于访问 Rest 服务的轻量级客户端,相较于传统的HttpURLConnection、Apache HttpClient、OkHttp等框架,RestTemplate大大简化了发起HTTP请求以及处理响应的过程。

RestTemplate只是对其它Rest客户端的一个封装,本身并没有自己的实现。

RestTemplate默认实现是HttpURLConnection,这是JDK自带的REST客户端实现。
引入哪种Http客户端的Jar包,就会使用哪种Http客户端的实现。

相关Http客户端Maven依赖

com.squareup.okhttp3
okhttp
4.4.0
org.apache.httpcomponents
httpclient
4.5.11

RestTemplate配置

RestTemplate包含以下几个部分:

  • HttpMessageConverter 对象转换器
  • ClientHttpRequestFactory 默认是 JDK的HttpURLConnection
  • ResponseErrorHandler 异常处理
  • ClientHttpRequestInterceptor 请求拦截器

在这里插入图片描述

使用HttpClient作为实现

首先需要引入HttpClient的Maven依赖。

import org.apache.http.client.HttpClient;import org.apache.http.config.Registry;import org.apache.http.config.RegistryBuilder;import org.apache.http.conn.socket.ConnectionSocketFactory;import org.apache.http.conn.socket.PlainConnectionSocketFactory;import org.apache.http.conn.ssl.NoopHostnameVerifier;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;import org.apache.http.impl.client.HttpClientBuilder;import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;import org.apache.http.ssl.SSLContextBuilder;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.http.converter.StringHttpMessageConverter;import org.springframework.web.client.DefaultResponseErrorHandler;import org.springframework.web.client.RestTemplate;import javax.net.ssl.HostnameVerifier;import javax.net.ssl.SSLContext;import java.nio.charset.StandardCharsets;import java.security.KeyManagementException;import java.security.KeyStoreException;import java.security.NoSuchAlgorithmException;import java.util.List;@Configurationpublic class RestClientConfig {
private static final Logger logger = LoggerFactory.getLogger(RestClientConfig.class); // 连接池的最大连接数默认为0 @Value("${remote.maxTotalConnect:0}") private int maxTotalConnect; // 单个主机的最大连接数 @Value("${remote.maxConnectPerRoute:200}") private int maxConnectPerRoute; // 连接超时默认2s @Value("${remote.connectTimeout:2000}") private int connectTimeout; // 读取超时默认30s @Value("${remote.readTimeout:30000}") private int readTimeout; @Bean public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate(); restTemplate.setRequestFactory(clientHttpRequestFactory()); restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); // 设置编码格式为UTF-8 List
> converterList = restTemplate.getMessageConverters(); HttpMessageConverter
converterTarget = null; for (HttpMessageConverter
item : converterList) {
if (item.getClass() == StringHttpMessageConverter.class) {
converterTarget = item; break; } } if (converterTarget != null) {
converterList.remove(converterTarget); } HttpMessageConverter
converter = new StringHttpMessageConverter(StandardCharsets.UTF_8); converterList.add(1, converter); logger.info("-----restTemplate-----初始化完成"); return restTemplate; } @Bean public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {
try {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build(); httpClientBuilder.setSSLContext(sslContext); HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE; SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier); // 注册http和https请求 Registry
socketFactoryRegistry = RegistryBuilder.
create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", sslConnectionSocketFactory).build(); // 开始设置连接池 PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); // 最大连接数 poolingHttpClientConnectionManager.setMaxTotal(maxTotalConnect); // 同路由的并发数,路由是对maxTotal的细分 poolingHttpClientConnectionManager.setDefaultMaxPerRoute(maxConnectPerRoute); httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager); httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)); // 重试次数 HttpClient httpClient = httpClientBuilder.build(); // 数据读取超时时间 HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); // httpClient连接配置 // 连接超时 clientHttpRequestFactory.setConnectTimeout(connectTimeout); // 读取超时 clientHttpRequestFactory.setReadTimeout(readTimeout); // 连接不够用的等待时间 clientHttpRequestFactory.setConnectionRequestTimeout(20000); return clientHttpRequestFactory; } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
logger.error("初始化HTTP连接池出错", e); } return null; }}

使用OkHttp作为实现

首先需要引入OkHttp的Maven依赖。

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.client.ClientHttpRequestFactory;import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;import org.springframework.web.client.RestTemplate;@Configurationpublic class RestTemplateConfig {
@Bean public RestTemplate restTemplate(ClientHttpRequestFactory factory){
return new RestTemplate(factory); } @Bean public OkHttp3ClientHttpRequestFactory okHttp3ClientHttpRequestFactory(){
OkHttp3ClientHttpRequestFactory factory = new OkHttp3ClientHttpRequestFactory(); // 单位:毫秒 factory.setConnectTimeout(10000); factory.setReadTimeout(10000); factory.setWriteTimeout(10000); return factory; }}

发送GET请求

// 1-getForObject()User user1 = this.restTemplate.getForObject(uri, User.class);// 2-getForEntity()ResponseEntity
responseEntity1 = this.restTemplate.getForEntity(uri, User.class);HttpStatus statusCode = responseEntity1.getStatusCode();HttpHeaders header = responseEntity1.getHeaders();User user2 = responseEntity1.getBody(); // 3-exchange()RequestEntity requestEntity = RequestEntity.get(new URI(uri)).build();ResponseEntity
responseEntity2 = this.restTemplate.exchange(requestEntity, User.class);User user3 = responseEntity2.getBody();

发送POST请求

// 1-postForObject()User user1 = this.restTemplate.postForObject(uri, user, User.class);// 2-postForEntity()ResponseEntity
responseEntity1 = this.restTemplate.postForEntity(uri, user, User.class);// 3-exchange()RequestEntity
requestEntity = RequestEntity.post(new URI(uri)).body(user);ResponseEntity
responseEntity2 = this.restTemplate.exchange(requestEntity, User.class);

设置HTTP Header

// 1-Content-TypeRequestEntity
requestEntity = RequestEntity .post(new URI(uri)) .contentType(MediaType.APPLICATION_JSON) .body(user);// 2-AcceptRequestEntity
requestEntity = RequestEntity .post(new URI(uri)) .accept(MediaType.APPLICATION_JSON) .body(user);// 3-OtherRequestEntity
requestEntity = RequestEntity .post(new URI(uri)) .header("Authorization", "Basic " + base64Credentials) .body(user);

发送文件

MultiValueMap
multiPartBody = new LinkedMultiValueMap<>();multiPartBody.add("file", new ClassPathResource("/tmp/user.txt"));RequestEntity
> requestEntity = RequestEntity .post(uri) .contentType(MediaType.MULTIPART_FORM_DATA) .body(multiPartBody);

下载文件

// 小文件RequestEntity requestEntity = RequestEntity.get(uri).build();ResponseEntity
responseEntity = restTemplate.exchange(requestEntity, byte[].class);byte[] downloadContent = responseEntity.getBody(); // 大文件 ResponseExtractor
> responseExtractor = new ResponseExtractor
>() {
@Override public ResponseEntity
extractData(ClientHttpResponse response) throws IOException { File rcvFile = File.createTempFile("rcvFile", "zip"); FileCopyUtils.copy(response.getBody(), new FileOutputStream(rcvFile)); return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders()).body(rcvFile); }};File getFile = this.restTemplate.execute(targetUri, HttpMethod.GET, null, responseExtractor);

构建测试用的Http服务

import lombok.Data;@Datapublic class User {
private Long id; private String username; private Integer age;}import com.google.gson.Gson;import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.annotation.*;@RestController@RequestMapping("/")@Slf4jpublic class DemoController {
@GetMapping("demo/{id}/{userName}") public User get(@PathVariable Long id, @PathVariable String userName, Integer age){
log.info("id:{}, userName:{}, age:{}", id, userName, age); User user = new User(); user.setId(id); user.setUsername(userName); user.setAge(age); return user; } @PostMapping("demo") public User post(@RequestBody String json){
log.info("json:{}", json); Gson gson = new Gson(); User user = gson.fromJson(json, User.class); return user; }}

RestTemplate使用

GET请求

在RestTemplate中,发送一个GET请求,有两种方式:getForObject()getForEntity()

getForEntity

getForEntity方法的返回值是一个ResponseEntity<T>ResponseEntity<T>是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、响应消息体等。

在这里插入图片描述
示例:

String url = "http://127.0.0.1:8080/demo/{id}/{userName}?age=18";// 方式一ResponseEntity
userResponseEntity = restTemplate.getForEntity(url, User.class, 1001L, "Tom");User user = userResponseEntity.getBody();HttpStatus statusCode = userResponseEntity.getStatusCode();int statusCodeValue = userResponseEntity.getStatusCodeValue();HttpHeaders headers = userResponseEntity.getHeaders();System.out.println("user = " + user + "; statusCode = " + statusCode + "; statusCodeValue = " + statusCodeValue + "; headers = " + headers);// 方式二Map
urlParams = new HashMap<>();urlParams.put("id", 1001L);urlParams.put("userName", "James");ResponseEntity
userResponseEntity2 = restTemplate.getForEntity(url, User.class, urlParams);// 方式三UriComponents uriComponents = UriComponentsBuilder.fromUriString(url).build().expand(1001L, "James").encode();URI uri = uriComponents.toUri();ResponseEntity
entity = restTemplate.getForEntity(uri, User.class);

getForObject

getForObject函数实际上是对getForEntity函数的进一步封装,如果你只关注返回的消息体的内容,对其他信息都不关注,此时可以使用getForObject

在这里插入图片描述
示例:

//URL中的{id}占位符最终将会用方法的id参数来填充String url = "http://127.0.0.1:8083/demo/{id}/{userName}?age=18";Long id = 1001L;String userName = "Tom";//方式一:最后一个参数是大小可变的参数列表,每个参数都会按出现顺序插入到指定URL的占位符中User user = restTemplate.getForObject(url, User.class, id, userName);//方式二:将id参数放到Map中,并以id作为key,然后将这个Map作为最后一个参数Map
urlParams = new HashMap<>();urlParams.put("id", id);urlParams.put("userName", userName);User user2 = restTemplate.getForObject(url, User.class, urlParams);//方式二:参考getForEntity案例

POST请求

POST请求对应三个方法,postForObject()、postForEntity()和postForLocation(),每个方法同样对应有三个具体的重载方法。postForObject()、postForEntity()类似于getForObject()和postForEntity(),postForLocation()返回的是一个URI对象。

postForEntity

在这里插入图片描述

String url = "http://localhost:8080/demo2/{address}";// 方式一User user = new User();user.setAge(25);user.setUsername("王五");// 第4个参数可以是Object... uriVariables 或者 Map
uriVariablesResponseEntity
userResponseEntity = restTemplate.postForEntity(url, user, User.class, "SH");User userBody = userResponseEntity.getBody();HttpStatus statusCode = userResponseEntity.getStatusCode();int statusCodeValue = userResponseEntity.getStatusCodeValue();HttpHeaders headers = userResponseEntity.getHeaders();System.out.println("user = " + userBody + "; statusCode = " + statusCode + "; statusCodeValue = " + statusCodeValue + "; headers = " + headers);

postForObject

如果你只关注,返回的消息体,可以直接使用postForObject。用法和getForObject一致。

在这里插入图片描述

exchange通用请求

String getUrl = "http://127.0.0.1:8083/demo/{id}/{userName}?age=18";//GET资源//参数3是请求头部分;参数4是响应数据要转成对象;最后一个参数用于替换URL中的占位符ResponseEntity
userResponseEntity = restTemplate.exchange(getUrl, HttpMethod.GET, null, User.class, 1001L, "Tom");System.out.println("exchange = " + userResponseEntity + "; response body = " + userResponseEntity.getBody());String postUrl = "http://localhost:8083/demo2";//POST资源HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);String jsonParams = "{\"username\":\"123\",\"age\":23}";HttpEntity
httpEntity = new HttpEntity(jsonParams, headers);ResponseEntity
responseEntity = restTemplate.exchange(postUrl, HttpMethod.POST, httpEntity, User.class);System.out.println("exchange = " + responseEntity + "; response body = " + responseEntity.getBody());

POST请求,from-data传参

String url = "http://localhost:8083/demo2";HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.MULTIPART_FORM_DATA);MultiValueMap map = new LinkedMultiValueMap();map.add("id",1001L);HttpEntity httpEntity = new HttpEntity(map, headers);ResponseEntity
responseEntity = restTemplate.postForEntity(url, httpEntity, User.class);System.out.println(responseEntity.getBody());

RestTemplate携带参数和header头信息

String url="http://www.baidu.com";MultiValueMap
map = new LinkedMultiValueMap<>();map.add("id",id);HttpHeaders header = new HttpHeaders();// 需求需要传参为form-data格式header.setContentType(MediaType.MULTIPART_FORM_DATA);HttpEntity
> httpEntity = new HttpEntity<>(map, header);JSONObject response = restTemplate.postForObject(url, httpEntity, JSONObject.class);

URI 使用

URI uri = new URIBuilder().setScheme("http").setHost(ip).setPort(port).setPath("/platform/service")            .addParameter("user", "")            .addParameter("service", "")            .build();            UriComponents uriComponents = UriComponentsBuilder.fromUriString(url).build().expand(1001L, "James").encode();URI uri = uriComponents.toUri();

RestTemplate封装

package com.demo.spring;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpEntity;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpMethod;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import org.springframework.web.client.RestTemplate;import sun.misc.BASE64Encoder;import java.io.UnsupportedEncodingException;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.HashMap;import java.util.Map;@Componentpublic class RestUtil {
@Autowired private RestTemplate restTemplate; //一些自定义的请求头参数 public static final String supplierID = ""; public static final String interfacekey = ""; /** * DLT专用执行方法 * * @param param 请求参数:可以添加一些常量请求值 * @param url 访问的url * @param method 请求的方法 * @return */ public String execute(Map
param, String url, HttpMethod method) {
HttpHeaders headers = this.getDefaultHeader(); Map
requestor = this.getDefaultParam(); param.put("requestor", requestor); param.put("supplierID", supplierID); HttpEntity
> requestEntity = new HttpEntity<>(param, headers); ResponseEntity
response = restTemplate.exchange(url, method, requestEntity, String.class); return response.getBody(); } /** * 获取默认的头请求信息 * * @return */ public HttpHeaders getDefaultHeader() {
String timestamp = "" + System.currentTimeMillis(); String signature = EncoderByMd5(supplierID + timestamp + interfacekey); HttpHeaders headers = new HttpHeaders(); headers.add("signature", signature); headers.add("timestamp", timestamp); return headers; } /** * 获取默认的参数 * * @return */ public Map
getDefaultParam() { Map
defParam = new HashMap<>(); defParam.put("invoker", "xx"); defParam.put("operatorName", "xx"); return defParam; } /** * 通过MD5加密 * * @param str * @return */ public static String EncoderByMd5(String str) { if (str == null) { return null; } try { // 确定计算方法 MessageDigest md5 = MessageDigest.getInstance("MD5"); BASE64Encoder base64en = new BASE64Encoder(); // 加密后的字符串 return base64en.encode(md5.digest(str.getBytes("utf-8"))).toUpperCase(); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { return null; } } /** * get请求 * * @param url 请求的url * @param jsonData 请求的json * @return */ public String restGet(String url, String jsonData) { return request(url, jsonData, HttpMethod.GET); } /** * @param url 请求的url * @param jsonData json数据 * @param httpMethod * @return */ private String request(String url, String jsonData, HttpMethod httpMethod) { ResponseEntity
response = null; try { if (StringUtils.isEmpty(url)) { throw new IllegalArgumentException(); } HttpEntity
requestEntity = new HttpEntity
(jsonData); response = restTemplate.exchange(url, httpMethod, requestEntity, String.class); } catch (Exception ex) { ex.printStackTrace(); return ""; } return response.getBody(); } /** * Get请求获取实体类 * * @param url 请求的url * @param responseType 返回的类型 * @param parms 不限定个数的参数 * @param
泛型 * @return */ public
T getForEntity(String url, Class
responseType, Object... parms) { return (T) restTemplate.getForEntity(url, responseType, parms); } /** * Get请求 * * @param url * @param parm * @return */ public String get(String url, Map
parm) { return restTemplate.getForEntity(url, String.class, parm).getBody(); }}

手动指定转换器(HttpMessageConverter)

调用reseful接口传递的数据内容是json格式的字符串,返回的响应也是json格式的字符串。然而restTemplate.postForObject方法的请求参数RequestBean和返回参数ResponseBean却都是java类。是RestTemplate通过HttpMessageConverter自动帮我们做了转换的操作。

默认情况下RestTemplate自动帮我们注册了一组HttpMessageConverter用来处理一些不同的contentType的请求。

  • StringHttpMessageConverter来处理text/plain
  • MappingJackson2HttpMessageConverter来处理application/json
  • MappingJackson2XmlHttpMessageConverter来处理application/xml

可以在org.springframework.http.converter包下找到所有spring帮我们实现好的转换器。

如果现有的转换器不能满足我们的需求,我们还可以实现org.springframework.http.converter.HttpMessageConverter接口自己写一个。

选好一个HttpMessageConverter后并注册到我们的RestTemplate中:

RestTemplate restTemplate = new RestTemplate();//获取RestTemplate默认配置好的所有转换器List
> messageConverters = restTemplate.getMessageConverters();//默认的MappingJackson2HttpMessageConverter在第7个 先把它移除掉messageConverters.remove(6);//添加上GSON的转换器messageConverters.add(6, new GsonHttpMessageConverter());
com.google.code.gson
gson
2.8.5

上面的例子使用GsonHttpMessageConverter替换掉默认用来处理application/jsonMappingJackson2HttpMessageConverter

设置底层连接方式

创建一个RestTemplate的实例,可以像上述例子中简单地调用默认的无参数构造函数。这将使用java.net包中的标准Java类作为底层实现来创建HTTP请求。

但很多时候我们需要像传统的HttpClient那样设置HTTP请求的一些属性。RestTemplate使用了一种很偷懒的方式实现了这个需求,那就是直接使用一个HttpClient作为底层实现。

//生成一个设置了连接超时时间、请求超时时间、异常最大重试次数的httpClientRequestConfig config = RequestConfig.custom()        .setConnectionRequestTimeout(10000)        .setConnectTimeout(10000)        .setSocketTimeout(30000).build();HttpClientBuilder builder = HttpClientBuilder.create()        .setDefaultRequestConfig(config)        .setRetryHandler(new DefaultHttpRequestRetryHandler(5, false));HttpClient httpClient = builder.build();//使用httpClient创建一个ClientHttpRequestFactory的实现ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);//ClientHttpRequestFactory作为参数构造一个使用作为底层的RestTemplateRestTemplate restTemplate = new RestTemplate(requestFactory);

设置拦截器(ClientHttpRequestInterceptor)

有时候我们需要对请求做一些通用的拦截设置,这就可以使用拦截器进行处理。拦截器需要我们实现org.springframework.http.client.ClientHttpRequestInterceptor接口自己写。

举个简单的例子,写一个在header中根据请求内容和地址添加令牌的拦截器。

import org.springframework.http.HttpRequest;import org.springframework.http.client.ClientHttpRequestExecution;import org.springframework.http.client.ClientHttpRequestInterceptor;import org.springframework.http.client.ClientHttpResponse;import java.io.IOException;public class TokenInterceptor implements ClientHttpRequestInterceptor {
@Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
//请求地址 String checkTokenUrl = request.getURI().getPath(); //请求内容 String requestBody = new String(body); //生成令牌 String token = "token"; //将令牌放入请求header中 request.getHeaders().add("X-Auth-Token",token); request.getHeaders().add("X-Auth-Token","yq"); return execution.execute(request, body); }}

RestTemplate原理

参考

转载地址:http://tjchb.baihongyu.com/

你可能感兴趣的文章
readdir函数_PHP readdir()函数与示例
查看>>
数字拆分为斐波那契数列_检查数字是否为斐波那契
查看>>
大数据 java 代码示例_Java变量类型与示例
查看>>
c语言条件语句示例_PHP中的条件语句和示例
查看>>
pl/sql中的赋值运算符_如何在SQL中使用AND / OR运算符?
查看>>
操作系统磁盘调度_磁盘调度| 操作系统
查看>>
sql与nosql_SQL与NoSQL之间的差异
查看>>
可编程ic卡 通用吗_8255可编程IC
查看>>
icse ccf_ICSE的完整形式是什么?
查看>>
java bitset_Java BitSet and()方法与示例
查看>>
date.after方法_Java Date after()方法与示例
查看>>
ruby 嵌套函数_Ruby嵌套直到循环带有示例
查看>>
JavaScript中的地图与对象
查看>>
krsort_PHP krsort()函数与示例
查看>>
Python字符串格式:%vs.format
查看>>
android淡入淡出动画_在Android中淡入动画示例
查看>>
random.next_Java Random next()方法与示例
查看>>
java uuid静态方法_Java UUID equals()方法与示例
查看>>
ruby hash方法_Ruby中带有示例的Hash.invert方法
查看>>
ruby hash方法_Ruby中带有示例的Hash.select方法
查看>>