前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring3 RestTemplate

Spring3 RestTemplate

原创
作者头像
HLee
修改2021-08-20 18:06:12
2.8K0
修改2021-08-20 18:06:12
举报
文章被收录于专栏:房东的猫房东的猫

简介

RestTemplate是执行HTTP请求的同步阻塞式的客户端,它在HTTP客户端库(例如JDK HttpURLConnection,Apache HttpComponents,okHttp等)基础封装了更加简单易用的模板方法API。也就是说RestTemplate是一个封装,底层的实现还是java应用开发中常用的一些HTTP客户端。但是相对于直接使用底层的HTTP客户端库,它的操作更加方便、快捷,能很大程度上提升我们的开发效率。

RestTemplate作为spring-web项目的一部分,在Spring 3.0版本开始被引入。RestTemplate类通过为HTTP方法(例如GET,POST,PUT,DELETE等)提供重载的方法,提供了一种非常方便的方法访问基于HTTP的Web服务。如果你的Web服务API基于标准的RESTful风格设计,使用效果将更加的完美。

根据Spring官方文档及源码中的介绍,RestTemplate在将来的版本中它可能会被弃用,因为他们已在Spring 5中引入了WebClient作为非阻塞式Reactive HTTP客户端。但是RestTemplate目前在Spring 社区内还是很多项目的“重度依赖”,比如说Spring Cloud。另外,RestTemplate说白了是一个客户端API封装,和服务端相比,非阻塞Reactive 编程的需求并没有那么高。

RestTemplateSpring的一个rest客户端,在Spring-web这个包下。这个包虽然叫做Spring-web,但是它的RestTemplate可以脱离Spring 环境使用。

代码语言:javascript
复制
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.2.6.RELEASE</version>
</dependency>

如果是在Spring环境下使用RestTemplate,将maven坐标从spring-web换成spring-boot-starter-web

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

RestTemplate配置初始化为一个Bean。这种初始化方法,是使用了JDK 自带的HttpURLConnection作为底层HTTP客户端实现。

代码语言:javascript
复制
@Configuration
public class ContextConfig {

    //默认使用JDK 自带的HttpURLConnection作为底层实现
    @Bean
    public RestTemplate restTemplate(){
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate;
    }
}

源码分析

RestTemplate只是对其他的HTTP客户端的封装,其本身并没有实现HTTP相关的基础功能。其底层实现是可以配置切换的,我们本小节就带着大家来看一下RestTemplate底层实现,及如何实现底层基础HTTP库的切换。

RestTemplate有一个非常重要的类叫做HttpAccessor,可以理解为用于HTTP接触访问的基础类。下图为源码:

代码语言:javascript
复制
public abstract class HttpAccessor {

	/** Logger available to subclasses. */
	protected final Log logger = HttpLogging.forLogName(getClass());

	private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

	private final List<ClientHttpRequestInitializer> clientHttpRequestInitializers = new ArrayList<>();


	/**
	 * Set the request factory that this accessor uses for obtaining client request handles.
	 * <p>The default is a {@link SimpleClientHttpRequestFactory} based on the JDK's own
	 * HTTP libraries ({@link java.net.HttpURLConnection}).
	 * <p><b>Note that the standard JDK HTTP library does not support the HTTP PATCH method.
	 * Configure the Apache HttpComponents or OkHttp request factory to enable PATCH.</b>
	 * @see #createRequest(URI, HttpMethod)
	 * @see SimpleClientHttpRequestFactory
	 * @see org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory
	 * @see org.springframework.http.client.OkHttp3ClientHttpRequestFactory
	 */
	public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
		Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
		this.requestFactory = requestFactory;
	}
	......
}

从源码中我们可以分析出以下几点信息:

  • RestTemplate 支持至少三种HTTP客户端库。
    • SimpleClientHttpRequestFactory。对应的HTTP库是Java JDK自带的HttpURLConnection
    • HttpComponentsAsyncClientHttpRequestFactory。对应的HTTP库是Apache HttpComponents
    • OkHttp3ClientHttpRequestFactory。对应的HTTP库是OkHttp
  • Java JDK自带的HttpURLConnection是默认的底层HTTP实现客户端
  • SimpleClientHttpRequestFactory,即Java JDK自带的HttpURLConnection不支持HTTP协议的Patch方法,如果希望使用Patch方法,需要将底层HTTP客户端实现切换为Apache HttpComponentsOkHttp
  • 可以通过设置setRequestFactory方法,来切换RestTemplate的底层HTTP客户端实现类库。

底层实现切换方法

各种HTTP客户端性能以及易用程度评测来看,OkHttp 优于 Apache HttpComponentsApache HttpComponents优于HttpURLConnection。所以我个人更建议大家将底层HTTP实现切换为okHTTP

切换为okHTTP

首先通过maven坐标将okHTTP的包引入到项目中来。

代码语言:javascript
复制
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.7.2</version>
</dependency>

如果是Spring 环境下通过如下方式使用OkHttp3ClientHttpRequestFactory初始化RestTemplate bean对象。

代码语言:javascript
复制
@Configuration
public class ContextConfig {
    @Bean("OKHttp3")
    public RestTemplate OKHttp3RestTemplate(){
        RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory());
        return restTemplate;
    }
}

