RestTemplate学习

RestTemplate

简介

  • RestTemplate是由Spring框架提供的一个可用于应用中调用rest服务的类它简化了与http服务的通信方式,统一了RESTFul的标准,封装了http连接,我们只需要传入url及其返回值类型即可。相较于之前常用的HttpClientRestTemplate是一种更为优雅的调用RESTFul服务的方式。
  • 在Spring应用程序中访问第三方REST服务与使用~类有关。RestTemplate类的设计原则与许多其他Spring的模板类(例如JdbcTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法。
  • RestTemplate默认依赖JDK提供了http连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为例如Apache HttpCompoent、Netty或OKHttp等其他Http libaray。
  • 考虑到了RestTemplate类是为了调用REST服务而设计的,因此它的主要方法与REST的基础紧密相连就不足为奇了,后者时HTTP协议的方法:HEAD、GET、POST、PUT、DELETE、OPTIONS例如,RestTemplate类具有headForHeaders()、getForObject()、putForObject(),put()和delete()等方法。
  • 解决微服务中服务之间相互独立无法访问的问题,使得服务之间可以远程调用

创建RestTemplate

  • 因为RestTemplate是Spirng框架提供的所以只要是一个Springboot项目就不用考虑导包的问题,这些都是提供好的。

  • 但是Spring并没有将其加入SpringBean容器中,需要我们手动加入,因为我们首先创建一个Springboot配置类,再在配置类中将我们的RestTemlate注册到Bean容器中

第一种方式

使用Springboot提供的RestTemplateBuilder构造类来构造一个RestTemplate,可以自定义一些连接参数,如:连接超时时间,读取超时时间,还有认证信息等

java 复制代码
@Configuration
public class WebConfiguration {
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder){
        return builder
                //设置连接超时时间
                .setConnectTimeout(Duration.ofSeconds(5000))
                //设置读取超时时间
                .setReadTimeout(Duration.ofSeconds(5000))
                //设置认证信息
                .basicAuthentication("username","password")
                //设置根路径
                .rootUri("https://api.test.com/")
                //构建
                .build();
    }
}

可选参数如下

添加自定义的拦截器

​ 自定义拦截器示例

java 复制代码
@Slf4j
public class CustomClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {

        //打印请求明细
        logRequestDetails(request,body);
        ClientHttpResponse response = execution.execute(request, body);
        //打印响应明细
        logResponseDetails(response);
        
        return response;
    }

    private void logRequestDetails(HttpRequest request, byte[] body){
        log.debug("Headers:{}",request.getHeaders());
        log.debug("body:{}",new String(body, StandardCharsets.UTF_8));
        log.debug("{}:{}",request.getMethod(),request.getMethodValue());
    }

    private void logResponseDetails(ClientHttpResponse response) throws IOException {
        log.debug("Status code : {}",response.getStatusCode());
        log.debug("Status text : {}",response.getStatusText());
        log.debug("Headers : {}",response.getHeaders());
        log.debug("Response body: {}", StreamUtils.copyToString(response.getBody(),StandardCharsets.UTF_8));
    }
}

使用RestTemplateBuilder构造类,添加自定义拦截器,构造带有自定义拦截器的RestTemplate实例

java 复制代码
@Configuration
public class WebConfiguration {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder){
        return builder
                .additionalInterceptors(new CustomClientHttpRequestInterceptor())
                //构建
                .build();
    }    
}
第二种方法

使用RestTemplate构造方法构造一个RestTemlate,虽然不能像RestTemplate构造类那样更详细、更多样的配置参数,但是RestTemplate构造方法在一般情况是够用的。

  • 无参构造 全部参数默认
  • 指定ClientHttpRequestFactory 的构造方法可以指定自己实现的ClientHttpRequestFactory(客户端http请求工厂)其他的与无参构造相同。
    • ClientHttpRequestFactory
  • 指定List<HttpMessageConverter<?>>的构造方法可以指定自己是实现的HttpMessageConverter(Http消息转换器)传入其他与无参构造相同。
java 复制代码
@Configuration
public class WebConfiguration {

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    
}

两者方法都可使用,前者提供了多样的自定义参数的选择,可以将RestTemplate配置的更为完善,后者则简化了配置虽然配置多样性不如前者,但是日常使用调用些API还是足以使用

RestTemplate API使用

在使用RestTemplate前先让我们看看RestTemplate有哪些API

