1 RestTemplate

  RestTemplate是在客户端访问 Restful 服务的一个核心类;RestTemplate通过提供回调方法和允许配置信息转换器来实现个性化定制RestTemplate的功能,通过RestTemplate可以封装请求对象,也可以对响应对象进行解析。

  技巧01:RestTemplate默认使用JDK提供的包去建立HTTP连接,当然,开发者也可以使用诸如 Apache HttpComponents, Netty, and OkHttp 去建立HTTP连接。

  技巧02:RestTemplate内部默认使用HttpMessageConverter来实现HTTTP messages 和 POJO 之间的转换,可以通过RestTemplate的成员方法 setMessageConverters(java.util.List<org.springframework.http.converter.HttpMessageConverter<?>>). 去修改默认的转换器。

  技巧03:RestTemplate内部默认使用SimpleClientHttpRequestFactoryand DefaultResponseErrorHandler去创建HTTP连接和处理HTTP错误,可以通过HttpAccessor.setRequestFactory(org.springframework.http.client.ClientHttpRequestFactory) and setErrorHandler(org.springframework.web.client.ResponseErrorHandler)去做相应的修改。.

  1.1 RestTemplate中方法概览

    RestTemplate为每种HTTP请求都实现了相关的请求封装方法

    技巧01:这些方法的命名是有讲究的,方法名的第一部分表示HTTP请求类型,方法名的第二部分表示响应类型

      例如:getForObject 表示执行GET请求并将响应转化成一个Object类型的对象  

    技巧02:利用RestTemplate封装客户端发送HTTP请求时,如果出现异常就会抛出 RestClientException 类型的异常;可以通过在创建RestTemplate对象的时候指定一个ResponseErrorHandler类型的异常处理类来处理这个异常

    技巧02:exchange 和 excute 这两个方法是通用的HTTP请求方法,而且这两个方法还支持额外的HTTP请求类型【PS: 前提是使用的HTTP连接包也支持这些额外的HTTP请求类型】

    技巧03:每种方法都有3个重载方法,其中两个接收String类型的请求路径和响应类型、参数;另外一个接收URI类型的请求路径和响应类型。

    技巧04:使用String类型的请求路径时,RestTemplate会自动进行一次编码,所以为了避免重复编码问题最好使用URI类型的请求路径

      例如:restTemplate.getForObject("http://example.com/hotel list") becomes"http://example.com/hotel%20list"

    技巧05:URI 和URL 知识点扫盲

      参考博文01  参考博文02

    技巧06:利用接收URI参数的RestTemplate.getForObject方法发送Get请求

  1.2 常用构造器

    技巧01:利用无参构造器创建RestTemplate实例时,什么都是使用默认的【即:使用HttpMessageConverter来实现HTTTP messages 和 POJO 之间的转换、使用SimpleClientHttpRequestFactoryand DefaultResponseErrorHandler去创建HTTP连接和处理HTTP错误

    技巧02:利用 RestTemplate(ClientHttpRequestFactoryrequestFactory) 创建RestTemplate实例时使用自定义的requestFactory去创建HTTP连接

    技巧03:利用 RestTemplate(java.util.List<HttpMessageConverter<?>> messageConverters)  创建RestTemplate实例时使用自定义的转换器列表实现HTTTP messages 和 POJO 之间的转换

  1.3 GET相关方法

    技巧01:本博文使用的是SpringBoot项目,利用了一个配置文件来将RestTemplate注入的容器中

package cn.xiangxu.test_demo.common.config;

import cn.xiangxu.test_demo.domain.domain_do.Student;
import cn.xiangxu.test_demo.utils.proxy.JdkProxy;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import org.springframework.web.client.RestTemplate; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* @author 王杨帅
* @create 2018-08-18 13:07
* @desc 创建Bean的配置类
**/
@Configuration
public class BeanConfig { @Bean
public RestTemplate restTemplate() {
return new RestTemplate();
} }

BeanConfig.java

    模拟后台代码:点击获取