如果是非Spring环境,直接new RestTemplate(new OkHttp3ClientHttpRequestFactory()之后使用就可以了。

切换为Apache HttpComponents

代码语言:javascript
复制
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.12</version>
</dependency>

使用HttpComponentsClientHttpRequestFactory初始化RestTemplate bean对象

代码语言:javascript
复制
@Bean("httpClient")
public RestTemplate httpClientRestTemplate(){
    RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
    return restTemplate;
}

GET请求使用详解

RestTemplate可以发送HTTP GET请求,经常使用到的方法有两个:

  • getForObject()
  • getForEntity()

二者的主要区别在于,getForObject()返回值是HTTP协议的响应体。getForEntity()返回的是ResponseEntityResponseEntity是对HTTP响应的封装,除了包含响应体,还包含HTTP状态码、contentTypecontentLengthHeader等信息。

getForObject() 方法

1. String方式接受请求结果数据

Spring环境下写一个单元测试用例,以String类型接收响应结果信息

代码语言:javascript
复制
/**
 * 以String的方式接受请求结果数据
 */
@Test
public void simpleTest()
{
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://jsonplaceholder.typicode.com/posts/1";
    String str = restTemplate.getForObject(url, String.class);
    System.out.println(str);
}

getForObject第二个参数为返回值的类型,String.class以字符串的形式接受getForObject响应结果。

代码语言:javascript
复制
{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}

2. POJO对象方式接受结果数据

代码语言:javascript
复制
/**
 * 以POJO对象的方式接受结果数据
 */
@Test
public void simpleTest2()
{
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://jsonplaceholder.typicode.com/posts/1";
    PostDTO postDTO = restTemplate.getForObject(url, PostDTO.class);
    System.out.println(postDTO.toString());
}

POJO的定义如下,根据JSON String的数据格式定义。

代码语言:javascript
复制
@Getter
@Setter
public class PostDTO {

    private int userId;
    private int id;
    private String title;
    private String body;

    @Override
    public String toString() {
        return "PostDTO{" +
                "userId=" + userId +
                ", id=" + id +
                ", title='" + title + '\'' +
                ", body='" + body + '\'' +
                '}';
    }
}

输出打印结果如下:

代码语言:javascript
复制
PostDTO{userId=1, id=1, title='sunt aut facere repellat provident occaecati excepturi optio reprehenderit', body='quia et suscipit
suscipit recusandae consequuntur expedita et cum
reprehenderit molestiae ut ut quas totam
nostrum rerum est autem sunt rem eveniet architecto'}

3. 数组方式接收请求结果

访问http://jsonplaceholder.typicode.com/posts可以获得JSON数组方式的请求结果

代码语言:javascript
复制
/**
 * 以数组的方式接收请求结果
 */
@Test
public void testArrays() {
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://jsonplaceholder.typicode.com/posts";
    PostDTO[] postDTOs = restTemplate.getForObject(url, PostDTO[].class);
    System.out.println("数组长度:" + postDTOs.length);
}

请求的结果被以数组的方式正确接收,输出如下:

代码语言:javascript
复制
数组长度:100

4. 使用占位符号传参的几种方式

下的几个请求都是在访问"http://jsonplaceholder.typicode.com/posts/1",只是使用了占位符语法,这样在业务使用上更加灵活。

  • 使用占位符的形式传递参数:
代码语言:javascript
复制
String url = "http://jsonplaceholder.typicode.com/{1}/{2}";
PostDTO postDTO = restTemplate.getForObject(url, PostDTO.class, "posts", 1);

String url = "http://jsonplaceholder.typicode.com/{type}/{id}";
String type = "posts";
int id = 1;
PostDTO postDTO = restTemplate.getForObject(url, PostDTO.class, type, id);
  • 使用 map 装载参数:
代码语言:javascript
复制
String url = "http://jsonplaceholder.typicode.com/{type}/{id}";
Map<String,Object> map = new HashMap<>();
map.put("type", "posts");
map.put("id", 1);
PostDTO  postDTO = restTemplate.getForObject(url, PostDTO.class, map);

getForEntity()方法

上面的所有的getForObject请求传参方法,getForEntity都可以使用,使用方法上也几乎是一致的,只是在返回结果接收的时候略有差别。使用ResponseEntity<T> responseEntity来接收响应结果。用responseEntity.getBody()获取响应体。响应体内容同getForObject方法返回结果一致。剩下的这些响应信息就是getForEntity比getForObject多出来的内容。

  • HttpStatus statusCode = responseEntity.getStatusCode();获取整体的响应状态信息
  • int statusCodeValue = responseEntity.getStatusCodeValue(); 获取响应码值
  • HttpHeaders headers = responseEntity.getHeaders();获取响应头
代码语言:javascript
复制
@Test
public void testEntityPoJo() {

   RestTemplate restTemplate = new RestTemplate();

   String url = "http://jsonplaceholder.typicode.com/posts/5";
   ResponseEntity<PostDTO> responseEntity = restTemplate.getForEntity(url, PostDTO.class);
   PostDTO postDTO = responseEntity.getBody(); // 获取响应体
   System.out.println("HTTP 响应body:" + postDTO.toString());


   //以下是getForEntity比getForObject多出来的内容
   HttpStatus statusCode = responseEntity.getStatusCode(); // 获取响应码
   int statusCodeValue = responseEntity.getStatusCodeValue(); // 获取响应码值
   HttpHeaders headers = responseEntity.getHeaders(); // 获取响应头

   System.out.println("HTTP 响应状态:" + statusCode);
   System.out.println("HTTP 响应状态码:" + statusCodeValue);
   System.out.println("HTTP Headers信息:" + headers);
}

输出打印结果:

代码语言:javascript
复制
HTTP 响应body:PostDTO{userId=1, id=5, title='nesciunt quas odio', body='repudiandae veniam quaerat sunt sed
alias aut fugiat sit autem sed est
voluptatem omnis possimus esse voluptatibus quis
est aut tenetur dolor neque'}
HTTP 响应状态:200 OK
HTTP 响应状态码:200
HTTP Headers信息:[Date:"Wed, 18 Aug 2021 10:15:35 GMT", Content-Type:"application/json; charset=utf-8", Content-Length:"225", Connection:"keep-alive", x-powered-by:"Express", x-ratelimit-limit:"1000", x-ratelimit-remaining:"995", x-ratelimit-reset:"1628549805", vary:"Origin, Accept-Encoding", access-control-allow-credentials:"true", cache-control:"max-age=43200", pragma:"no-cache", expires:"-1", x-content-type-options:"nosniff", etag:"W/"e1-IivojO0CtPZmcMK0iydTbsfG7Wc"", via:"1.1 vegur", CF-Cache-Status:"HIT", Age:"16347", Accept-Ranges:"bytes", Report-To:"{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=mNiimkAj9nxgJY2mPgt%2BZiLtyBznKyGTACp2FaHMN69AQpEXNkB690z0Isfj3PQ5v39XckkOy3YNoK7tfGdRpRciZpEeOeA6%2BGXdZIMtV9asOAgcVNx%2F81%2Fy3nmbax2ygY9Cq7dCiq00HoDkOuIz"}],"group":"cf-nel","max_age":604800}", NEL:"{"success_fraction":0,"report_to":"cf-nel","max_age":604800}", Server:"cloudflare", CF-RAY:"680a61c1de2f368c-LAX", alt-svc:"h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400, h3=":443"; ma=86400"]

POST请求使用详解

RestTemplatePOST请求也包含两个主要方法:

  • postForObject()
  • postForEntity()

二者的主要区别在于,postForObject()返回值是HTTP协议的响应体。postForEntity()返回的是ResponseEntityResponseEntity是对HTTP响应的封装,除了包含响应体,还包含HTTP状态码、contentType、contentLength、Header等信息。

postForObject()方法

1. 发送JSON格式请求

代码语言:javascript
复制
/**
 * postForObject发送JSON格式请求
 */
@Test
public void simpleTest()  {

    RestTemplate restTemplate = new RestTemplate();

    // 请求地址
    String url = "http://jsonplaceholder.typicode.com/posts";

    // 要发送的数据对象
    PostDTO postDTO = new PostDTO();
    postDTO.setUserId(110);
    postDTO.setTitle("zimug 发布文章");
    postDTO.setBody("zimug 发布文章 测试内容");

    // 发送post请求,并输出结果
    PostDTO result = restTemplate.postForObject(url, postDTO, PostDTO.class);
    System.out.println(result);
}
  • jsonplaceholder.typicode.com是一个可以提供在线免费RESTful测试服务的一个网站;
  • ”/posts"服务接收PostDTO 参数对象,并将请求结果以JSON字符串的形式进行响应。响应结果就是请求参数对象对应的JSON字符串;
  • 所以postForObject方法第二个参数是请求数据对象,第三个参数是返回值类型;
代码语言:javascript
复制
PostDTO{userId=110, id=101, title='zimug 发布文章', body='zimug 发布文章 测试内容'}

2. 模拟表单数据提交

使用postForObject模拟表单数据提交的例子,即:提交x-www-form-urlencoded格式的数据

代码语言:javascript
复制
/**
 * postForObject模拟表单数据提交
 */
@Test
public void simpleTest2() {

    RestTemplate restTemplate = new RestTemplate();

    // 请求地址
    String url = "http://jsonplaceholder.typicode.com/posts";

    // 请求头设置,x-www-form-urlencoded格式的数据
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

    //提交参数设置
    MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
    map.add("title", "zimug 发布文章第二篇");
    map.add("body", "zimug 发布文章第二篇 测试内容");

    // 组装请求体
    HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);

    // 发送post请求,并打印结果,以String类型接收响应结果JSON字符串
    String result = restTemplate.postForObject(url, request, String.class);
    System.out.println(result);
}