相信大家看到这么多方法,一定很头大,但是我们仔细看上述的方法,我们可以提取出主要的几种方法是(这里只讨论Http请求的):

  • GET
  • POST
  • PUT
  • DELETE
  • HEAD
  • OPTIONS
  • EXCHANGE
  • EXECUTE

这里我给大家安利一个网站,它提供免费的RESTFul api的样例测试。 httpbin A simple HTTP Request & Response Service.

GET

通过上图我们可以发现RestTemlate发送GET请求的方法有两种

java 复制代码
public <T> T getForObject(...)
public <T> ResponseEntity<T> getForEntity(...)
getForEntity()

后缀带有Entity的方法都代表返回一个ResponseEntity,ResponseEntity是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码,contentType、contentLength、响应消息体等

通过它继承父类(HttpEntity<T>)的getHeader()方法我们可以获取contentType、contentLength、响应消息体等。比如下面这个例子。

java 复制代码
public void queryWeather() {

        ResponseEntity<Object> forEntity = restTemplate.getForEntity("https://restapi.amap.com/v3/weather/weatherInfo?city=510100&key=e7a5fa943f706602033b6b329c49fbc6", Object.class);
        System.out.println("状态码:"+forEntity.getStatusCode());
        System.out.println("状态码内容:"+forEntity.getStatusCodeValue());
        HttpHeaders headers = forEntity.getHeaders();
        System.out.println("响应头:"+headers);
        Object body = forEntity.getBody();
        System.out.println("响应内容:"+body);
    }

该例子中getForEntity()方法的第一个参数为我要调用服务的URL,第二个参数则为响应内容的类的类型(Java嘛 万物皆对象)还可以添加第三个参数,第三个参数为一个可变参数 代表着调用服务时的传参。

第三个参数可以使用key-value的map来传入参数

get请求也可通过向在url上添加查询参数来发送带有请求的参数

getForObject()

相比于前者getForEntity()该方法则是,更偏向于直接获取响应内容的,因为他直接返回响应实体的body(响应内容),。比如下面这个例子

java 复制代码
public void queryWeather() {
        
        Object body = restTemplate.getForObject("https://restapi.amap.com/v3/weather/weatherInfo?city=510100&key=e7a5fa943f706602033b6b329c49fbc6", Object.class);
        System.out.println(body);
    }

方法参数签名与getForEntity()基本一致。

POST

POST请求有如下三种方法

  • public URI postForLocation(...)
  • public <T> T postForObject(...)
  • public <T> ResponseEntity<T> postForEntity(...)

前两种用法与GET基本一致不做详细介绍,这里着重介绍postForLocation()

postForEntity()

该方法有三个参数,第一个为调用服务的地址(URL)

第二个参数表示上传的参数(json格式提交)

第三个表示返回响应内容的具体类型

第四个参数也用于指定参数(在URL中添加)

java 复制代码
@Override
    public void queryWeather() {
        User user = new User();
        user.setName("鲁大师");
        ResponseEntity<Object> objectResponseEntity = restTemplate.postForEntity("https://restapi.amap.com/v3/weather/weatherInfo?city=510100&key=e7a5fa943f706602033b6b329c49fbc6", user, Object.class);
        System.out.println("消息响应内容:"+objectResponseEntity.getBody());
    }
postForObject()

使用方法与getForObject类似只是多了一个传入对象参数(传入方式与postForEntity()相同)

java 复制代码
public void queryWeather() {
        User user = new User();
        user.setName("鲁大师");
        ResponseEntity<Object> objectResponseEntity = restTemplate.postForEntity("https://httpbin.org/post", user, Object.class);
        MediaType contentType = objectResponseEntity.getHeaders().getContentType();
        System.out.println(contentType);
        System.out.println("消息响应内容:"+objectResponseEntity.getBody());
    }
postForLocation()

postForLocation传参用法与前两者一致,只不过返回从实体变成了一个URL,因此它不需要指定返回响应内容的类型。

ini 复制代码
public void queryWeather() {
        User user = new User();
        user.setName("鲁大师");
        URI uri = restTemplate.postForLocation("https://httpbin.org/post", user);
        System.out.println(uri);
    }

这个只需要服务提供者返回一个 URI 即可,该URI返回值体现的是:用于提交完成数据之后的页面跳转,或数据提交完成之后的下一步数据操作URI

使用POST以表单方式提交

这里我们着重说一下,如何自己封装一个请求体。

我们需要用到如下几个类

  • HttpHeaders
  • MultiValueMap<K,V>
  • HttpEntity<T>
