【Spring Cloud系列】- RestTemplate使用详解

这篇具有很好参考价值的文章主要介绍了【Spring Cloud系列】- RestTemplate使用详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【Spring Cloud系列】- RestTemplate使用详解

一、初识RestTemplate

RestTemplate是Spring框架提供用于调用Rest接口的一个应用,它简化了与http服务通信方式。RestTemplate统一Restfull调用的标准,封装HTTP链接,只要需提供URL及返回值类型即可完成调用。相比传统的HttpClient与Okhttp,RestTemplate是一种优雅,简洁调用RESTfull服务的方式。

RestTemplate默认依赖JDK提供Http连接的能力(HttpURLConnection),如果有需要的话也可以通过SetRequestFactory方法替换为如:Apache HttpComponents、Netty或OKHttp等其他HTTP库。

二、RestTemplate调用流程详解:

2.1. 实例化RestTemplate

RestTemplate template=new RestTemplate();

2.2. 通过RestTemplate内部通过调用 doExecute 方法,首先就是获取 ClientHttpRequest

RestTemplate中doExecute核心代码
  • 通过ClientHttpRequestFactory工厂生产一个ClientHttpRequest
    ClientHttpRequest request;
    try {
    	request = createRequest(url, method);
     }catch (IOException ex) {
        ResourceAccessException exception = createResourceAccessException(url, method, ex);
    	throw exception;
    }
    
  • 封装了请求头和请求体,使用了HttpMessageConverter
    if (requestCallback != null) {
        requestCallback.doWithRequest(request);
    }
    
  • 执行ClientHttpRequest中execute 方法请求
    response = request.execute();
    
  • ClientHttpRequest中handleResponse方法处理Http响应结果,执行失败并抛出ResponseErrorHandler异常
    handleResponse(url, method, response);
    

    handleResponse方法代码

    ResponseErrorHandler errorHandler = getErrorHandler();
    boolean hasError = errorHandler.hasError(response);
    if (logger.isDebugEnabled()) {
        try {
            HttpStatusCode statusCode = response.getStatusCode();
            logger.debug("Response " + statusCode);
        }catch (IOException ex) {
            logger.debug("Failed to obtain response status code", ex);
        }
    }
    if (hasError) {
    	errorHandler.handleError(url, method, response);
    }
    

2.3. RestTemplate 实现了抽象类 HttpAccessor ,可以调用父类(HttpAccessor)的 createRequest方法

在HttpAccessor中

private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
public ClientHttpRequestFactory getRequestFactory() {
   return this.requestFactory;
}
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
  ClientHttpRequest request = getRequestFactory().createRequest(url, method);
  initialize(request);
  if (logger.isDebugEnabled()) {
	 logger.debug("HTTP " + method.name() + " " + url);
  }
  return request;
}

2.4. SimpleClientHttpRequestFactory 实现了接口ClientHttpRequestFactory,同时实现createRequest方法

在RestTemplate可以设置是否使用缓存流,默认设置:bufferRequestBody =true,缺点是当发送大量数据时,比如put/post的保存和修改,那么可能内存消耗严重。所以这时候可以设置 RestTemplate.setBufferRequestBody(false);

即使用 SimpleStreamingClientHttpRequest 来实现

@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
    //创建java.net.HttpURLConnection 
	HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
    //创建connection属性,同时设置了setDoInput
	prepareConnection(connection, httpMethod.name());
	if (this.bufferRequestBody) {
		return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
	}
	else {
		return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
	}
}

2.5. openConnection 即打开连接,而是 prepareConnection 各种连接准备,针对请求者

openConnection源码:

protected HttpURLConnection openConnection(URL url, @Nullable Proxy proxy) throws IOException {
	URLConnection urlConnection = (proxy != null ? url.openConnection(proxy) : url.openConnection());
	if (!(urlConnection instanceof HttpURLConnection)) {
		throw new IllegalStateException(
					"HttpURLConnection required for [" + url + "] but got: " + urlConnection);
	}
	return (HttpURLConnection) urlConnection;
}

prepareConnection方法:

protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
  //设置连接超时时间:connectTimeout
  if (this.connectTimeout >= 0) {
	  connection.setConnectTimeout(this.connectTimeout);
  }
  //设置读超时时间:readTimeout
  if (this.readTimeout >= 0) {
	  connection.setReadTimeout(this.readTimeout);
  }
  Boolean mayWrite =("POST".equals(httpMethod) || 
                     "PUT".equals(httpMethod) ||
					 "PATCH".equals(httpMethod) || 
                     "DELETE".equals(httpMethod));
  //设置URL允许输入:connection.setDoInput(true);//默认true
  //URL连接可用于输入和/或输出。 如果您打算使用URL连接进行输入,请将DoInput标志设置为true;
  //否则,设置为false。 默认值是true。
  //HttpUrlConnection中方法setDoInput(true);以后就可以使用conn.getInputStream().read();
  connection.setDoInput(true);
  connection.setInstanceFollowRedirects("GET".equals(httpMethod));
  //URL连接可用于输入和/或输出。 如果您打算将URL连接用于输出,请将DoOutput标志设置为true,
  //如果不是,则为false 默认值是false。  
  //如get请求,用不到conn.getOutputStream(),因为参数直接追加在地址后面,因此默认是false
  //post、put、patch、delete请求会将setDoOutput设置为true
  //设置setDoOutput(true);以后就可以使用conn.getOutputStream().write()
  connection.setDoOutput(mayWrite); 
  connection.setRequestMethod(httpMethod);
}

2.6. 执行requestCallback.doWithRequest(request);

RequestCallback 封装了请求体和请求头对象,既在RequstCallback可以输入需要传输的head数据

在执行 doWithRequest 时,与Connection发送请求体有着密切关系,请求头就是 SimpleBufferingClientHttpRequest.addHeaders 方法,那么请求体 bufferedOutput 是如何赋值的呢?就是在 doWithRequest 里面,如下 StringHttpMessageConverter (其他 MessageConvert 也一样,这里也是经常乱码的原因;

RequestCallback 用于操作请求头和body,在请求发出前执行。以下是RequestCallback的 两个实现类

  1. AcceptHeaderRequestCallback:只处理请求头,用于getXXX()方法
  2. HttpEntityRequestCallback: 继承于AcceptHeaderRequestCallback可以处理请求头和body,用于putXXX()、postXXX()和exchange()方法。

2.7. 接着执行 response = request.execute();

调用接口ClientHttpRequest

public interface ClientHttpRequest extends HttpRequest, HttpOutputMessage {
    ClientHttpResponse execute() throws IOException;
}

然后使用实例 SimpleBufferingClientHttpRequest 封装请求体和请求头

@Override
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
	addHeaders(this.connection, headers);
	// JDK <1.8 doesn't support getOutputStream with HTTP DELETE
	if (getMethod() == HttpMethod.DELETE && bufferedOutput.length == 0) {
	    this.connection.setDoOutput(false);
	}
	if (this.connection.getDoOutput() && this.outputStreaming) {
		this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
	}
	this.connection.connect();
	if (this.connection.getDoOutput()) {
		FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
	}
	else {
		// Immediately trigger the request in a no-output scenario as well
		this.connection.getResponseCode();
	}
	return new SimpleClientHttpResponse(this.connection);
}

Delete通过请求方式和是否有请求体对象来判断是否需要发送请求体如果是delete请求,首先设置 DoOutput = true,然后根据是否有请求体数据,然后封装请求体FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());

2.8. 解析response

接着就是 response 的解析了,主要还是 Error 的解析。this.handleResponse(url, method, response);

内部处理多是解析error处理

protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
	ResponseErrorHandler errorHandler = getErrorHandler();
	boolean hasError = errorHandler.hasError(response);
	if (logger.isDebugEnabled()) {
	   try {
				HttpStatusCode statusCode = response.getStatusCode();
				logger.debug("Response " + statusCode);
			}
			catch (IOException ex) {
				logger.debug("Failed to obtain response status code", ex);
			}
	 }
	 if (hasError) {
			errorHandler.handleError(url, method, response);
	 }
}

三、RestTemplate常用方法及示例

3.1. RestTemplate中Get方法使用