请求数据打印结果如下:

代码语言:javascript
复制
{
  "title": "zimug 发布文章第二篇",
  "body": "zimug 发布文章第二篇 测试内容",
  "id": 101
}

3. Url支持占位符语法

如果url地址上面需要传递一些动态参数,可以使用占位符的方式:

代码语言:javascript
复制
String url = "http://jsonplaceholder.typicode.com/{1}/{2}";
String url = "http://jsonplaceholder.typicode.com/{type}/{id}";

具体的用法和使用GET方法请求是一致的。

postForEntity()方法

上面的所有的postForObject请求传参方法,postForEntity都可以使用,使用方法上也几乎是一致的,只是在返回结果接收的时候略有差别。使用ResponseEntity<T> responseEntity来接收响应结果。用responseEntity.getBody()获取响应体。响应体内容同postForObject方法返回结果一致。剩下的这些响应信息就是postForEntity比postForObject多出来的内容。

  • HttpStatus statusCode = responseEntity.getStatusCode();获取整体的响应状态信息
  • int statusCodeValue = responseEntity.getStatusCodeValue(); 获取响应码值
  • HttpHeaders headers = responseEntity.getHeaders();获取响应头
代码语言:javascript
复制
@Test
public void simpleTest() {

    RestTemplate restTemplate = new RestTemplate();

    // 请求地址
    String url = "http://jsonplaceholder.typicode.com/posts";

    // 要发送的数据对象
    PostDTO postDTO = new PostDTO();
    postDTO.setUserId(110);
    postDTO.setTitle("zimug 发布文章");
    postDTO.setBody("zimug 发布文章 测试内容");

    // 发送post请求,并输出结果
    ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, postDTO, String.class);
    String body = responseEntity.getBody(); // 获取响应体
    System.out.println("HTTP 响应body:" + postDTO.toString());


    //以下是postForEntity比postForObject多出来的内容
    HttpStatus statusCode = responseEntity.getStatusCode(); // 获取响应码
    int statusCodeValue = responseEntity.getStatusCodeValue(); // 获取响应码值
    HttpHeaders headers = responseEntity.getHeaders(); // 获取响应头


    System.out.println("HTTP 响应状态:" + statusCode);
    System.out.println("HTTP 响应状态码:" + statusCodeValue);
    System.out.println("HTTP Headers信息:" + headers);
}