package cn.xiangxu.rest_server.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*; /**
* @author 王杨帅
* @create 2018-08-18 21:56
* @desc 模拟get请求的服务端
**/
@RestController
@RequestMapping(value = "/get")
@Slf4j
public class GetServerConroller { /**
* 不带参数的get请求
* @return
*/
@GetMapping(value = "/get01")
public String get01() {
String result = "【get01】连接模拟的get服务端成功";
log.info(result);
return result;
} /**
* 带有请求参数的get请求
* 笔记:
* 1 get请求参数类型:
* 》 url中?后面的请求参数,格式以 key=value 的形式传递;后台需要用@RequestParam注解
* 如果前端的 key 和 后台方法的参数名称一致时可以不用@RequestParam注解【因为@RequestParam注解时默认的参数注解】
* 》 url中的路径参数
* 需要配合@RequestMapping和@PathVariable一起使用
* @param username 请求参数
* @return
*/
@GetMapping(value = "/get02")
public String get02( @RequestParam(value = "name", required = false, defaultValue = "王杨帅") String username) {
String result = "【get02】获取到的请求参数为:name = " + username;
log.info(result);
return result;
} /**
* 带有路径参数的get请求
* @param userId
* @return
*/
@GetMapping(value = "/get03/{id}")
public String get03(
@PathVariable(value = "id") Integer userId
) {
String result = "【get03】获取到的路径参数为:userId = " + userId;
log.info(result);
return result;
} /**
* 既有路径参数又有请求参数的get请求
* 笔记:
* 1 @PathVariable和@RequestParam都可以设定是否必传【默认必传】
* 2 @PathVariable不可以设定默认值,@RequestParam可以设定默认值【默认值就是不传入的时候代替的值】
* 3 @PathVariable如果设置必传为true,前端不传入时就会报错【技巧:开启必传】
* 4 @RequestParam如果设置必传为true,前端不传入还是也会报错【技巧:关闭必传,开启默认值】
* 5 @PathVariable可以设置正则表达式【详情参见:https://www.cnblogs.com/NeverCtrl-C/p/8185576.html】
* @param userId
* @param username
* @return
*/
@GetMapping(value = "/get04/{id}")
public String get04(
@PathVariable(value = "id") Integer userId,
@RequestParam(value = "name", required = false, defaultValue = "王杨帅") String username
) {
String result = "【get04】获取到的路径参数为:userId = " + userId + " 获取到的请求参数为:" + username;
log.info(result);
return result;
} }

远程Restful服务

    模拟前端请求:点击获取

package cn.xiangxu.test_demo.controller;

import cn.xiangxu.test_demo.TestDemoApplicationTests;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate; import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map; @Component
@Slf4j
public class GetClientControllerTest extends TestDemoApplicationTests { @Autowired
private RestTemplate restTemplate; @Test
public void testDemo() throws Exception {
System.out.println("Hello Boy"); } /**
* 请求无参数的get请求
* @return
*/
@Test
public void get01() throws Exception {
String forObject = restTemplate.getForObject(
"http://127.0.0.1:8080/get/get01",
String.class
);
log.info("响应数据为:" + forObject);
} /**
* 请求有请求参数的get请求:利用占位符进行请求参数传递
* @return
*/
@Test
public void get0201() throws Exception {
String forObject = restTemplate.getForObject(
"http://127.0.0.1:8080/get/get02?name={1}",
String.class,
"三少"
);
log.info("响应数据为:" + forObject);
} /**
* 请求有请求参数的get请求:利用Map进行请求参数传递
* @return
*/
@Test
public void get0202() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("name", "warrior");
String forObject = restTemplate.getForObject(
"http://127.0.0.1:8080/get/get02?name={name}",
String.class,
params
);
log.info("响应数据为:" + forObject);
} /**
* 请求有路径参数的get请求:
* @return
*/
@Test
public void get03() throws Exception {
String forObject = restTemplate.getForObject(
"http://127.0.0.1:8080/get/get03/88888888",
String.class);
log.info("响应数据为:" + forObject);
} /**
* 请求既有路径参数又有请求参数逇get请求
* @return
*/
@Test
public void get04() throws Exception {
String forObject = restTemplate.getForObject(
"http://127.0.0.1:8080/get/get04/99999?name={1}",
String.class,
"fury"
);
log.info("响应数据为:" + forObject);
} /**
* 请求无参数的get请求
* @return
*/
@Test
public void get01_e() throws Exception {
ResponseEntity<String> forObject = restTemplate.getForEntity(
"http://127.0.0.1:8080/get/get01",
String.class
);
log.info("状态码:" + forObject.getStatusCode());
log.info("状态值:" + forObject.getStatusCodeValue());
log.info("响应数据为:" + forObject);
} /**
* 请求有请求参数的get请求:利用占位符进行请求参数传递
* @return
*/
@Test
public void get0201_e() throws Exception {
ResponseEntity<String> forObject = restTemplate.getForEntity(
"http://127.0.0.1:8080/get/get02?name={1}",
String.class,
"三少"
);
log.info("响应数据为:" + forObject);
} /**
* 请求有请求参数的get请求:利用Map进行请求参数传递
* @return
*/
@Test
public void get0202_e() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("name", "warrior");
ResponseEntity<String> forObject = restTemplate.getForEntity(
"http://127.0.0.1:8080/get/get02?name={name}",
String.class,
(Map<String, ?>) params);
log.info("响应数据为:" + forObject);
} /**
* 请求有路径参数的get请求:
* @return
*/
@Test
public void get03_e() throws Exception {
ResponseEntity<String> forObject = restTemplate.getForEntity(
"http://127.0.0.1:8080/get/get03/888888",
String.class
);
log.info("响应数据为:" + forObject);
} @Test
public void get04_e() throws Exception {
} /**
* 请求既有路径参数又有请求参数逇get请求
* @return
*/
@Test
public void test01() throws Exception {
ResponseEntity<String> forObject = restTemplate.getForEntity(
"http://127.0.0.1:8080/get/get04/99999?name={1}",
String.class,
"fury"
);
log.info("响应数据为:" + forObject);
} }