HttpHeaders

故名思意,就是用来封装Http请求的请求头的,这里我们要设置他的ContentType为**MediaType.APPLICATION_FORM_URLENCODED**以使得我们提交的参数是以Form(表单)的形式提交。

java 复制代码
//设置请求头, x-www-form-urlencoded格式的数据
        HttpHeaders httpHeaders = new HttpHeaders();
        //这里指定参数以UTF-8编码格式传输
        MediaType mediaType = new MediaType(MediaType.APPLICATION_FORM_URLENCODED, UTF_8);
        httpHeaders.setContentType(mediaType);
        //提交参数设置
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("name","鲁大师");
MultiValueMap<K,V>

该类是用来封装请求参数的,是以key-value的形式封装但是以单个key对应多个value的格式传输(也就是是以单个key:[value...]的格式传输的)。

arduino 复制代码
//提交参数设置
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("name","鲁大师");

如果像传输单个key对应单个value使用普通的Map传参即可

HttpEntity<T>

该类是用来封装请求的,主要作用就是将请求头和请求体封装在一起成为一个请求实体 T用来指定用来封装参数的容器的类型。

java 复制代码
//组装请求体
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, httpHeaders);
测试

通过上述介绍后,我们就可以自己封装一个以form形式提交参数的POST请求了。

@Test void contextLoads() { //请求地址 String url = "httpbin.org/post";

java 复制代码
    //设置请求头, x-www-form-urlencoded格式的数据
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

    //提交参数设置
    MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
    map.add("name","鲁大师");

    //组装请求体
    HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, httpHeaders);
java 复制代码
    //发送post请求并打印结果 以String类型接收响应结果JSON字符串
    String s = restTemplate.postForObject(url, request, String.class);
    System.out.println(s);
}
PUT

PUT请求的方法只有一类

  • void put()
PUT()

使用方法与postForEntity()参数基本一致,只是put方法没有返回值(也就不必去设置响应内容的类型了)。

java 复制代码
@Test
    void contextLoads() {
        //请求地址
        String url = "http://httpbin.org/put";
        User user = new User();
        user.setName("鲁大师");
        restTemplate.put(url,user);
    }
DELETE

PUT一样,DELETE方法只有一类

  • void delete()
delete()

delete()可以指定url中的中的参数,但是RestTemplatedelete()方法是不支持上传requestBody的。

java 复制代码
void contextLoads() {
    //请求地址
    String url = "http://httpbin.org/delete";
    restTemplate.delete(url);
}

HEADER也只有一类方法

  • public HttpHeaders headForHeaders()

主要用来发送请求获取响应头部信息,但是像DELETEPUT这类没有响应的方法,是不能使用该方法的(因为没有响应也就没有响应头了)。

java 复制代码
@Test
    void contextLoads() {
        //请求地址
        String url = "http://httpbin.org/get";
        HttpHeaders httpHeaders = restTemplate.headForHeaders(url);
        System.out.println(httpHeaders);
    }
OPTIONS
  • public Set<HttpMethod> optionsForAllow()

该方法的主要用来判断该服务地址,能够使用那种方法去执行

java 复制代码
	@Test
    void contextLoads() {
        //请求地址
        String url = "http://httpbin.org/get";
        Set<HttpMethod> httpMethods = restTemplate.optionsForAllow(url);
        System.out.println(httpMethods);
    }
EXCHANGE
  • ResponseEntity exchange() 该接口与其他接口不同
  • 该方法允许用户指定请求的方法(get,post,put等)

  • 可以在请求中增加body以及头信息,其内容通过参数HttpEntity<?> requestEntity描述

  • exchange支持'含参数的类型(即泛型)'作为返回类型,该特性通过ParameterizedTypeReferenceresponseType 描述

该方法支持五个参数

  • 第一个是服务地址
  • 第二个是请求方法
  • 第三个是写入的请求实体
  • 第四个是响应内容的类型
  • 第五个是扩展模板的变量或包含URI模板变量的映射
java 复制代码
@Test
void contextLoads() {
    //请求地址
    String url = "http://httpbin.org/post";
    User user = new User();
    user.setName("彭于晏");
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity<User> userHttpEntity = new HttpEntity<>(user, httpHeaders);
    ResponseEntity<Object> exchange = restTemplate.exchange(url, HttpMethod.POST, userHttpEntity, Object.class);
    System.out.println(exchange);
}