输出打印结果

代码语言:javascript
复制
HTTP 响应body:PostDTO{userId=110, id=0, title='zimug 发布文章', body='zimug 发布文章 测试内容'}
HTTP 响应状态:201 CREATED
HTTP 响应状态码:201
HTTP Headers信息:[Date:"Wed, 18 Aug 2021 10:33:01 GMT", Content-Type:"application/json; charset=utf-8", Content-Length:"110", Connection:"keep-alive", x-powered-by:"Express", x-ratelimit-limit:"1000", x-ratelimit-remaining:"999", x-ratelimit-reset:"1629282825", vary:"Origin, X-HTTP-Method-Override, Accept-Encoding", access-control-allow-credentials:"true", cache-control:"no-cache", pragma:"no-cache", expires:"-1", access-control-expose-headers:"Location", location:"http://jsonplaceholder.typicode.com/posts/101", x-content-type-options:"nosniff", etag:"W/"6e-CLaV3nE0EYRnzjaVSaMnfjSuvGo"", via:"1.1 vegur", CF-Cache-Status:"DYNAMIC", Report-To:"{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=uhnKwOL%2FPmY6EfrTsM2AjEcQqpe%2FrQ%2FR1PghzZ9s%2FvPrjjechh1cp5jshrPlQV%2BTwr%2FHnEQnTPRgtr5B8BSPWEAGCmsP4YjAWj9PMD8YnuWL6Ol7pBN6E%2B%2Bfj3k5polSy5IyR7kQ2x12X013Uym6"}],"group":"cf-nel","max_age":604800}", NEL:"{"success_fraction":0,"report_to":"cf-nel","max_age":604800}", Server:"cloudflare", CF-RAY:"680a7b44a8ca530d-LAX", alt-svc:"h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400, h3=":443"; ma=86400"]

postForLocation() 方法

postForLocation的传参的类型、个数、用法基本都和postForObject()postForEntity()一致。和前两者的唯一区别在于返回值是一个URI。该URI返回值体现的是:用于提交完成数据之后的页面跳转,或数据提交完成之后的下一步数据操作URI

代码语言:javascript
复制
@Test
public void testURI() {

    RestTemplate restTemplate = new RestTemplate();

    // 请求地址
    String url = "http://jsonplaceholder.typicode.com/posts";

    PostDTO postDTO = new PostDTO();
    postDTO.setUserId(110);
    postDTO.setTitle("zimug 发布文章");
    postDTO.setBody("zimug 发布文章 测试内容");

    // 发送post请求,并输出结果
    URI uri = restTemplate.postForLocation(url,postDTO);
    System.out.println(uri);
}

输出结果如下,含义是:提交了post之后,该post的id是101,可以通过如下的连接去获取数据。

代码语言:javascript
复制
http://jsonplaceholder.typicode.com/posts/101

HTTP method使用方法详解

熟悉RESTful风格的朋友,应该了解RESTful风格API使用HTTP method表达对资源的操作。

常用HTTP方法

RESTful风格语义(操作)

GET

查询、获取数据

POST

新增、提交数据

DELETE

删除数据

PUT

更新、修改数据

HEAD

获取HTTP请求头数据

OPTIONS

判断URL提供的当前API支持哪些HTTP method方法

使用 DELETE方法去删除资源

代码语言:javascript
复制
/**
 * 删除一个已经存在的资源,使用RestTemplate的delete(uri)方法。
 * 该方法会向URL代表的资源发送一个HTTP DELETE方法请求。
 */
@Test
public void testDelete()  {
    String url = "http://jsonplaceholder.typicode.com/posts/1";
    restTemplate.delete(url);
}

11:35:05.344 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP DELETE http://jsonplaceholder.typicode.com/posts/1
11:35:06.194 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK

使用PUT方法去修改资源

代码语言:javascript
复制
/**
 * 修改一个已经存在的资源,使用RestTemplate的put()方法。
 * 该方法会向URL代表的资源发送一个HTTP PUT方法请求。
 */
@Test
public void testPut()  {
    // 请求地址
    String url = "http://jsonplaceholder.typicode.com/posts/1";

    // 要发送的数据对象(修改数据)
    PostDTO postDTO = new PostDTO();
    postDTO.setUserId(110);
    postDTO.setTitle("zimug 发布文章");
    postDTO.setBody("zimug 发布文章 测试内容");

    // 发送PUT请求
    restTemplate.put(url, postDTO);
}

11:37:21.468 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP PUT http://jsonplaceholder.typicode.com/posts/1
11:37:21.512 [main] DEBUG org.springframework.web.client.RestTemplate - Writing [PostDTO{userId=110, id=0, title='zimug 发布文章', body='zimug 发布文章 测试内容'}] with org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
11:37:22.580 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK

通用请求方法exchange方法

exchange方法是一个通用的方法,它可以发送GET、POST、DELETE、PUT等等HTTP方法请求。

  • 下面的两种方式发送GET请求效果是一样的
代码语言:javascript
复制
//使用getForEntity发送GET请求
ResponseEntity<PostDTO> responseEntity
            = restTemplate.getForEntity(url, PostDTO.class);
//使用exchange发送GET请求
ResponseEntity<PostDTO> responseEntity = restTemplate.exchange(url, HttpMethod.GET,
            null, PostDTO.class);
  • 下面的两种方式发送POST请求效果是一样的
代码语言:javascript
复制
// 使用postForEntity发送POST请求
ResponseEntity<String> responseEntity
            = restTemplate.postForEntity(url, postDTO, String.class);
