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內部默認使用SimpleClientHttpRequestFactory
and 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 知識點掃盲
技巧06:利用接收URI參數的RestTemplate.getForObject方法發送Get請求
1.2 常用構造器
技巧01:利用無參構造器創建RestTemplate實例時,什么都是使用默認的【即:使用HttpMessageConverter來實現HTTTP messages 和 POJO 之間的轉換、使用
】SimpleClientHttpRequestFactory
and DefaultResponseErrorHandler
去創建HTTP連接和處理HTTP錯誤
技巧02:利用 RestTemplate(ClientHttpRequestFactory requestFactory) 創建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(); } }
模擬后台代碼:點擊獲取

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; } }
模擬前端請求:點擊獲取

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(); } }
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); }
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