模拟客户端

    1.3.1 public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables)

      1》远程服务代码【不带请求参数的】

      1》模拟客户端代码【不带请求参数的】

      2》远程服务代码【带请求参数的】

        技巧01:HTTP请求中url路径?后面的参数就是请求参数格式以 key=value 的形式传递;后台需要用@RequestParam注解,如果前端的 key 和 后台方法的参数名称一致时可以不用@RequestParam注解【因为@RequestParam注解时默认的参数注解】

        技巧02:对于请求参数,最好在服务端利用@RequestParam注解设置该请求参数为非必传参数并设定默认值

      2》模拟客户端代码【带请求参数的】

      3》远程服务代码【带路径参数的】

        技巧01:HTTP请求的路径可以成为路径参数,前提是服务端进行路径配置【即:需要配合@RequestMapping和@PathVariable一起使用】

        技巧02:由于路径参数不能设置默认是,所以在后台通过@PathVariable将路径参数设置成必传可以减少出错率

        技巧03:@PathVariable可以设置正则表达式【详情参见:https://www.cnblogs.com/NeverCtrl-C/p/8185576.html

      3》模拟客户端代码【带路径参数的】

      4》远程服务代码【带路径参数和请求参数的】

        技巧01: @PathVariable和@RequestParam都可以设定是否必传【默认必传】
        技巧02:@PathVariable不可以设定默认值,@RequestParam可以设定默认值【默认值就是不传入的时候代替的值】
        技巧03: @PathVariable如果设置必传为true,前端不传入时就会报错【技巧:开启必传】
        技巧04:@RequestParam如果设置必传为true,前端不传入还是也会报错【技巧:关闭必传,开启默认值】
        技巧05:@PathVariable可以设置正则表达式【详情参见:https://www.cnblogs.com/NeverCtrl-C/p/8185576.html】

      4》模拟客户端代码【带路径参数和请求参数的】

    1.3.2 public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)

      1》远程服务代码【带请求参数的】

      1》模拟客户端代码【带请求参数的】

    1.3.3 public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)

      技巧01:getForObject 和 getForEntity 的区别:后者可以获取到更多的响应信息,前者这可以获取到响应体的数据

      1》远程服务代码【不带请求参数的】

      1》模拟客户端代码【不带请求参数的】

      2》远程服务代码【带请求参数的】

      2》模拟客户端代码【带请求参数的】

      3》远程服务代码【带路径参数的】

      3》模拟客户端代码【带路径参数的】

      4》远程服务代码【带路径参数和请求参数的】

      4》模拟客户端代码【带路径参数和请求参数的】

    1.3.4 public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)

      1》远程服务代码【带请求参数的】

      1》模拟客户端代码【带请求参数的】

  1.4 POST

    服务端源代码:点击前往

package cn.xiangxu.rest_server.controller;

