Spring boot restTemplate

Posted xiaodujava

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring boot restTemplate相关的知识,希望对你有一定的参考价值。

Spring boot restTemplate

上一节 spring boot 数据库连接池

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 进行接收。

  1. 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);
    }
  1. 上传文件到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的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot restTemplate

spring boot 注入 restTemplate

如何在 Java Spring boot 中模拟 RestTemplate?

使用注入 java 和 spring boot 的 RestTemplate 类进行单元测试

spring-boot Autowired DiscoveryClient RestTemplate UnknownHostException

(030)Spring Boot之RestTemplate访问web服务案例