上述代码模拟了一个简单的POST请求 可以理解为可以动态的指定请求方法和请求实体的一个方法。

EXECUTE

T execute() 该方法就是执行请求的方法,我们可以发现上述的所有方法的最后执行都是调用的该方法执行,所以他在RestTemplate中十分重要

该方法有五个参数

  • 服务地址
  • 请求的方法
  • 准备请求的对象(requestCallback)
  • 从响应中提取返回值的对象
  • 扩展模板的变量或包含URI模板变量的映射
execute()
java 复制代码
@Override
	@Nullable
	public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
			@Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {

		URI expanded = getUriTemplateHandler().expand(url, uriVariables);
		return doExecute(expanded, method, requestCallback, responseExtractor);
	}

通过上述源码我们可以发现execute()方法只是将我们传入的String类型的URL转换为了URL类型,最后执行请求是由doExecute()方法

doExecute()

这里需要了解两个类:RequestCallbackResPonseExtractor

RequestCallback: 用于操作请求头和body,在请求发出前执行。不需要关心关闭请求或处理错误:这都将由RestTemplate处理。

该接口有两个实现类:

ResPonseExtractor: 解析HTTP响应的数据,而且不需要担心异常和资源的关闭。

该接口在RestTemplate中同样有两个实现类:

HeadersExtractor 提取响应HttpHeaders的响应提取器。直接提取响应体中的响应头
ResponseEntityResponseExtractor<T> HttpEntity的响应提取器。可以获取响应实体里面包括响应头,响应体等。具体请查看 HttpEntity
java 复制代码
@Test
void contextLoads() {
    //请求地址
    String url = "http://httpbin.org/post";
    User user = new User();
    user.setName("彭于晏");
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity<User> userHttpEntity = new HttpEntity<>(user, httpHeaders);
    ResponseEntity<Object> execute = restTemplate.execute(url, HttpMethod.POST, restTemplate.httpEntityCallback(userHttpEntity), restTemplate.responseEntityExtractor(Object.class));
    System.out.println(execute);
}
解惑
  • 前面我们介绍方法的时候发现有个一个可变参数,那个参数被描述成了扩展模板的变量或是包含URI模板变量的映射

我们来简单看一下这个参数,我们知道请求传参可以通过url拼接参数的方式传参,拼接参数也分为两种:

  • 路径中嵌入占位的格式(http://httpbin.org/{1}/post)也叫模板映射
  • 末尾添加Key-value格式(http://httpbin.org/post?name="彭于晏")即扩展模板的变量
  • 当我们最后一参数传入map时会以key-value的格式拼接在URL后(通俗的说就是这样设置的变量会跟着URL路径后面)

http://httpbin.org/post?name="彭于晏"

java 复制代码
@Test
void contextLoads() {
    //请求地址
    String url = "http://httpbin.org/get";
    HashMap<String, String> map = new HashMap<>();
    map.put("name","彭于晏");
    Object forObject = restTemplate.getForObject(url, Object.class, map);
    System.out.println(forObject);
}
  • 当我们传入简单的对象如String,Integer时且路径中有嵌入的占位符时就会代替调用URL中占位符
typescript 复制代码
@Test
void contextLoads() {
    //请求地址
    String url = "http://httpbin.org/{2}/get";
    HashMap<String, String> map = new HashMap<>();
    Object forObject = restTemplate.getForObject(url, Object.class, 99);
    System.out.println(forObject);
}
相关推荐
Libby博仙31 分钟前
Spring Boot 条件化注解深度解析
java·spring boot·后端
源代码•宸1 小时前
Golang原理剖析(Map 源码梳理)
经验分享·后端·算法·leetcode·golang·map
小周在成长1 小时前
动态SQL与MyBatis动态SQL最佳实践
后端
瓦尔登湖懒羊羊1 小时前
TCP的自我介绍
后端
小周在成长1 小时前
MyBatis 动态SQL学习
后端
子非鱼9211 小时前
SpringBoot快速上手
java·spring boot·后端
我爱娃哈哈1 小时前
SpringBoot + XXL-JOB + Quartz:任务调度双引擎选型与高可用调度平台搭建
java·spring boot·后端
JavaGuide1 小时前
Maven 4 终于快来了,新特性很香!
后端·maven
开心就好20252 小时前
全面解析iOS应用代码混淆和加密加固方法与实践注意事项
后端
Thomas游戏开发2 小时前
分享一个好玩的:一次提示词让AI同时开发双引擎框架
前端·javascript·后端