// 使用exchange发送POST请求
ResponseEntity<String> responseEntity
            = restTemplate.exchange(url, HttpMethod.POST,null, String.class);
  • 下面的两种方式发送DELETE请求效果是一样的,只是一个有返回值,一个返回值为void
代码语言:javascript
复制
// 使用delete发送DELETE请求,返回值为void
restTemplate.delete(url);
// 使用exchange发送DELETE请求
ResponseEntity<String> result = restTemplate.exchange(url, HttpMethod.DELETE,null,String.class);

使用HEAD方法获取HTTP请求头数据

代码语言:javascript
复制
/**
 * 获取某个资源的URI的请求头信息,并且只专注于获取HTTP请求头信息。
 */
@Test
public void testHEAD()  {
    String url = "http://jsonplaceholder.typicode.com/posts/1";
    HttpHeaders httpHeaders  = restTemplate.headForHeaders(url);

    //断言该资源接口数据为JSON类型
    Assert.assertTrue(httpHeaders.getContentType().includes(MediaType.APPLICATION_JSON));
    System.out.println(httpHeaders);
}

11:42:34.621 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP HEAD http://jsonplaceholder.typicode.com/posts/1
11:42:35.209 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
[Date:"Thu, 19 Aug 2021 03:42:35 GMT", Content-Type:"application/json; charset=utf-8", Content-Length:"292", Connection:"keep-alive", X-Powered-By:"Express", X-Ratelimit-Limit:"1000", X-Ratelimit-Remaining:"996", X-Ratelimit-Reset:"1628129969", Vary:"Origin, Accept-Encoding", Access-Control-Allow-Credentials:"true", Cache-Control:"max-age=43200", Pragma:"no-cache", Expires:"-1", X-Content-Type-Options:"nosniff", Etag:"W/"124-yiKdLzqO5gfBrJFrcdJ8Yq0LGnU"", Via:"1.1 vegur", CF-Cache-Status:"HIT", Age:"5897", Accept-Ranges:"bytes", Report-To:"{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=hWkTRUmOhESgmonXAQsTbnj9P5kkUsNARt0JLem2yaEhGmIeKxAEv9trcpOYHUgqmziyFJgYKvFXjf8rruxTEovs02Kc5nzrDbAgFRtNEUAmSbZM9Y4x9CL8LFN57F%2BJXr5mVJz5drWUAkQVBio%2B"}],"group":"cf-nel","max_age":604800}", NEL:"{"success_fraction":0,"report_to":"cf-nel","max_age":604800}", Server:"cloudflare", CF-RAY:"68105f6eaca40538-LAX", alt-svc:"h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400, h3=":443"; ma=86400"]

使用OPTIONS获取HTTP资源支持的method

代码语言:javascript
复制
/**
 * optionsForAllow测试该URL资源是否支持GET、POST、PUT、DELETE,即增删改查。
 */
@Test
public void testOPTIONS()  {
    String url = "http://jsonplaceholder.typicode.com/posts/1";
    Set<HttpMethod> optionsForAllow  = restTemplate.optionsForAllow(url);

    HttpMethod[] supportedMethods = {HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE};
    //测试该url资源是否支持GET、POST、PUT、DELETE,即增删改查
    Assert.assertTrue(optionsForAllow.containsAll(Arrays.asList(supportedMethods)));
}

文件上传与下载

RestTemplate是HTTP客户端库,所以为了使用RestTemplate进行文件上传和下载,需要我们先编写服务端的支持文件上传和下载的程序。

文件上传

代码语言:javascript
复制
RestTemplate restTemplate = new RestTemplate();

@Test
public void testUpload()  {
    // 文件上传服务上传接口
    String url = "http://localhost:2000/spring-master/fileUpload/upload";
    // 待上传的文件(存在客户端本地磁盘)
    String filePath = "/Users/booker/Documents/material/图片/4.jpeg";

    // 封装请求参数
    FileSystemResource resource = new FileSystemResource(new File(filePath));
    MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
    param.add("uploadFile", resource);  //服务端MultipartFile uploadFile
    //param.add("param1", "test");   //服务端如果接受额外参数,可以传递


    // 发送请求并输出结果
    System.out.println("--- 开始上传文件 ---");
    String result = restTemplate.postForObject(url, param, String.class);
    System.out.println("--- 访问地址:" + result);
}

文件上传之后,可以通过上面的访问地址,在浏览器访问。或者通过RestTemplate客户端进行下载。

文件下载

执行下列代码之后,被下载文件url,会被正确的保存到本地磁盘目录targetPath。

代码语言:javascript
复制
@Test
public void testDownLoad() throws IOException {
    // 待下载的文件地址
    String url = "http://localhost:2000/spring-master/0968094e-7332-4705-9893-1884d42a5028.jpeg";
    ResponseEntity<byte[]> rsp = restTemplate.getForEntity(url, byte[].class);
    System.out.println("文件下载请求结果状态码:" + rsp.getStatusCode());

    // 将下载下来的文件内容保存到本地
    String targetPath = "/Users/booker/Documents/projects/git/master/target/1.jpeg";
    Files.write(Paths.get(targetPath), Objects.requireNonNull(rsp.getBody(), "未获取到下载文件"));
}