3.1.1 getForObject(URI url, Class responseType)
@Test
public void TestRestGetMethod() throws Exception {
    RestTemplate restTemplate = new RestTemplate();
    URI url = URI.create("http://api.goyeer.com:8888/user");
    String response = restTemplate.getForObject(url, String.class);
}
3.1.2 getForObject(String url, Class responseType, Object… uriVariables)
@Test
public void TestGetForObjcetByArgument() throws Exception {
    RestTemplate restTemplate = new RestTemplate();
    String appkey="Goy_20200034_MES";
    String appSecret="GY202305-0801-41d4-a716-9998880";
    String url = "http://api.goyeer.com:8888/getToken?appkey={1}&appsecret={2}";
    String responseToken = restTemplate.getForObject(url,String.class, appkey,appSecret);
}
3.1.3 getForObject(String url, Class responseType, Map<String, ?> uriVariables)
@Test
public void TestGetForObjectByMap() throws Exception{
    Map<String,Object> map=new HashMap();
    map.put("appkey","Goy_20200034_MES");
    map.put("appSecret","GY202305-0801-41d4-a716-9998880");
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://api.goyeer.com:8888/user?appkey={appkey}&appsecret={appSecret}";
    String response = restTemplate.getForObject(url,String.class, map);
}
3.1.4 ResponseEntity getForEntity(URI url, Class responseType)
@Test
public void TestGetForEntity() throws Exception {
    RestTemplate restTemplate = new RestTemplate();
    URI url = URI.create("http://api.goyeer.com:8888/user");
    ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
    int httpCode =response.getStatusCode().value();
    String resp= response.getBody();
    System.out.println(httpCode);
    System.out.println(resp);
}
3.1.5 ResponseEntity getForEntity(String url, Class responseType, Object… uriVariables)
@Test
public void TestGetForEntityByArgument() throws Exception {
    RestTemplate restTemplate = new RestTemplate();
    String appkey="Goy_20200034_MES";
    String appSecret="GY202305-0801-41d4-a716-9998880";
    String url = "http://api.goyeer.com:8888/getToken?appkey={1}&appsecret={2}";
    ResponseEntity<String> response  = restTemplate.getForEntity(url,String.class, appkey,appSecret);
    int httpCode =response.getStatusCode().value();
    String resp= response.getBody();
    System.out.println(httpCode);
    System.out.println(resp);
}
3.1.6 getForEntity(String url, Class responseType, Map<String, ?> uriVariables)
@Tes
public void TestGetForEntityByMap() throws Exception {
    RestTemplate restTemplate = new RestTemplate();
    Map<String,Object> map=new HashMap();
    map.put("appkey","Goy_20200034_MES");
    map.put("appSecret","GY202305-0801-41d4-a716-9998880");
    String url = "http://api.goyeer.com:8888/getToken?appkey={appkey}&appsecret={appsecret}";
    ResponseEntity<String> response  = restTemplate.getForEntity(url,String.class, map);
    int httpCode =response.getStatusCode().value();
    String resp= response.getBody();
    System.out.println(httpCode);
    System.out.println(resp);
}
3.1.7 getForEntity与getForObject区别

GetForEntity和GetForObject用法几乎完全一致,区别在于前者可以查看请求状态码,请求头信息。
getForEntity返回的是一个ResponseEntity,而getForObject返回的就只是返回内容。getForObject的返回相当于只返回http的body部份而getForEntity的返回是返回全部信息

3.2. RestTemplate中POST方法使用

