Spring boot restTemplate
Posted xiaodujava
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring boot restTemplate相关的知识,希望对你有一定的参考价值。
Spring boot restTemplate
上一节 spring boot 数据库连接池
源码
Spring boot restTemplate
简介
restTemplate 是spring 提供的进行http 访问的工具;spring boot 中已经配置好了;
springboot 中 对 restTemplate 的支持
在 RestTemplateAutoConfiguration 中可以看到 已经创建了 bean, RestTemplateBuilder (注意springboot版本,我的为2.1.6)
@Bean
@ConditionalOnMissingBean
public RestTemplateBuilder restTemplateBuilder() {
RestTemplateBuilder builder = new RestTemplateBuilder();
HttpMessageConverters converters = this.messageConverters.getIfUnique();
if (converters != null) {
builder = builder.messageConverters(converters.getConverters());
}
List<RestTemplateCustomizer> customizers = this.restTemplateCustomizers.orderedStream()
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(customizers)) {
builder = builder.customizers(customizers);
}
return builder;
}
在项目中使用,配置RestTemplate
添加配置类, 配置 restTemplate
@Configuration
@ConditionalOnClass(RestTemplate.class)
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder
// 配置 readTimeout
.setReadTimeout(Duration.ofSeconds(5))
// 配置 连接超时
.setConnectTimeout(Duration.ofSeconds(2)).build();
}
}
项目中使用 只需要引入一下即可
@Autowired
private RestTemplate restTemplate;
restTemplate 重要参数
url: 请求地址
responseType: 返回值类型
uriVariables: url 变量值
Object request: 请求体;可以是个httpEntity;若为其他,则会创建一个httpEntity ,request为 httpEntity 的body
HttpEntity: 请求参数,可以设置body, headers
responseEnity: 请求返回值,包含具体的body, headers, httpstatus 等http信息
restTemplate GET POST PUT DELETE 操作 传参
get 请求
- getForObject
// 接收url 和 responseType
restTemplate.getForObject(JDBC_DEMO_URL + "/user/" + id, UserEntity.class);
// url传参; 使用 模板占位符 {index};进行传参,Object... uriVariables 按照index顺序进行传参
// 例如 url = /user/batch?ids={0}&ids={1}, 参数 uriVariables可变数组按照顺序传参;
// 注意: 这里的 collect 是我封装好的 = ‘?ids={index}’;例如 /user/batch?ids={0}&ids={1}
restTemplate.getForObject(JDBC_DEMO_URL + "/user/batch" + collect, List.class, ids);
// url传参, 同时模板占位符 {feild}进行传参, 使用 map uriVariables 进行参数赋值
// 例如 url = /user/batch?uName={name}&password={password};
// params = new Map(); put(name,name),put(password,password)
restTemplate.getForObject(JDBC_DEMO_URL + "/user/batch" + collect, List.class, params);
- getForEntity
getForEntity 和 getForObject 使用方式一样;只是返回的类型不一样;
getForEntity 返回 responseEntity, 可以有更具体的信息, body, headers, httpstatus 等信息;
ForENtity 和ForObject 请求使用是一样的 只是返回值不同;以下只使用其中的一种。
Post 请求
postForObject
public List<String> batchAddUser(@RequestBody List<UserEntity> userEntities) throws Exception {
return restTemplate.postForObject(JDBC_DEMO_URL + "/user/batch", userEntities, List.class);
}
参数userEntities; 是请求体,会封装到httpEntity 的body中;
post 请求的所有方法: 具体的参数可以参考restTemplate 重要参数
Put 请求
restTemplate.put(JDBC_DEMO_URL + "/user", userEntity);
delete 请求
Map<String, String> map = new HashMap<>();
map.put("id", id);
// 使用{field}占位符,进行参数映射
restTemplate.delete(JDBC_DEMO_URL + "/user/{id}", map);
}
使用 exchange
restTemplate.exchange(JDBC_DEMO_URL + "/user", HttpMethod.PUT, new HttpEntity<>(userEntity), Void.class);
总结: restTemplate 进行http 请求使用很简单,主要注意 1.使用占位符进行参数赋值。 2. 对请求参数 request 的使用, 是使用 实体类,还是httpEntity; 使用还是很简单的 见名知意。
restTemplate Form表单提交
form 表单提交,我们需要传递一个httpEntity ,并设置 body 为 multivalueMap;
public String addUser(@RequestBody UserEntity userEntity) throws Exception {
// 模拟form 表单提交,form 表单提交body 需要为 MultiValueMap
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, Object> p = new LinkedMultiValueMap<>();
p.add("uName", userEntity.getuName());
p.add("password", userEntity.getPassword());
p.add("money", userEntity.getMoney());
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(p, headers);
return restTemplate.postForEntity(JDBC_DEMO_URL + "/user", httpEntity, String.class).getBody();
}
restTemplate FileUpload
使用spring 框架的时候 通常我们上传文件 都是使用的 multipartFile 进行接收。
- restTemplate 上传文件,使用MultipartFile 接收
例如对方代码: 上传文件 使用multipartFile 接收
@PostMapping("file")
public void upload(@RequestPart(name = "file") MultipartFile file) throws Exception
使用restTemplate 调用该接口
- 使用FileSystemResource 上传; 需要借助临时文件, FileSystemResource 需要传入一个File,
所以我们需要创建一个临时的file
// 通过fileSystemResource 进行文件上传, 使用multipartFile 接收;缺点需要通过创建临时文件上传,以及使用完后不能再次使用
private void uploadByFileSystemResource(MultipartFile file) throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
File tempFile = new File(Files.createTempDir().getPath() + "/" + file.getOriginalFilename());
file.transferTo(tempFile);
FileSystemResource fileSystemResource = new FileSystemResource(tempFile);
MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
param.add("file", fileSystemResource);
HttpEntity httpEntity = new HttpEntity<>(param, headers);
restTemplate.postForEntity(JDBC_DEMO_URL + "/user/file", httpEntity, Void.class);
}
- 使用 inputStreamResource 或者byteArrayResource
这里我们需要使用 两个 httpEntity;
private void uploadBy(MultipartFile file) throws Exception {
// ByteArrayResource 和InputStreamResource 都可以
ByteArrayResource byteArrayResource = new ByteArrayResource(file.getBytes());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.parseMediaType(Objects.requireNonNull(file.getContentType())));
httpHeaders.setContentDispositionFormData("file", file.getOriginalFilename());
HttpEntity<ByteArrayResource> bodyHttpEntity = new HttpEntity<>(byteArrayResource, httpHeaders);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", bodyHttpEntity);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(body, headers);
restTemplate.postForEntity(JDBC_DEMO_URL + "/user/file", httpEntity, Void.class);
}
- 上传文件到body中,通过request.getInputStream 接收文件
例如 :
接收上传文件的代码示例:
// 上传文件,配合restTemplate 使用
@PostMapping("file2")
public void upload(HttpServletRequest request) throws Exception {
FileOutputStream fileOutputStream = new FileOutputStream("F:\\\\app\\\\aaa.jpg");
IOUtils.copy(request.getInputStream(), fileOutputStream);
IOUtils.closeQuietly(fileOutputStream);
}
- 上传文件,使用InputStreamResource 上传
// 通过InputStreamResource 上传, 通过request.getInputStream 接收
private void uploadByInputStreamResource(MultipartFile file) throws Exception {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.parseMediaType(file.getContentType()));
InputStreamResource inputStreamResource = new InputStreamResource(file.getInputStream());
HttpEntity httpEntity = new HttpEntity<>(inputStreamResource, httpHeaders);
restTemplate.postForEntity(JDBC_DEMO_URL + "/user/file2", httpEntity, Void.class);
}
- 使用byteArrayResource 上传
private void uploadByByteArrayResource(MultipartFile file) throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(file.getContentType()));
ByteArrayResource byteArrayResource = new ByteArrayResource(file.getBytes());
HttpEntity httpEntity = new HttpEntity<>(byteArrayResource, headers);
restTemplate.postForEntity(JDBC_DEMO_URL + "/user/file2", httpEntity, Void.class);
}
restTemplate 配置使用HttpClient
restTemplate 默认使用的是 SimpleClientHttpRequestFactory 进行获取http连接的;SimpleClientHttpRequestFactory 是使用的使用JDK 提供的java.net.HttpURLConnection 获取连接;
也可以配置restTemplate 使用 apache 的 httpClient
1.加入httpClient 依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.9</version>
</dependency>
加入 httpClient 依赖后; restTemplate 就自动选择httpClient 进行http请求; 如何自动选择的可以参考ClientHttpRequestFactorySupplier ;
但是一般我们不想使用默认的配置; 可以配置相关的http连接信息
2. 配置自定义HttpClient连接信息
@Configuration
@ConditionalOnClass(RestTemplate.class)
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder/*.setReadTimeout(Duration.ofSeconds(5)).setConnectTimeout(Duration.ofSeconds(2))*/.build();
}
@Configuration
@ConditionalOnClass(HttpClient.class)
@EnableConfigurationProperties(HttpClientProperties.class)
public static class HttpClientConfig {
@Autowired
private HttpClientProperties httpClientProperties;
// 配置 httpClient
@Bean
public HttpClient httpClient() {
HttpClientBuilder customHttpClient = HttpClients.custom();
RequestConfig.Builder customRequestConfig = RequestConfig.custom();
customRequestConfig.setConnectionRequestTimeout(httpClientProperties.getConnectionTimeout()) // 获取连接超时时间
.setSocketTimeout(httpClientProperties.getReadTimeout()) //
.setConnectTimeout(2000); // 建立连接超时时间
customHttpClient.setConnectionManager(httpClientConnectionManager())
.setDefaultRequestConfig(customRequestConfig.build())
.evictIdleConnections(httpClientProperties.getMaxIdleTime(), TimeUnit.SECONDS) // 连接空闲时间,超时驱逐
.disableAutomaticRetries(); // 禁止自动重试
return customHttpClient.build();
}
// 配置 httpClientConnectionManager 连接请求管理者
@Bean
public HttpClientConnectionManager httpClientConnectionManager() {
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
poolingHttpClientConnectionManager.setMaxTotal(httpClientProperties.getMaxConnectionTotal()); // 池中最大连接总数
int defMaxPerRoute = httpClientProperties.getMaxConnectionTotal() / 10;
if (defMaxPerRoute <= 0) defMaxPerRoute = 1;
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(defMaxPerRoute); // 最大路由
return poolingHttpClientConnectionManager;
}
// 配置 httpRequestFactory 这个是spring提供的
@Bean
public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory(HttpClient httpClient, RestTemplate restTemplate) {
HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
restTemplate.setRequestFactory(httpComponentsClientHttpRequestFactory);
return httpComponentsClientHttpRequestFactory;
}
}
连接配置类
@ConfigurationProperties(prefix = "httpclient.config")
@Data
public class HttpClientProperties {
private Long connectionTimeout = 2000L; // 2s
private Long readTimeout = 10000L; // 10s
private Integer maxConnectionTotal = 30; // 30
private Integer maxIdleTime = 180; // 180s
}
配置文件中配置相关属性
httpclient:
config:
connection-timeout: 3000
read-timeout: 10000
max-connection-total: 1
max-idle-time: 30
restTemplate 配置使用OkHttp
OkHttp 优缺点就不说了; 网上一大批;我们就使用restTemplate 整合下Okhttp。
创建使用 Okhttp的restTemplate
@Bean
public RestTemplate restTemplateOkHttpClient(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}
配置Okhttp
@Configuration
@ConditionalOnClass(OkHttpClient.class)
public static class OkHttpClientConfig {
@Bean
public OkHttpClient okHttpClient() {
OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();
return okHttpClient.build();
}
@Bean
public OkHttp3ClientHttpRequestFactory okHttp3ClientHttpRequestFactory(@Qualifier("restTemplateOkHttpClient") RestTemplate restTemplateOkHttpClient) {
OkHttp3ClientHttpRequestFactory okHttp3ClientHttpRequestFactory = new OkHttp3ClientHttpRequestFactory();
// 设置restTemplate 使用 okHttpRequestFactory
restTemplateOkHttpClient.setRequestFactory(okHttp3ClientHttpRequestFactory);
okHttp3ClientHttpRequestFactory.setWriteTimeout(15000);
okHttp3ClientHttpRequestFactory.setConnectTimeout(3000);
okHttp3ClientHttpRequestFactory.setReadTimeout(15000);
return okHttp3ClientHttpRequestFactory;
}
}
项目中引用并使用
@Autowired
@Qualifier("restTemplateOkHttpClient")
RestTemplate restTemplateOkHttpClient;
...
// 正常使用restTemplate 请求方法即可
restTemplateOkHttpClient.postForObject(JDBC_DEMO_URL + "/user/batch", userEntities, List.class);
restTemplate 配置请求拦截器
例如记录请求详情和时间
创建 LogClientHttpRequestInterceptor 继承 ClientHttpRequestInterceptor
@Slf4j
public class LogClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
Stopwatch stopwatch = Stopwatch.createStarted();
String method = request.getMethodValue();
String uri = request.getURI().toString();
ClientHttpResponse response = execution.execute(request, body);
log.warn("\\n【\\t{} [{}] \\n body: {} \\n 请求耗时:{}ms】", uri, method, new String(body, StandardCharsets.UTF_8), stopwatch.elapse以上是关于Spring boot restTemplate的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Java Spring boot 中模拟 RestTemplate?
使用注入 java 和 spring boot 的 RestTemplate 类进行单元测试
spring-boot Autowired DiscoveryClient RestTemplate UnknownHostException