14:56:13.076 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:2000/spring-master/0968094e-7332-4705-9893-1884d42a5028.jpeg
14:56:13.098 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[application/octet-stream, application/json, application/x-jackson-smile, application/cbor, application/*+json, */*]
14:56:13.112 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
14:56:13.114 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [[B] as "image/jpeg"
文件下载请求结果状态码:200 OK

这种下载方法实际上是将下载文件一次性加载到客户端本地内存,然后从内存将文件写入磁盘。这种方式对于小文件的下载还比较适合,如果文件比较大或者文件下载并发量比较大,容易造成内存的大量占用,从而降低应用的运行效率。

大文件下载

这种下载方式的区别在于

  • 设置了请求头APPLICATION_OCTET_STREAM,表示以流的形式进行数据加载
  • RequestCallback 结合File.copy保证了接收到一部分文件内容,就向磁盘写入一部分内容。而不是全部加载到内存,最后再写入磁盘文件。
代码语言:javascript
复制
/**
 * 大文件下载
 * @throws IOException
 */
@Test
public void testDownLoadBigFile() throws IOException {
    // 待下载的文件地址
    String url = "http://localhost:2000/spring-master/0968094e-7332-4705-9893-1884d42a5028.jpeg";
    // 文件保存的本地路径
    String targetPath = "/Users/lihuan/Documents/projects/git/master/target/2.jpeg";
    //定义请求头的接收类型
    RequestCallback requestCallback = request -> request.getHeaders()
            .setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
    //对响应进行流式处理而不是将其全部加载到内存中
    restTemplate.execute(url, HttpMethod.GET, requestCallback, clientHttpResponse -> {
        Files.copy(clientHttpResponse.getBody(), Paths.get(targetPath));
        return null;
    });
}

14:59:17.443 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:2000/spring-master/0968094e-7332-4705-9893-1884d42a5028.jpeg
14:59:17.463 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK

请求失败异常处理

RestTemplate请求结果异常是可以自定义处理的。

  • ResponseErrorHandler是RestTemplate请求结果的异常处理器接口
代码语言:javascript
复制
# hasError用于判断HttpResponse是否是异常响应(通过状态码)
boolean hasError(ClientHttpResponse response) throws IOException;

# handleError用于处理异常响应结果(非200状态码段)
void handleError(ClientHttpResponse response) throws IOException;
  • DefaultResponseErrorHandler是ResponseErrorHandler的默认实现

从HttpResponse解析出Http StatusCode,如果状态码StatusCode为null,就抛出UnknownHttpStatusCodeException异常。

代码语言:javascript
复制
@Override
public void handleError(ClientHttpResponse response) throws IOException {
    HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
    if (statusCode == null) {
        byte[] body = getResponseBody(response);
        String message = getErrorMessage(response.getRawStatusCode(),
                response.getStatusText(), body, getCharset(response));
        throw new UnknownHttpStatusCodeException(message,
                response.getRawStatusCode(), response.getStatusText(),
                response.getHeaders(), body, getCharset(response));
    }
    handleError(response, statusCode);
}

RestTemplate自定义异常处理

实现自定义异常,实现ResponseErrorHandler 接口就可以。

代码语言:javascript
复制
public class RestErrorHandler implements ResponseErrorHandler {

    /**
     * 判断返回结果response是否是异常结果
     * 主要是去检查response 的HTTP Status
     * 仿造DefaultResponseErrorHandler实现即可
     */
    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        int rawStatusCode = response.getRawStatusCode();
        HttpStatus statusCode = HttpStatus.resolve(rawStatusCode);
        return (statusCode != null ? statusCode.isError(): hasError(rawStatusCode));
    }

    protected boolean hasError(int unknownStatusCode) {
        HttpStatus.Series series = HttpStatus.Series.resolve(unknownStatusCode);
        return (series == HttpStatus.Series.CLIENT_ERROR || series == HttpStatus.Series.SERVER_ERROR);
    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        // 里面可以实现你自己遇到了Error进行合理的处理
        //TODO 将接口请求的异常信息持久化
        System.out.println("handle Error");
    }
}
代码语言:javascript
复制
@Test
public void testEntity() {

    // 初始化RestTemplate
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setErrorHandler(new RestErrorHandler());

    String url = "http://jsonplaceholder.typicode.com/postss/1";
    ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);  //这行抛出异常
    //下面两行代码执行不到
    HttpStatus statusCode = responseEntity.getStatusCode(); // 获取响应码
    System.out.println("HTTP 响应状态:" + statusCode);
}

17:59:48.378 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://jsonplaceholder.typicode.com/postss/1
17:59:48.389 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/x-jackson-smile, application/cbor, application/*+json, */*]
17:59:48.989 [main] DEBUG org.springframework.web.client.RestTemplate - Response 404 NOT_FOUND
handle Error
17:59:48.996 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json;charset=utf-8"
HTTP 响应状态:404 NOT_FOUND

自动重试机制

通用的异常的处理机制:那就是自动重试。也就是说,在RestTemplate发送请求得到非200状态结果的时候,间隔一定的时间再次发送n次请求。n次请求都失败之后,最后抛出HttpClientErrorException。

通过maven坐标引入spring-retry,spring-retry的实现依赖于面向切面编程,所以引入aspectjweaver。以下配置过程都是基于Spring Boot应用。

代码语言:javascript
复制
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.2.5.RELEASE</version>
</dependency>
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
</dependency>

在Spring Boot 应用入口启动类,也就是配置类的上面加上@SpringRetry注解,表示让重试机制生效。

代码语言:javascript
复制
import org.springframework.retry.annotation.EnableRetry;

@EnableRetry

将正确的请求服务地址由“/posts/1”改成“/postss/1”。服务不存在所以抛出404异常,是为了触发重试机制。

代码语言:javascript
复制
@RestController
@RequestMapping(value = "/restRetry")
public class RetryController {

    @Resource
    private RetryService retryService;

    @GetMapping("/retry")
    public HttpStatus retry() {
        return retryService.retry();
    }
}
代码语言:javascript
复制
@Service
public class RetryService {

    @Resource
    private RestTemplate restTemplate;

    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");