3.2.1. postForObject(URI url, @Nullable Object request, Class responseType)
@Test
public void TestPostForObject() {
  try {
         RestTemplate restTemplate = new RestTemplate();
         URI url = URI.create("http://api.goyeer.com:7777/user/create");
         User user = new User();
         user.setId(1110L);
         user.setLoginName("Goy");
         user.setDepartment("Department");
         User respUser = restTemplate.postForObject(url, user, User.class);
         System.out.println(respUser.getLoginName());
   } catch (Exception exception) {
        throw exception;
   }
}
3.2.2. postForObject(String url, @Nullable Object request, Class responseType, Object… uriVariables)
@Test
public void TestPostForObejectByArgument()throws Exception{
    RestTemplate restTemplate = new RestTemplate();
    try {
        String uri="http://api.goyeer.com:7777/{1}/{2}";
        String controllerName="user";
        String operateName="create";
        User user = new User();
        user.setId(1110L);
        user.setLoginName("Goy");
        user.setDepartment("Department");
        User respUser = restTemplate.postForObject(uri, user, User.class,controllerName,operateName);
        System.out.println(respUser.getLoginName());
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
3.2.3. postForObject(String url, @Nullable Object request, Class responseType,Map<String, ?> uriVariables)
@Test
public void TestPostForObjectByMap() {
   RestTemplate restTemplate = new RestTemplate();
   try {
      String uri="http://localhost:7777/{controller}/{operate}";
      Map map=new HashMap();
      map.put("controller","user");
      map.put("operate","create");
      User user = new User();
      user.setId(1110L);
      user.setLoginName("Goy");
      user.setDepartment("Department");
      User respUser = restTemplate.postForObject(uri, user, User.class,map);
      System.out.println(respUser.getLoginName());
    } catch (Exception e) {
       throw new RuntimeException(e);
    }
}
3.2.4. 其他常用POST的方法

其它提供POST的方法有postForLocationpostForEntity用法如postForObject

相比于postForObject()方法, postForEntity() 返回响应体为 ResponseEntity 类型,其他两个方法功能一致。

与postForObject 和 postForEntity 方法类型, postForLocation 也发送post请求至特定uri并创建新的对象。唯一的差异是返回值为Location头信息。

四、常用RestTemplate方法

4.1. Head请求使用headForHeaders方法

headForHeaders方法 是HTTP中请求的一种。HEAD方法跟GET方法相同,只不过服务器响应时不会返回消息体。一个HEAD请求的响应中,HTTP头中包含的元信息应该和一个GET请求的响应消息相同。这种方法可以用来获取请求中隐含的元信息,而不用传输实体本身。也经常用来测试超链接的有效性、可用性和最近的修改。

一个HEAD请求的响应可被缓存,也就是说,响应中的信息可能用来更新之前缓存的实体。如果当前实体跟缓存实体的阈值不同(可通过Content-Length、Content-MD5、ETag或Last-Modified的变化来表明),那么这个缓存就被视为过期了。

  • headForHeaders(String url, Object… uriVariables)
  • headForHeaders(String url, Map<String, ?> uriVariables)
  • headForHeaders(URI url)

以上三个方法使用方法同getForObject类似

4.2. Put请求使用put方法

​ PUT请求是向服务器端发送数据的,从而改变信息,该请求就像数据库的update操作一样,用来修改数据的内容,但是不会增加数据的种类等,也就是说无论进行多少次PUT操作,其结果并没有不同。

  • put(String url, @Nullable Object request, Object… uriVariables)
  • put(String url, @Nullable Object request, Map<String, ?> uriVariables)
  • put(URI url, @Nullable Object request)

以上三个方法使用方法同getForObject类似

4.2. PATCH请求使用patchForObject方法

HTTP中为了提高交互操作性与防止错误,确实需要一种新的修改方法,而PUT方法已经被定义为用一个请求体去修改一个完整的资源。并且不能重复做部分更改,否则代理和缓存、甚至服务器或者客户端都会得到有问题的操作结果。
至此,PATCH方法有了被完全定义的必要。
PATCH在请求中定义了一个描述修改的实体集合,如果被请求修改的资源不存在,服务器可能会创建一个新的资源。

  • patchForObject(URI url, @Nullable Object request, Class responseType)
  • patchForObject(String url, @Nullable Object request, Class responseType,Map<String, ?> uriVariables)
  • patchForObject(String url, @Nullable Object request, Class responseType,Object… uriVariables)

以上三个方法使用方法同getForObject类似

4.2. Delete请求使用patchForObject方法

向服务器端提交数据,请求数据在报文body里;发送一个删除数据的请求。

  • delete(String url, Object… uriVariables)
  • delete(String url, Map<String, ?> uriVariables)
  • delete(URI url)

以上三个方法使用方法同getForObject类似

五、总结

RestTemplate是Spring自带的一个调用rest服务的客户端,它提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。在一般项目中完全可以替代HttpClient和OkHttp。

在后续Spring cloud系列文章中会多次使用到。文章来源地址https://www.toymoban.com/news/detail-479743.html

到了这里,关于【Spring Cloud系列】- RestTemplate使用详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Cloud 容错机试 Hystrix 服务降级 RestTemplate:

    雪崩效应:   如果短信服务炸了后面的所有服务就会起连锁反应造成全部服务挂掉 , 这就是雪崩效应 , 那么其实短信服务又不是我们主要业务 , 这个时候我们可以采用服务降级 , 服务降级就是暂时的把短信服务停掉能用就返回不能用就返回个错误 , 但是它也不会影响

    2024年02月07日
    浏览(32)
  • 【Spring Cloud系列】Hystrix应用详解

    在一个分布式系统中,每个服务都可能会调用其它的服务器,服务之间是相互调用相互依赖。假如微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务。这就是构成所谓“扇出”。 如果扇出的链路上某个微服务的调用响应的时间过长或者不可用,对微服A的调

    2024年02月16日
    浏览(42)
  • 【Spring Cloud系列】- Ribbon详解与实战

    在前面的文章 Eureka详解与实战、Eureka Client应用、RestTemplate详解及其负载均衡几篇文章中,已经介绍了Spring Cloud基本应用,本文将从讲解在进程层面的负载均衡,在Spring Cloud中如何使用Ribbon做系统应用层面的负载均衡使用。 Ribbon 是netflix 公司开源的基于客户端的负载均衡组件

    2024年02月15日
    浏览(34)
  • 【Spring Cloud系列】Feign详解与实战

    在前一章介绍了Ribbon的用法,在使用Ribbon是通过RestTemplate调用其他服务的API时,所有参数必须在请求的URL中进行拼接。如果参数过多,拼接请求字符串会导致效率下降。Spring Cloud提供另外一种调用API的解决方案,既使用 Spring Cloud Feign 。 Feign是一种负载均衡的HTTP客户端,它封

    2024年02月07日
    浏览(29)
  • 【Spring Cloud系列】-Eureka服务端高可用详解

    上一篇《Eureka使用详解》一文中我们详细介绍了什么是Spring Cloud微服务。Spring Cloud中Service注册中心及其Client如何实现并如何完成服务注册的。这一篇我们将介绍Eureka注册中心的高可用如何实现及其使用中的注意事项。 一. 序言 微服务就是开发一组小型服务的方式来完成对系

    2024年02月09日
    浏览(43)
  • 实战系列(一)| Dubbo和Spring Cloud的区别,包含代码详解

    Dubbo 和 Spring Cloud 都是微服务架构中的重要框架,但它们的定位和关注点不同。Dubbo 是阿里巴巴开源的一个高性能、轻量级的 RPC 框架,主要用于构建微服务之间的服务治理。而 Spring Cloud 是基于 Spring Boot 的一个微服务架构开发工具,它提供了一系列的开发工具和服务,帮助开

    2024年02月10日
    浏览(30)
  • 【springcloud 微服务】Spring Cloud Alibaba Sentinel使用详解

    目录 一、前言 二、分布式系统遇到的问题 2.1 服务可用性问题 2.1.1  单点故障

    2024年01月16日
    浏览(41)
  • 【springcloud 微服务】Spring Cloud Alibaba Nacos使用详解

    目录 一、前言 二、nacos介绍 2.1  什么是 Nacos 2.2 nacos 核心能力 2.2.1 服务发现和服务健康监测

    2024年01月22日
    浏览(42)
  • Spring 教程—REST 客户端详解(WebClient 、RestTemplate、HTTP 接口)

    Spring框架为调用REST端点提供了以下选择: WebClient - 非阻塞、响应式客户端和 fluent API。 RestTemplate - 带有模板方法API的同步客户端。 HTTP 接口 - 注解式接口,并生成动态代理实现。 WebClient  是一个非阻塞的、响应式的客户端,用于执行HTTP请求。它在5.0中引入,提供了  Re

    2024年02月07日
    浏览(33)
  • 【springcloud 微服务】Spring Cloud 微服务网关Gateway使用详解

    目录 一、微服务网关简介 1.1 网关的作用 1.2 常用网关 1.2.1 传统网关 1.2.2 云原生网关

    2023年04月16日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包