Spring MVC Rest 客户端 RestTemplate 详解

旧城等待, 2023-07-18 06:23 50阅读 0赞

目录

RestTemplate Rest 模板概述

Http GET 请求

Http Post 请求

RestTemplate 超时时间

切换 OkHttpClient 客户端


RestTemplate Rest 模板概述

带负载均衡(@LoadBalanced)的 RestTemplate 必须使用微服务名称发起请求,不能使用 ip:port
不带负载均衡(@LoadBalanced)的 RestTemplate 不能使用微服务名称发起请求,只能使用 ip:port

1、org.springframework.web.client.RestTemplate 类是 spring-web-x.x.x.RELEASE.jar 包下进行 HTTP 访问的 REST 客户端核心类。

2、Java 应用后台代码中如果想要向另外一个 Java 应用发起 Http 请求,通常使用 Apache 的 HttpClient 库来做。而 spring cloud 微服务架构中,各个微服务之间会频繁的发起 http 请求进行通信,所以 spring 对 http 请求进行了封装,以便请求使用起来更加方便。

3、RestTemplate 类能够以 rest 风格的方式,以 GET, POST, PUT, DELETE, HEAD, OPTIONS 等不同的方式向服务器发起HTTP 请求。

4、RestTemplate 对 http 的封装类似 JdbcTemplate 对 jdbc 的封装。本文环境:Java jdk 8 + Spring boot 2.1.3(spring 5.1.5)。




















构造函数 描述
RestTemplate() 默认使用 org.springframework.http.client.SimpleClientHttpRequestFactory 客户端,底层使用 jdk 标准的 API,即 java.net 包下的 API,如 java.net.HttpURLConnection 等。
RestTemplate(ClientHttpRequestFactory requestFactory) ClientHttpRequestFactory 接口的实现类给出底层实现的第三方 HTTP 客户端软件。如 OkHttp3Client、以及 RibbonClient 等
RestTemplate(List<HttpMessageConverter<?>> messageConverters) HttpMessageConverter 接口的实现对象能够在HTTP消息与Java POJO之间进行数据转换。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdteDE5OTMzMjg_size_16_color_FFFFFF_t_70

5、使用 RestTemplate 非常简单,对于 Spring boot、Spring Cloud 项目,spring-boot-starter-web 组件内部已经包含了 spring-web-x.x.x.RELEASE.jar ,所以只需要将 RestTemplate 实例交由 Spring 容器管理,然后在需要的地方获取使用即可。

  1. //可以直接在启动类(@SpringBootApplication) 中进行添加,或者在 @Configuration 标注的类中添加
  2. @Bean
  3. public RestTemplate restTemplate() {
  4. //将 RestTemplate 实例交由 Spring 容器管理
  5. return new RestTemplate();
  6. }
  7. //在使用 RestTemplate 的 Controller 或者其它地方自动注入,然后使用
  8. @Resource
  9. private RestTemplate restTemplate;

Http GET 请求
































方法 描述
<T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) url:请求的地址,responseType:返回的数据类型,ResponseEntity 对象包含了http响应头信息、响应正文、响应状态等信息
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) uriVariables:用于设置查询参数的可变值,map 的 key 值对于url中设置的可变参数值.
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object… uriVariables) uriVariables:专门用于处理 url 路径中的参数 @PathVariable
<T> T getForObject(URI url, Class<T> responseType) getForObject 方法是重载的方法,区别在于返回值直接是响应正文,不含响应头信息以及响应状态.
<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)  
<T> T getForObject(String url, Class<T> responseType, Object… uriVariables)  