import cn.xiangxu.rest_server.domain.Student;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*; /**
* @author 王杨帅
* @create 2018-08-19 9:41
* @desc 模拟post服务端接口
**/
@RestController
@RequestMapping(value = "/post")
@Slf4j
public class PostServerController { /**
* 模拟只有请求体参数的post请求
* @param student 请求体参数
* @return
*/
@PostMapping(value = "/post01")
public Student post01(
@RequestBody Student student
) {
log.info("【post01】获取到的请求体参数为:" + student);
student.setName("服务端修改");
return student; } /**
* 模拟既有请求体参数又有请求参数的post请求
* @param student 请求体参数
* @param infoMsg 请求参数
* @return
*/
@PostMapping(value = "/post02")
public String post02(
@RequestBody Student student,
@RequestParam(value = "info", required = false, defaultValue = "默认请求参数") String infoMsg
) {
String result = "【post02】-方法体参数为:student -> " + student + " 请求参数为:infoMsg -> " + infoMsg;
log.info(result);
return result;
} /**
* 模拟既有请求体参数又有请求参数和路径参数的post请求
* @param student 请求体参数
* @param infoMsg 请求参数
* @param userId 路径参数
* @return
*/
@PostMapping(value = "/post03/{id}")
public String post03(
@RequestBody Student student,
@RequestParam(value = "info", required = false, defaultValue = "默认请求参数") String infoMsg,
@PathVariable(value = "id", required = false) Integer userId
) { String result = "【psot03】-获取到的请求体参数为:student -> " + student + "\n" +
"请求参数为:infoMsg -> " + infoMsg + "\n" +
"路径参数为:userId -> " + userId; log.info(result); return result; } @GetMapping(value = "/connect")
public String connect() {
String result = "【post】-前后端连接测试成功";
log.info(result);
return result;
} }

    模拟客户端代码:点击前往

package cn.xiangxu.test_demo.controller;

import cn.xiangxu.test_demo.TestDemoApplicationTests;
import cn.xiangxu.test_demo.domain.domain_do.Student;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate; import java.util.HashMap;
import java.util.Map; import static org.junit.Assert.*; @Component
@Slf4j
public class PostClientControllerTest extends TestDemoApplicationTests { @Autowired
private RestTemplate restTemplate; @Test
public void connect() throws Exception {
} /**
* 模拟只有请求体参数的post请求
* @return
*/
@Test
public void post01() throws Exception {
Student student = Student.builder()
.name("warrior")
.age(24)
.build(); Student postForObject = restTemplate.postForObject(
"http://127.0.0.1:8080/post/post01",
student,
Student.class
); System.out.println(postForObject);
} /**
* 模拟既有请求体参数又有请求参数的POST请求【方式01】
* @return
*/
@Test
public void post0201() throws Exception {
Student student = Student.builder()
.name("王杨帅")
.address("渝足")
.build();
String forObject = restTemplate.postForObject(
"http://127.0.0.1:8080/post/post02?info={1}",
student,
String.class,
"模拟前端"
);
log.info("响应数据㘝:" + forObject);
} /**
* 模拟既有请求体参数又有请求参数的POST请求【方式02】
* @return
*/
@Test
public void post0202() throws Exception {
Student student = Student.builder()
.name("warrior")
.address("大足")
.build(); Map<String, Object> param = new HashMap<>();
param.put("info", "模拟前端"); String forObject = restTemplate.postForObject(
"http://127.0.0.1:8080/post/post02?info={info}",
student,
String.class,
param
); log.info("响应数据㘝:" + forObject);
} /**
* 模拟既有请求体参数又有请求参数和路径参数的POST请求
* @return
*/
@Test
public void post03() throws Exception {
Student student = Student.builder()
.name("fury")
.address("合川")
.build(); String forObject = restTemplate.postForObject(
"http://127.0.0.1:8080/post/post03/8888?info={1}",
student,
String.class,
"模拟请求参数" ); log.info("响应数据为:" + forObject);
} /*
下面是利用另外一个方法实现get请求:
笔记:
1 getForObject 和 getForEntity 的区别:后者可以获取到更多的响应信息,前者这可以获取到响应体的数据
*/ /**
* 模拟只有请求体参数的post请求
* @return
*/
@Test
public void post01_e() throws Exception {
Student student = Student.builder()
.name("王杨帅")
.address("渝足")
.build(); ResponseEntity<Student> forObject = restTemplate.postForEntity(
"http://127.0.0.1:8080/post/post01",
student,
Student.class
); log.info("响应数据为:" + forObject.getBody());
} /**
* 模拟既有请求体参数又有请求参数的POST请求【方式01】
* @return
*/
@Test
public void post0201_e() throws Exception {
Student student = Student.builder()
.name("王杨帅")
.address("渝足")
.build();
ResponseEntity<String> forObject = restTemplate.postForEntity(
"http://127.0.0.1:8080/post/post02?info={1}",
student,
String.class,
"模拟前端"
); log.info("响应数据为:" + forObject.getBody());
} /**
* 模拟既有请求体参数又有请求参数的POST请求【方式02】
* @return
*/
@Test
public void post0202_e() throws Exception {
Student student = Student.builder()
.name("warrior")
.address("大足")
.build(); Map<String, Object> param = new HashMap<>();
param.put("info", "模拟前端"); ResponseEntity<String> forObject = restTemplate.postForEntity(
"http://127.0.0.1:8080/post/post02?info={info}",
student,
String.class,
param
); log.info("响应数据为:" + forObject.getBody());
} /**
* 模拟既有请求体参数又有请求参数和路径参数的POST请求
* @return
*/
@Test
public void post03_e() throws Exception {
Student student = Student.builder()
.name("fury")
.address("合川")
.build(); ResponseEntity<String> forObject = restTemplate.postForEntity(
"http://127.0.0.1:8080/post/post03/8888?info={1}",
student,
String.class,
"模拟请求参数" ); log.info("响应数据为:" + forObject.getBody());
} /**
* 请求体封装【利用HttpEntity可以自定义请求体和请求头】
*/
@Test
public void test01() throws Exception {
Student student = Student.builder()
.name("bob")
.address("成都")
.build(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8); HttpEntity<Student> studentHttpEntity = new HttpEntity<>(student, headers); String forObject = restTemplate.postForObject(
"http://127.0.0.1:8080/post/post02?info={1}",
studentHttpEntity,
String.class,
"模拟前端"
); log.info(forObject); } }

    1.4.1 public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables)

      参数解释:

        url -> String类型的请求路径

        request -> 请求体对象

        responseType -> 响应数据类型

        uriVariables -> 请求参数

    1.4.2 public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables)

      参数解释:

        url -> String类型的请求路径

        request -> 请求体对象

        responseType -> 响应数据类型

        uriVariables -> 请求参数

    1.4.3 public <T> T postForObject(URI url, @Nullable Object request, Class<T> responseType)

      参数解释:

        url -> URI类型的请求路径

        request -> 请求体对象

        responseType -> 响应数据类型

    1.4.4 请求体对象说明

      技巧01:请求体对象(@Nullable Object request)可以直接传一个实体,服务端利用@RequestBody接收这个实体即可

      技巧02:请求体对象(@Nullable Object request)也可以传入一个  HttpEntity 的实例,服务端的代码不变;创建 HttpEntity 实例时可以设定请求体数据和请求头数据(详情请参见 HttpEntity 的相关构造函数)

  1.5 其他请求和GET、POST类似

    待更新......

