欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關于遠程調用RestTemplate的使用避坑指南

 更新時間:2021年10月28日 10:15:47   作者:河邊放牛娃  
這篇文章主要介紹了關于遠程調用RestTemplate的使用避坑指南,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

一、前言介紹

RestTemplate是Spring中用于遠程接口調用的工具類,它是Apache的HttpClient的模板封裝,使用起來非常方便,本文將講述這兩天自己在使用RestTemplate過程中遇到的問題,當然這些問題也是由于自己對RestTemplate工具類了解不夠全面不夠透徹造成的,希望自己遇到的這些問題能為大家提前避雷或是遇到類似問題時的一個解決參考。

二、 問題記錄

1. 慎!【url參數(shù)中有json字符串】

在使用RestTemplate進行遠程接口調用時,如果url拼接參數(shù)中json字符串時一定要小心,使用場景如下:利用restTemplate調用user的查詢信息接口,url中的一個參數(shù)user為json字符串格式{\"user\":\"xiaoming,\"age\":"12"}

  // JSON參數(shù)
        Map<String, String> paramMap = new HashMap<>(8);
        paramMap.put("name","xiaoming");
        paramMap.put("age","12");
        String paramJsonStr = JSONObject.toJSONString(paramMap);
 
        // 實際參數(shù) url = "http://localhost:8080/api/user?user={\"name\":\"xiaoming\",\"age\":\"12\"}&country=china";
        String url = "http://localhost:8080/api/user?user=" + paramJsonStr + "&country=china";
        RestTemplate restTemplate = new RestTemplate();
        // 調用出錯
        Object execute = restTemplate.execute(url, HttpMethod.GET, null, null);

此時當我們運行程序時會拋出以下錯誤:

錯誤意思大概是沒有足夠可用的變量值來填充擴展 'name',這是什么鬼意思,別著急讓我們跟跟代碼看看異常拋出的位置,最終定位如下,在創(chuàng)建URI過程中調用了 UriComponents.expandUriComponent()方法拋出異常:

這段代碼的作用其實就是通過NAMES_PATERN規(guī)則匹配到相應字符串然后利用 uriVariables.getValue(varibaleName)進行替換,再看看NAMES_PATERN的值就是用來匹配{}中的字符串內容的

private static final Pattern NAMES_PATTERN = Pattern.compile(\\{([^/]+?)\\});

問題分析到這兒相信大家應該也明白了RestTemplate在創(chuàng)建URI時會進行{param}占位替換,這個規(guī)則在文本輸出時應用比較多,如日志打印和控制臺打印中常有使用:

   String value = "test";
   logger.info("占位參數(shù){}",value);

解決辦法:

找到原因了,那么我們應當如何解決呢,既然RestTemplate在處理url時會進行{}變量替換,那它理應提供相應的接口調用,查看RestTemplate源碼它提供了多個exchange重載方法,其中多個方法都有uriVariables參數(shù),如下所示:

    public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
        URI expanded = this.getUriTemplateHandler().expand(url, uriVariables);
        return this.doExecute(expanded, method, requestCallback, responseExtractor);
    }

那么我們將url稍微修改即可解決問題:

2. 慎!【url參數(shù)中有經(jīng)過URLEncode的字符串】

其實在遇到第一個坑時,我并沒有采用上面給出的解決方式,而是想著將Json字符串經(jīng)過URLEncode編碼后在拼接到url后面,不就沒有{}符號了,不就可以完美解決問題了,心里想著就美滋滋,那讓我們來試一把吧:

      // JSON參數(shù)
        Map<String, String> paramMap = new HashMap<>(8);
        paramMap.put("name","xiaoming");
        paramMap.put("age","12");
        String paramJsonStr = JSONObject.toJSONString(paramMap);
 
        // 實際參數(shù) url = "http://localhost:8080/api/user?user=%7B%22name%22%3A%22xiaoming%22%2C%22age%22%3A%2212%22%7D&country=china";
        String encode = URLEncoder.encode(paramJsonStr, "utf-8");
        String url = "http://localhost:8080/api/user?user="+encode+"&country=china";
        System.out.println(url);
        RestTemplate restTemplate = new RestTemplate();
        Object execute = restTemplate.execute(url, HttpMethod.GET, null, null,paramJsonStr);

json字符串經(jīng)過編碼后已經(jīng)沒有{}符號了,也能夠成功調用接口,但是接口提供方無情的返回了錯誤:參數(shù)無法反序列化。聽這口氣肯定也是這json串的原因,趕緊用Postman試一試:

神奇的一幕出現(xiàn)了,居然成功了! Postman方式調用和RestTemplate調用有什么不一樣,為什么postman行,restTemplate不行?趕緊查看服務器日志看看兩種方式接收到的參數(shù)有和不一樣:

1. Postman方式服務器接收到的url:

"http://localhost:8080/api/user?user=%7B%22name%22%3A%22xiaoming%22%2C%22age%22%3A%2212%22%7D&country=china"

2. RestTemplate方式服務器接收到的url

"http://localhost:8080/api/user?user=%257B%2522name%2522%3A%2522xiaoming%2522%2C%2522age%2522%253A%252212%2522%257D&country=china"

restTemplate居然在每個百分號%后面都擅自加了25這個數(shù)字,難怪服務端沒法解析,它為什么要這么做?難道是restTemplate的url處理bug?讓我們跟一跟代碼看個究竟:

詳細調用層次就不貼了,簡單來說就是RestTemplate在處理url時會對url參數(shù)進行再編碼,也就是會對url中的特殊字符進行轉義,如%號會被轉義為%25,所以傳給服務端的url就被改變了,具體url特殊字符轉義知識請查看這篇文章

既然知道了原因,那么我們應該如何解決呢?

解決方案:

RestTemplate中的URI對象是通過UriTemplateHandler生成的,所以我們只需要利用java.net包中的URI自己構建URI對象傳給RestTemplate即可,這樣url中的特殊字符就不會被轉義了:

3. 慎!【url參數(shù)中存在特殊字符】 --- 針對HttpClient

前面兩個坑然我對RestTemplate有點望而生畏了,既然RestTemplate這么多坑,那好咋們換回老家伙apache家族的HttpClient,本以為可以一切順利,沒成想一坑接著一坑啊!!!

先看看調用場景:按照出生時間去查詢用戶信息

調用請求都還沒發(fā)出就無情報錯了:

通過異常信息可以容易知道在創(chuàng)建URL對象時url參數(shù)索引位置50處有非法字符,而這個字符剛好就是時間參數(shù)中的空格!,跟蹤代碼異常發(fā)生位置發(fā)現(xiàn)下面一段注釋,大意就是不允許url中有特殊字符存在,看了本文第二個坑的應該已經(jīng)明白url中特殊字符為什么需要進行轉義了,這里就不詳細敘述,至此解決方案就呼之欲出了。

解決方案:

需要對參數(shù)中的特殊字符進行轉義:

1. 直接特殊字符替換

2. 利用google的工具包UrlEscapers(可以處理url、xml、html中的特殊字符)

maven依賴

 <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
    <dependency>
      <groupId>com.google.guava</groupId>
          <artifactId>guava</artifactId>
          <version>28.1-jre</version>
    </dependency>

使用方式:UrlEscapers可以對路徑、參數(shù)、片段進行處理,提供了 path,parameter,fragment三個部分的Escape實例

分別調用UrlEscapers類的以下方法獲?。?/p>

  • urlFormParameterEscaper()
  • urlPathSegmentEscaper()
  • urlFragmentEscaper()

總結

本次三個案例本人覺得還是具有典型性,由于平時發(fā)起請求大多通過瀏覽器或者是postman這類的http模擬工具進行,而瀏覽器和模擬工具在內部會對請求url和參數(shù)進行一定處理,例如編碼和轉義,所以平時對這塊關注不多,而當我們在server端自我構建http請求進行遠程調用時這類問題就需要我們特別注意,稍有不慎就會掉入坑中,還有一點感悟在使用一個工具類時應當先大致閱讀一下工具類提供了哪些方法,做到心中有數(shù)使用時就會少走很多彎路。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • IDEA 如何導入別人的javaweb項目進行部署

    IDEA 如何導入別人的javaweb項目進行部署

    這篇文章主要介紹了IDEA 如何導入別人的javaweb項目進行部署,本文給大家分享我的詳細部署過程及遇到問題解決方法,需要的朋友可以參考下
    2023-03-03
  • springcloud gateway自定義斷言規(guī)則詳解,以后綴結尾進行路由

    springcloud gateway自定義斷言規(guī)則詳解,以后綴結尾進行路由

    這篇文章主要介紹了springcloud gateway自定義斷言規(guī)則詳解,以后綴結尾進行路由,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • java壓縮zip文件中文亂碼問題解決方法

    java壓縮zip文件中文亂碼問題解決方法

    這篇文章主要介紹了java壓縮zip文件中文亂碼問題的解決方法,需要的朋友可以參考下
    2014-07-07
  • IDEA提高開發(fā)效率的7個插件(推薦)

    IDEA提高開發(fā)效率的7個插件(推薦)

    這篇文章主要介紹了IDEA提高開發(fā)效率的7個插件(推薦),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • SpringBoot結合JSR303對前端數(shù)據(jù)進行校驗的示例代碼

    SpringBoot結合JSR303對前端數(shù)據(jù)進行校驗的示例代碼

    這篇文章主要介紹了SpringBoot結合JSR303對前端數(shù)據(jù)進行校驗的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09
  • java通過PDF模板填寫PDF表單

    java通過PDF模板填寫PDF表單

    這篇文章主要為大家詳細介紹了java通過PDF模板填寫PDF表單,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • Java的NIO之通道channel詳解

    Java的NIO之通道channel詳解

    這篇文章主要介紹了Java的NIO之通道channel詳解,通道channel由java.nio.channels 包定義的,Channel 表示IO源與目標打開的連接,Channel類類似于傳統(tǒng)的"流",只不過Channel本身不能直接訪問數(shù)據(jù),Channel只能與Buffer進行交互,需要的朋友可以參考下
    2023-10-10
  • Java中轉換器設計模式深入講解

    Java中轉換器設計模式深入講解

    這篇文章主要給大家介紹了關于Java中轉換器設計模式的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Java具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-04-04
  • Servlet開發(fā)JavaWeb工程示例詳解

    Servlet開發(fā)JavaWeb工程示例詳解

    這篇文章主要介紹了Servlet開發(fā)JavaWeb工程示例詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • JavaWeb文件上傳入門教程

    JavaWeb文件上傳入門教程

    這篇文章主要為大家詳細介紹了JavaWeb文件上傳入門教程,分析了文件上傳原理、介紹了第三方上傳組件,感興趣的小伙伴們可以參考一下
    2016-06-06

最新評論