getForEntity(URI url, Class responseType)

  1. /** 接口提供方
  2. * http://localhost:8080/getInfo?uid=3030&agencyId=20807
  3. * @param uid
  4. * @param agencyId
  5. * @return
  6. */
  7. @GetMapping("getInfo")
  8. public String getInfo(@RequestParam String uid, String agencyId) {
  9. JSONObject jsonObject = new JSONObject();
  10. jsonObject.put("code", 200);
  11. jsonObject.put("uid", uid);
  12. jsonObject.put("agencyId", agencyId);
  13. return jsonObject.toString();
  14. }
  15. //接口调用方
  16. @GetMapping("test1")
  17. public String test1() {
  18. String body = null;
  19. try {
  20. URI uri = new URI("http://localhost:8080/getInfo?uid=3030&agencyId=20807");
  21. ResponseEntity<String> forEntity = restTemplate.getForEntity(uri, String.class);
  22. //http响应状态码,如成功时返回 200
  23. int statusCodeValue = forEntity.getStatusCodeValue();
  24. //http响应的正文内容,即对方返回的数据,如 {"uid":"3030","code":200,"agencyId":"20807"}
  25. body = forEntity.getBody();
  26. //http响应的头信息,如 Content-Type=[text/plain;charset=UTF-8], Content-Length=[44], Date=[Tue, 24 Mar 2020 12:45:43 GMT]}
  27. HttpHeaders headers = forEntity.getHeaders();
  28. System.out.println("headers=" + headers + "\nstatusCodeValue=" + statusCodeValue + "\nbody=" + body);
  29. } catch (URISyntaxException e) {
  30. e.printStackTrace();
  31. }
  32. return body;
  33. }

getForEntity(String url, Class responseType, Object… uriVariables)

  1. /**
  2. * 接口提供方
  3. * http://localhost:8080/getData
  4. * @param uid
  5. * @param agencyId
  6. * @return
  7. */
  8. @GetMapping("getData/{agencyId}/{uid}")
  9. public List<Map<String, Object>> getData(@PathVariable String uid, @PathVariable String agencyId, String info) {
  10. List<Map<String, Object>> dataMapList = new ArrayList<>(8);
  11. Map<String, Object> map = new HashMap<>(8);
  12. map.put("code", 200);
  13. map.put("uid", uid);
  14. map.put("info", info);
  15. map.put("agencyId", agencyId);
  16. dataMapList.add(map);
  17. return dataMapList;
  18. }
  19. //接口调用方
  20. @GetMapping("test2")
  21. public List<Map<String, Object>> test2() {
  22. String uri = "http://localhost:8080/getData/{agency_id}/{u_id}?info=长沙";
  23. //返回数据类型为 List 类型
  24. ResponseEntity<List> forEntity = restTemplate.getForEntity(uri, List.class, 20792, "999UP000");
  25. //http响应状态码,如成功时返回 200
  26. int statusCodeValue = forEntity.getStatusCodeValue();
  27. //http响应的正文内容,即对方返回的数据,如 {"uid":"3030","code":200,"agencyId":"20807"}
  28. List<Map<String, Object>> body = forEntity.getBody();
  29. //http响应的头信息,如 Content-Type=[text/plain;charset=UTF-8], Content-Length=[44], Date=[Tue, 24 Mar 2020 12:45:43 GMT]}
  30. HttpHeaders headers = forEntity.getHeaders();
  31. System.out.println("headers=" + headers + "\nstatusCodeValue=" + statusCodeValue + "\nbody=" + body);
  32. return body;
  33. }

getForObject(String url, Class responseType, Map uriVariables)

  1. /**
  2. * 接口提供方
  3. * http://localhost:8080/getMessage
  4. *
  5. * @param uid
  6. * @param agencyId
  7. * @return
  8. */
  9. @GetMapping("getMessage")
  10. public JSONObject getMessage(String uid, String agencyId) {
  11. List<Map<String, Object>> dataMapList = new ArrayList<>(8);
  12. Map<String, Object> map = new HashMap<>(8);
  13. JSONObject jsonObject = new JSONObject();
  14. jsonObject.put("code", 200);
  15. jsonObject.put("uid", uid);
  16. jsonObject.put("agencyId", agencyId);
  17. return jsonObject;
  18. }
  19. //接口调用方
  20. @GetMapping("test3")
  21. public JSONObject test3() {
  22. String uri = "http://localhost:8080/getMessage?uid={uid}&agencyId={agency_id}";
  23. Map<String, Object> paramMap = new HashMap<>(4);
  24. paramMap.put("uid", "9998UYT9");
  25. paramMap.put("agency_id", 20802);
  26. //getForObject 返回数据类型为 JSONObject 类型的响应内容,即直接获取返回值.
  27. JSONObject body = restTemplate.getForObject(uri, JSONObject.class, paramMap);
  28. System.out.println(body);
  29. return body;
  30. }