    @Retryable(value = RestClientException.class, maxAttempts = 3, backoff = @Backoff(delay = 5000L,multiplier = 2))
    public HttpStatus retry() {
        System.out.println("发起远程API请求:" + DATE_TIME_FORMATTER.format(LocalDateTime.now()));

        String url = "http://jsonplaceholder.typicode.com/postss/1";
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
        // 获取响应码
        return responseEntity.getStatusCode();
    }
}
  • @Retryable注解的方法在发生异常时会重试,参数说明:
    • value:当指定异常发生时会进行重试 ,HttpClientErrorException是RestClientException的子类。
    • include:和value一样,默认空。如果 exclude也为空时,所有异常都重试
    • exclude:指定异常不重试,默认空。如果 include也为空时,所有异常都重试
    • maxAttemps:最大重试次数,默认3
    • backoff:重试等待策略,默认空
  • @Backoff注解为重试等待的策略,参数说明:
    • delay:指定重试的延时时间,默认为1000毫秒
    • multiplier:指定延迟的倍数,比如设置delay=5000,multiplier=2时,第一次重试为5秒后,第二次为10(5x2)秒,第三次为20(10x2)秒。
代码语言:javascript
复制
http://localhost:2000/spring-master/restRetry/retry

发起远程API请求:2021-08-19 18:23:00
发起远程API请求:2021-08-19 18:23:06
发起远程API请求:2021-08-19 18:23:16
2021-08-19 18:23:17.263 ERROR 5111 --- [nio-2000-exec-4] c.s.m.自.c.GlobalExceptionHandlerAdvice   : 系统异常:404 Not Found: [{}]

通过BasicAuth认证

服务提供方通常会通过一定的授权、鉴权认证逻辑来保护API接口。其中比较简单、容易实现的方式就是使用HTTP 的Basic Auth来实现接口访问用户的认证。在服务端加入Basic Auth认证的情况下,该如何使用RestTemplate访问服务端接口。

HttpBasic认证原理说明

  • 首先,HttpBasic模式要求传输的用户名密码使用Base64模式进行加密。如果用户名是 "admin"  ,密码是“ admin”,则将字符串"admin:admin"使用Base64编码算法加密。加密结果可能是:YWtaW46YWRtaW4=。
  • 然后,在Http请求中使用authorization作为一个HTTP请求头Header name,“Basic YWtaW46YWRtaW4=“作为Header的值,发送给服务端。(注意这里使用Basic+空格+加密串)
  • 服务器在收到这样的请求时,到达BasicAuthenticationFilter过滤器,将提取“authorization”的Header值,并使用用于验证用户身份的相同算法Base64进行解码。
  • 解码结果与登录验证的用户名密码匹配,匹配成功则可以继续过滤器后续的访问。

HTTP Basic Auth服务端实现

当然我们也可以不用自己去搭建服务端,给大家介绍一个提供免费在线的RESTful接口服务的网站:https://www.httpbin.org/。这个网站为我们提供了Basic Auth认证测试服务接口。如果我们只是为了学习RestTemplate,直接用这个网站提供的服务就可以了。

这个接口服务是通过OpenAPI(swagger)实现的,所以可以进行在线的访问测试。所以可以先通过页面操作测试一下,再开始下面学习使用RestTemplate访问服务端接口。

请求头方式携带认证信息

在HTTP请求头中携带Basic Auth认证的用户名和密码,具体实现参考下文代码注释:

代码语言:javascript
复制
@Test
public void testBasicAuth() {
    //该url上携带用户名密码是httpbin网站测试接口的要求,
    //真实的业务是不需要在url上体现basic auth用户名密码的
    String url = "http://www.httpbin.org/basic-auth/admin/adminpwd";

    //在请求头信息中携带Basic认证信息(这里才是实际Basic认证传递用户名密码的方式)
    HttpHeaders headers = new HttpHeaders();
    headers.set("authorization", "Basic " + Base64.getEncoder().encodeToString("admin:adminpwd".getBytes()));

    //发送请求
    HttpEntity<String> ans = restTemplate
            .exchange(url, HttpMethod.GET,   //GET请求
                    new HttpEntity<>(null, headers),   //加入headers
                    String.class);  //body响应数据接收类型
    System.out.println(ans);
}

18:40:17.659 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://www.httpbin.org/basic-auth/admin/adminpwd
18:40:17.670 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/x-jackson-smile, application/cbor, application/*+json, */*]
18:40:18.389 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
18:40:18.390 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"
<200,{
  "authenticated": true, 
  "user": "admin"
}
,[Date:"Thu, 19 Aug 2021 10:40:18 GMT", Content-Type:"application/json", Content-Length:"48", Connection:"keep-alive", Server:"gunicorn/19.9.0", Access-Control-Allow-Origin:"*", Access-Control-Allow-Credentials:"true"]>

拦截器方式携带认证信息

上小节中的代码虽然实现了功能,但是不够好。因为每一次发送HTTP请求,我们都需要去组装HttpHeaders 信息,这样不好,造成大量的代码冗余。那么有没有一种方式可以实现可以一次性的为所有RestTemplate请求API添加Http Basic认证信息呢?答案就是:在RestTemplate Bean初始化的时候加入拦截器,以拦截器的方式统一添加Basic认证信息。