2 WebClient

  WebClient 是一个非阻塞、响应式的HTTP客户端,它以响应式被压流的方式执行HTTP请求;WebClient默认使用 Reactor Netty 作为HTTP连接器,当然也可以通过ClientHttpConnector修改其它的HTTP连接器。

  技巧01:使用WebClient需要进入Spring5的相关依赖,如果使用的是SpringBoot项目的话直接引入下面的依赖就可以啦

        <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.web.reactive.function.client; import java.net.URI;
import java.nio.charset.Charset;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import org.reactivestreams.Publisher;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ClientHttpRequest;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.util.UriBuilder;
import org.springframework.web.util.UriBuilderFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; public interface WebClient {
WebClient.RequestHeadersUriSpec<?> get(); WebClient.RequestHeadersUriSpec<?> head(); WebClient.RequestBodyUriSpec post(); WebClient.RequestBodyUriSpec put(); WebClient.RequestBodyUriSpec patch(); WebClient.RequestHeadersUriSpec<?> delete(); WebClient.RequestHeadersUriSpec<?> options(); WebClient.RequestBodyUriSpec method(HttpMethod var1); WebClient.Builder mutate(); static WebClient create() {
return (new DefaultWebClientBuilder()).build();
} static WebClient create(String baseUrl) {
return (new DefaultWebClientBuilder()).baseUrl(baseUrl).build();
} static WebClient.Builder builder() {
return new DefaultWebClientBuilder();
} public interface RequestBodyUriSpec extends WebClient.RequestBodySpec, WebClient.RequestHeadersUriSpec<WebClient.RequestBodySpec> {
} public interface RequestHeadersUriSpec<S extends WebClient.RequestHeadersSpec<S>> extends WebClient.UriSpec<S>, WebClient.RequestHeadersSpec<S> {
} public interface ResponseSpec {
WebClient.ResponseSpec onStatus(Predicate<HttpStatus> var1, Function<ClientResponse, Mono<? extends Throwable>> var2); <T> Mono<T> bodyToMono(Class<T> var1); <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> var1); <T> Flux<T> bodyToFlux(Class<T> var1); <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> var1);
} public interface RequestBodySpec extends WebClient.RequestHeadersSpec<WebClient.RequestBodySpec> {
WebClient.RequestBodySpec contentLength(long var1); WebClient.RequestBodySpec contentType(MediaType var1); WebClient.RequestHeadersSpec<?> body(BodyInserter<?, ? super ClientHttpRequest> var1); <T, P extends Publisher<T>> WebClient.RequestHeadersSpec<?> body(P var1, Class<T> var2); <T, P extends Publisher<T>> WebClient.RequestHeadersSpec<?> body(P var1, ParameterizedTypeReference<T> var2); WebClient.RequestHeadersSpec<?> syncBody(Object var1);
} public interface RequestHeadersSpec<S extends WebClient.RequestHeadersSpec<S>> {
S accept(MediaType... var1); S acceptCharset(Charset... var1); S cookie(String var1, String var2); S cookies(Consumer<MultiValueMap<String, String>> var1); S ifModifiedSince(ZonedDateTime var1); S ifNoneMatch(String... var1); S header(String var1, String... var2); S headers(Consumer<HttpHeaders> var1); S attribute(String var1, Object var2); S attributes(Consumer<Map<String, Object>> var1); WebClient.ResponseSpec retrieve(); Mono<ClientResponse> exchange();
} public interface UriSpec<S extends WebClient.RequestHeadersSpec<?>> {
S uri(URI var1); S uri(String var1, Object... var2); S uri(String var1, Map<String, ?> var2); S uri(Function<UriBuilder, URI> var1);
} public interface Builder {
WebClient.Builder baseUrl(String var1); WebClient.Builder defaultUriVariables(Map<String, ?> var1); WebClient.Builder uriBuilderFactory(UriBuilderFactory var1); WebClient.Builder defaultHeader(String var1, String... var2); WebClient.Builder defaultHeaders(Consumer<HttpHeaders> var1); WebClient.Builder defaultCookie(String var1, String... var2); WebClient.Builder defaultCookies(Consumer<MultiValueMap<String, String>> var1); WebClient.Builder clientConnector(ClientHttpConnector var1); WebClient.Builder filter(ExchangeFilterFunction var1); WebClient.Builder filters(Consumer<List<ExchangeFilterFunction>> var1); WebClient.Builder exchangeFunction(ExchangeFunction var1); WebClient.Builder exchangeStrategies(ExchangeStrategies var1); WebClient.Builder clone(); WebClient.Builder apply(Consumer<WebClient.Builder> var1); WebClient build();
}
}