20200324215517266.gif

Http Post 请求
































方法 描述
<T> ResponseEntity<T> postForEntity(URI url, @Nullable Object request, Class<T> responseType)

url:请求的地址;

request:请求的正文(body)内容,即对方 @RequestBody 参数的值,可以为 null。

responseType:返回的数据类型,

ResponseEntity 对象包含了http响应头信息、响应正文、响应状态等信息

<T> ResponseEntity<T> postForEntity(String url, @Nullable Object request,Class<T> responseType, Map<String, ?> uriVariables) uriVariables:用于设置查询参数的可变值,map 的 key 值对于url中设置的可变参数值.
<T> ResponseEntity<T> postForEntity(String url, @Nullable Object request,Class<T> responseType, Object… uriVariables) uriVariables:专门用于处理 url 路径中的参数 @PathVariable
T postForObject(URI url, @Nullable Object request, Class<T> responseType) getForObject 方法是重载的方法,区别在于返回值直接是响应正文,不含响应头信息以及响应状态.
T postForObject(String url, @Nullable Object request, Class<T> responseType,Map<String, ?> uriVariables)  
T postForObject(String url, @Nullable Object request, Class<T> responseType,Object… uriVariables)  

postForEntity(String url, @Nullable Object request,Class responseType, Map uriVariables)

  1. /**接口提供方
  2. * http://localhost:8080/postInfo?uid=44KKP990&agencyId=208071
  3. *
  4. * @param uid
  5. * @param agencyId
  6. * @return
  7. */
  8. @PostMapping("postInfo")
  9. public JSONObject postInfo(@RequestParam String uid, String agencyId) {
  10. JSONObject jsonObject = new JSONObject();
  11. jsonObject.put("code", 200);
  12. jsonObject.put("uid", uid);
  13. jsonObject.put("agencyId", agencyId);
  14. return jsonObject;
  15. }
  16. //接口调用方
  17. @GetMapping("test11")
  18. public JSONObject test11() {
  19. JSONObject body = null;
  20. String uri = "http://localhost:8080/postInfo?uid={uid}&agencyId=208071";
  21. Map<String, String> paramMap = new HashMap<>(4);
  22. paramMap.put("uid", "8899HG9");
  23. ResponseEntity<JSONObject> forEntity = restTemplate.postForEntity(uri, null, JSONObject.class, paramMap);
  24. //http响应状态码,如成功时返回 200
  25. int statusCodeValue = forEntity.getStatusCodeValue();
  26. //http响应的正文内容,即对方返回的数据,如 {"uid":"3030","code":200,"agencyId":"20807"}
  27. body = forEntity.getBody();
  28. //http响应的头信息,如 Content-Type=[text/plain;charset=UTF-8], Content-Length=[44], Date=[Tue, 24 Mar 2020 12:45:43 GMT]}
  29. HttpHeaders headers = forEntity.getHeaders();
  30. System.out.println("headers=" + headers + "\nstatusCodeValue=" + statusCodeValue + "\nbody=" + body);
  31. return body;
  32. }

postForObject(String url, @Nullable Object request, Class responseType,Object… uriVariables)

  1. /**
  2. * 接口提供方
  3. * http://localhost:8080/postData/{agencyId}
  4. */
  5. @PostMapping("postData/{agencyId}")
  6. public List<Map<String, Object>> postData(@PathVariable String agencyId, @RequestBody List<String> ids) {
  7. List<Map<String, Object>> dataMapList = new ArrayList<>(8);
  8. Map<String, Object> map = new HashMap<>(8);
  9. map.put("code", 200);
  10. map.put("uid", ids.toArray());
  11. map.put("agencyId", agencyId);
  12. dataMapList.add(map);
  13. return dataMapList;
  14. }
  15. //接口调用方
  16. @GetMapping("test22")
  17. public List<Map<String, Object>> test22() {
  18. String uri = "http://localhost:8080/postData/{agencyId}";
  19. List<String> ids = new ArrayList<>(4);
  20. ids.add("3000L");
  21. ids.add("99UH7");
  22. ids.add("99JHG");
  23. //返回数据类型为 List 类型
  24. List<Map<String,Object>> body = restTemplate.postForObject(uri, ids, List.class, 20792);
  25. return body;
  26. }