代码语言:javascript
复制
@Test
public void testBasicAuth2() {

    // 初始化restTemplate
    RestTemplate restTemplate = new RestTemplate();

    // 实现一个拦截器:使用拦截器为每一个HTTP请求添加Basic Auth认证用户名密码信息
    ClientHttpRequestInterceptor interceptor = (httpRequest, bytes, execution) -> {
        httpRequest.getHeaders().set("authorization",
                "Basic " + Base64.getEncoder().encodeToString("admin:adminpwd".getBytes()));
        return execution.execute(httpRequest, bytes);
    };
    //添加拦截器
    restTemplate.getInterceptors().add(interceptor);

    // 该url上携带用户名密码是httpbin网站测试接口的要求,
    // 真实的业务是不需要在url上体现basic auth用户名密码的
    String url = "http://www.httpbin.org/basic-auth/admin/adminpwd";
    
    // 发送请求
    HttpEntity<String> ans = restTemplate.exchange(url, HttpMethod.GET,   //GET请求
                    new HttpEntity<>(null),   //加入headers
                    String.class);  //body响应数据接收类型
    System.out.println(ans);
}

18:49:43.763 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://www.httpbin.org/basic-auth/admin/adminpwd
18:49:43.773 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/x-jackson-smile, application/cbor, application/*+json, */*]
18:49:44.402 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
18:49:44.403 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"
<200,{
  "authenticated": true, 
  "user": "admin"
}
,[Date:"Thu, 19 Aug 2021 10:49:44 GMT", Content-Type:"application/json", Content-Length:"48", Connection:"keep-alive", Server:"gunicorn/19.9.0", Access-Control-Allow-Origin:"*", Access-Control-Allow-Credentials:"true"]>

上面的方式使用了拦截器,但仍然是我们自己来封装HTTP headers请求头信息。进一步的简化方法就是,Spring RestTemplate 已经为我们提供了封装好的Basic Auth拦截器,我们直接使用就可以了,不需要我们自己去实现拦截器。

代码语言:javascript
复制
@Test
public void testBasicAuth3() {

    // 初始化restTemplate
    // 方式一
//        RestTemplate restTemplate = new RestTemplate();
//        restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor("admin", "adminpwd"));

    // 方式二
    RestTemplate restTemplate = new RestTemplateBuilder().basicAuthentication("admin", "adminpwd").build();


    // 该url上携带用户名密码是httpbin网站测试接口的要求,
    // 真实的业务是不需要在url上体现basic auth用户名密码的
    String url = "http://www.httpbin.org/basic-auth/admin/adminpwd";

    // 在请求头信息中携带Basic认证信息(这里才是实际Basic认证传递用户名密码的方式)
    HttpHeaders headers = new HttpHeaders();
    headers.set("authorization", "Basic " + Base64.getEncoder().encodeToString("admin:adminpwd".getBytes()));

    // 发送请求
    HttpEntity<String> ans = restTemplate.exchange(url, HttpMethod.GET,   //GET请求
            new HttpEntity<>(null, headers),   //加入headers
            String.class);  //body响应数据接收类型
    System.out.println(ans);
}

配置及使用代理

代理Proxy作为跳板成为服务的直接访问者,代理使用者(真正的客户端)是间接访问服务。这样在服务端看来,每次请求是代理发出的,从代理IP池中一直更换代理发送请求,这样能够降低IP封锁的可能。

代码语言:javascript
复制
/**
 * 代理使用者RestTemplate
 */
@Test
public void testProxyIp() {

    RestTemplate restTemplate = new RestTemplate();

    String url = "http://www.httpbin.org/ip";

    SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
    requestFactory.setProxy(
            new Proxy(Proxy.Type.HTTP, new InetSocketAddress("88.99.10.251", 1080)  //设置代理服务
            )
    );
    restTemplate.setRequestFactory(requestFactory);
    //发送请求
    String result = restTemplate.getForObject(url, String.class);
    System.out.println(result);  //打印响应结果
}

代理类型可以是HTTP也可以是SOCKS。下图是"http://www.httpbin.org/ip"的请求响应结果,返回的是代理服务器的ip,而不是我家里的ip。说明我们为RestTemplate 设置的代理生效了。

代码语言:javascript
复制
15:29:39.354 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://www.httpbin.org/ip
15:29:39.368 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[text/plain, application/json, application/x-jackson-smile, application/cbor, application/*+json, */*]
15:29:45.479 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
15:29:45.480 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [java.lang.String] as "application/json"
{
  "origin": "88.99.10.251"
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 源码分析
    • 底层实现切换方法
      • 切换为okHTTP
      • 切换为Apache HttpComponents
  • GET请求使用详解
    • getForObject() 方法
      • 1. String方式接受请求结果数据
      • 2. POJO对象方式接受结果数据
      • 3. 数组方式接收请求结果
      • 4. 使用占位符号传参的几种方式
    • getForEntity()方法
    • POST请求使用详解
      • postForObject()方法
        • 1. 发送JSON格式请求
        • 2. 模拟表单数据提交
        • 3. Url支持占位符语法
      • postForEntity()方法
        • postForLocation() 方法
        • HTTP method使用方法详解
          • 使用 DELETE方法去删除资源
            • 使用PUT方法去修改资源
              • 通用请求方法exchange方法
                • 使用HEAD方法获取HTTP请求头数据
                  • 使用OPTIONS获取HTTP资源支持的method
                  • 文件上传与下载
                    • 文件上传
                      • 文件下载
                        • 大文件下载
                        • 请求失败异常处理
                          • RestTemplate自定义异常处理
                          • 自动重试机制
                          • 通过BasicAuth认证
                            • HttpBasic认证原理说明
                              • HTTP Basic Auth服务端实现
                                • 请求头方式携带认证信息
                                  • 拦截器方式携带认证信息
                                  • 配置及使用代理
                                  相关产品与服务
                                  测试服务
                                  测试服务 WeTest 包括标准兼容测试、专家兼容测试、手游安全测试、远程调试等多款产品,服务于海量腾讯精品游戏,涵盖兼容测试、压力测试、性能测试、安全测试、远程调试等多个方向,立体化安全防护体系,保卫您的信息安全。
                                  领券
                                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档