WebClient源码

  2.1 创建WebClient实例的方式

    技巧01:从WebClient的源码中可以看出,WebClient接口提供了三个静态方法来创建WebClient实例

    2.1.1 利用create()创建

      该方法不接受任何参数,返回类型是WebClient

      技巧01:由于利用create()创建的WebClient对象没有设定baseURL,所以  uri("http://127.0.0.1:8080/get/get01") 返回的结果就是最终需要请求的远程服务器的URL;这里的 uri("http://127.0.0.1:8080/get/get01")相当于重写baseURL。

    2.2.2 利用create(String baseUrl)创建

      该方法接收一个String类型的对象作为baseUrl,直接返回WebClient对象

      技巧01:由于利用create(String baseUrl)创建的WebClient对象时已经设定了baseURL,所以 uri("/get01") 会将返回的结果和baseUrl进行拼接组成最终需要远程请求的资源RUL

    2.2.3 利用builder创建【推荐使用】

      该方法不接受任何参数,返回的是 WebClient.Builder

      技巧01:由于返回的不是WebClient类型而是WebClient.Builder,我们可以通过返回的 WebClient.Builder 设置一些配置参数(例如:baseUrl等),然后在调用build就可以返回WebClient对象了【WebClient.Builder先关知识点请继续往下看】

    2.2.4 重写baseURL

      》如果在创建WebClient时没有指定baseURL就可以用uri进行重写【前提:uri的参数必须是一个完整的路径】 -> 完全重写

      》即使在创建WebClient的时候指定了baseURL也可以用uri进行重写【前提:uri的参数必须是一个完整的路径】-> 部分重写

      》即使在创建WebClient的时候指定了baseURL也可以用uri进行重写【前提:此时需要用到参数是一个函数式接口的uri方法】 ->  部分重写

        技巧01:S uri(Function<UriBuilder, URI> var1)  -> 这个uri方法接收一个lambda表达式【输入是UriBuilder,输出时RUI对象】

        技巧02:uri方法返回的URI对象会被转化成远程请求资源的URL

        技巧03:UriBuilder的replacePath方法是替换端口后面的所有信息【详情参见UriBuilder,其实UriBuilder和URI很像】

      》实战技巧

        在使用uri时尽量使用S uri(Function<UriBuilder, URI> var1) ,因为这个方法可以设定更多的配置信息【例如:设置端口、设置参数、拼接路径、替换路径、替换参数......】

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.web.util; import java.net.URI;
import java.util.Map;
import org.springframework.lang.Nullable;
import org.springframework.util.MultiValueMap; public interface UriBuilder {
UriBuilder scheme(@Nullable String var1); UriBuilder userInfo(@Nullable String var1); UriBuilder host(@Nullable String var1); UriBuilder port(int var1); UriBuilder port(@Nullable String var1); UriBuilder path(String var1); UriBuilder replacePath(@Nullable String var1); UriBuilder pathSegment(String... var1) throws IllegalArgumentException; UriBuilder query(String var1); UriBuilder replaceQuery(@Nullable String var1); UriBuilder queryParam(String var1, Object... var2); UriBuilder queryParams(MultiValueMap<String, String> var1); UriBuilder replaceQueryParam(String var1, Object... var2); UriBuilder replaceQueryParams(MultiValueMap<String, String> var1); UriBuilder fragment(@Nullable String var1); URI build(Object... var1); URI build(Map<String, ?> var1);
}