//其余操作都是同理

RestTemplate 超时时间

1、new RestTemplate() 时默认使用 SimpleClientHttpRequestFactory 工厂,它底层默认使用 Java 原生的 HttpURLConnection API 做 Http 请求,默认 connectTimeout = -1(连接超时),readTimeout = -1(读取超时)都没有做限制,即一直等待。

20200326202819886.gif

2、显然无限期的等待是绝不允许的,必须设置超时时间,以 SimpleClientHttpRequestFactory 工厂为例:

  1. //设置 http 连接工厂,然后将 RestTemplate 实例交由 Spring 容器管理
  2. @Bean
  3. public RestTemplate restTemplate() {
  4. //先设置 http 连接工厂的连接超时时间为 30 秒,读取超时时间为 120 秒
  5. //请求发送后,对方数据量比较大,需要长时间处理数据时,读取超时时间需要设置的长一些,否则对方还未处理完,连接就超时了。
  6. SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
  7. requestFactory.setConnectTimeout(30 * 1000);
  8. requestFactory.setReadTimeout(120 * 1000);
  9. return new RestTemplate(requestFactory);
  10. }

3、验证非常简单,请求 youtube ,铁定连不上而会超时,然后抛异常: java.net.SocketTimeoutException: Read timed out

String body = restTemplate.getForObject(“https://ca.youtube.com/“, String.class);

切换 OkHttpClient 客户端

1、ClientHttpRequestFactory 有多个实现类,okhttp3 作为现在最热门 http 客户端,这里就以 OkHttpClient 为例。

2、第一步项目中引入 OkHttpClient 的依赖,否则会报:java.lang.NoClassDefFoundError: okhttp3/OkHttpClient

  1. <!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
  2. <dependency>
  3. <groupId>com.squareup.okhttp3</groupId>
  4. <artifactId>okhttp</artifactId>
  5. <version>3.14.5</version>
  6. </dependency>

版本不能随意引,org.springframework.http.client.OkHttp3ClientHttpRequestFactory 类的源码上面通常有提示说明对应的OkHttp 版本,如本文 spring-web-5.0.7.RELEASE.jar 提示的 OkHttp 版本是 3.X,如果引入 4.X 则启动会失败。

3、接下来就是创建 RestTemplate 对象的时候,传入 OkHttp3ClientHttpRequestFactory 工厂即可:

  1. //设置 http 连接工厂,然后将 RestTemplate 实例交由 Spring 容器管理
  2. @Bean
  3. public RestTemplate restTemplate() {
  4. //先设置 http 连接工厂的连接超时时间为 30 秒,读取超时时间为 120 秒
  5. OkHttp3ClientHttpRequestFactory okRequestFactory = new OkHttp3ClientHttpRequestFactory();
  6. okRequestFactory.setConnectTimeout(30 * 1000);
  7. okRequestFactory.setReadTimeout(120 * 1000);
  8. return new RestTemplate(okRequestFactory);
  9. }

验证底层到底用的什么 http 客户端也很简单,仍然请求 youtube(https://ca.youtube.com) ,连接超时时控制层会打印异常,其中就可以看到 okhttp3.internal.xxxx 的错误信息。

4、其它如 Apache 的 HttpClient、以及 Netty、RibbonClient 等 http 客户端使用都是同理。

Ribbon 的负载均衡就是在 RestTemplate 上加上@LoadBalanced,可以参考 Netflix Ribbon 负载均衡

扩展内容可以继续参考:https://blog.csdn.net/f641385712/article/details/100713622

发表评论

表情:
评论列表 (有 0 条评论,50人围观)

还没有评论,来说两句吧...

相关阅读