UriBuilder.java

  

  2.2 WebClient相关接口概览

    2.2.1 WebClient.Builder

    public interface Builder {
WebClient.Builder baseUrl(String var1); WebClient.Builder defaultUriVariables(Map<String, ?> var1); WebClient.Builder uriBuilderFactory(UriBuilderFactory var1); WebClient.Builder defaultHeader(String var1, String... var2); WebClient.Builder defaultHeaders(Consumer<HttpHeaders> var1); WebClient.Builder defaultCookie(String var1, String... var2); WebClient.Builder defaultCookies(Consumer<MultiValueMap<String, String>> var1); WebClient.Builder clientConnector(ClientHttpConnector var1); WebClient.Builder filter(ExchangeFilterFunction var1); WebClient.Builder filters(Consumer<List<ExchangeFilterFunction>> var1); WebClient.Builder exchangeFunction(ExchangeFunction var1); WebClient.Builder exchangeStrategies(ExchangeStrategies var1); WebClient.Builder clone(); WebClient.Builder apply(Consumer<WebClient.Builder> var1); WebClient build();
}

      该接口可用来在创建WebClient实例时修改一些默认的配置,而且灵活性特别大;利用该接口对象可以设置WebClient的cookie、header等信息。

      》获取WebClient.Builder对象

        技巧01:获取WebClient.Builder对象或就可以进行一些配置设置了,配置完成后调用build方法就可以生成WebClient对象啦

WebClient.Builder builder = WebClient.builder();

    2.2.2 WebClient.RequestBodySpec

      该接口主要用来这顶媒体类型和请求体的【需要请求体类型的HTTP请求需要用到,例如:POST】

      技巧01:调用post()后返回的类型是RequestBodyUriSpec

      技巧02:uri()方法需要 UriSpec 类型的实例才可以调用,但是 UriSpec  恰恰继承了 RequestBodyUriSpec,uri() 方法返回的类型是RequestHeadersSpec

      技巧03:contentType() 方法需要RequestBodySpec类型的实例才可以调用,但是RequestHeadersSpec的父接口是RequestHeadersSpec,contentType() 的返回类型是WebClient.RequestBodySpec

      技巧04:body() 方法需要RequestBodySpec类型的实例才可以调用,body() 的返回值类型是WebClient.RequestHeadersSpec

      技巧总结:使用WebClient是由严格的调用你顺序的,如下:

        HTTP请求类型 -> 请求URL -> 请求体类型 -> 请求体

    2.2.3 其他接口

      待更新......2018年8月20日21:20:46

      

  2.3 WebClient 请求方法概览

    

    详情信息待更新...... 2018年8月20日21:38:05

       

SpringBoot26 RestTemplate、WebClient的更多相关文章

  1. Spring Cloud Alibaba基础教程:支持的几种服务消费方式(RestTemplate、WebClient、Feign)

    通过<Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现>一文的学习,我们已经学会如何使用Nacos来实现服务的注册与发现,同时也介绍如何通过LoadBal ...

  2. SpringMvc 中的实用工具类介绍(包括 ResponseEntity、 RestTemplate、WebUtils 等)

    此部分内容将包含 ResponseEntity. RestTemplate.WebUtils 等 1. ResponseEntity ① Sprring Mvc 中作为方法的返回值使用法 @Reque ...

  3. .Net5下WebRequest、WebClient、HttpClient是否还存在使用争议?

    WebRequest.WebClient.HttpClient 是C#中常用的三个Http请求的类,时不时也会有人发表对这三个类使用场景的总结,本人是HttpClient 一把梭,也没太关注它们的内部 ...

  4. Silverlight项目笔记7:xml/json数据解析、TreeView、引用类型与数据绑定错误、图片加载、虚拟目录设置、silverlight安全机制引发的问题、WebClient缓存问题

    1.xml/json数据解析 (1)xml数据解析 使用WebClient获取数据,获取到的数据实例化为一个XDocument,使用XDocument的Descendants(XName)方法获得对应 ...

  5. HttpWebRequest、HttpWebResponse、HttpClient、WebClient等http网络访问类的使用示例汇总

    工作中长期需要用到通过HTTP调用API以及文件上传下载,积累了不少经验,现在将各种不同方式进行一个汇总. 首先是HttpWebRequest: /// <summary> /// 向服务 ...

  6. 78、WebClient实现上传下载 System.Net、System.Uri类

    高层类.使用简单.均支持异步版本.支持http,https,fpt,files等URI. 一.下载 方法: Stream= client.OpenRead(serverUri): 打开一个可读的流.对 ...

  7. webrequest、httpwebrequest、webclient、HttpClient 四个类的区别

    一.在 framework 开发环境下: webrequest.httpwebreques  都是基于Windows Api 进行包装, webclient 是基于webrequest 进行包装:(经 ...

  8. 基于springboot的RestTemplate、okhttp和HttpClient对比

    1.HttpClient:代码复杂,还得操心资源回收等.代码很复杂,冗余代码多,不建议直接使用. 2.RestTemplate: 是 Spring 提供的用于访问Rest服务的客户端, RestTem ...

  9. C#中HttpWebRequest、WebClient、HttpClient的使用

    HttpWebRequest: 命名空间: System.Net,这是.NET创建者最初开发用于使用HTTP请求的标准类.使用HttpWebRequest可以让开发者控制请求/响应流程的各个方面,如  ...

随机推荐

  1. python学习之面向对象(上)

    定义了一个Animal类,该类包括了构造函数,私有方法,公有方法,静态方法,属性的方问等 双下划线"__"组成了私有成员的定义约束,其它情况则为公有成员 #_metaclass_= ...

  2. [CSU1911]Card Game

    vjudge 题意 两个数组\(\{a_i\}\)和\(\{b_i\}\),求从中分别选出两个数或运算结果为\(x\)的方案数. sol 裸的FWT. code #include<cstdio& ...

  3. 类数组对象:arguments

    在js中调用一个函数的时候,我们经常会给这个函数传递一些参数,js把传入到这个函数的全部参数存储在一个叫做arguments的东西里面,那它到底是什么呢? 一.描述 arguments 是一个对应于传 ...

  4. C# 反射之SqlDatareader转换为Model实体.

    虽说反射的效率比较低,但是在当今的时代,盛行的大数据,以及高并发的产生,硬件的产能正在逐渐的提升,所以我们可以用空间来换取时间.反射所消耗的那些性能问题其实在企业级开发而言也就无所谓了.二 : 反射得 ...

  5. FIR滤波器和IIR滤波器的区别

    数字滤波器广泛应用于硬件电路设计,在离散系统中尤为常见,一般可以分为FIR滤波器和IIR滤波器,那么他们有什么区别和联系呢. FIR滤波器 定义: FIR滤波器是有限长单位冲激响应滤波器,又称为非递归 ...

  6. java代码=--数组复制

    总结:arraycopy注意数组定义的长度.不足会补0 package clientFrame; //数组的复制arraycopy() public class Xiang { public stat ...

  7. if-else 循环嵌套结构

    package com.a; import java.util.*; public class Core2 {         public static void main(String[] arg ...

  8. java代码逆序输出数字

    总结:请告诉我更好的方法~~~总觉得不好. package com.badu; import java.util.Scanner; //逆序输出数字: // class fa { public sta ...

  9. 摆花 (DP动态规划)

    2012_p3 摆花 (flower.cpp/c/pas) 时间限制: 1 Sec  内存限制: 128 MB提交: 17  解决: 10[提交][状态][讨论版][命题人:外部导入] 题目描述 3. ...

  10. jQuery笔记——DOM操作

    在 JavaScript 中,DOM 不但内容庞大繁杂,而且我们开发的过程中需要考虑更多的兼容性.扩展性.在 jQuery 中,已经将最常用的 DOM 操 作方法进行了有效封装,并且不需要考虑浏览器的 ...