亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

解決FeignClient Get請(qǐng)求參數(shù)接收不到的問題

 更新時(shí)間:2024年11月15日 08:56:36   作者:MysticalYcc  
在使用FeignClient進(jìn)行GET請(qǐng)求時(shí),如果參數(shù)接收不到,通常是因?yàn)镕eign默認(rèn)將參數(shù)綁定為@RequestBody,而GET請(qǐng)求無法包含請(qǐng)求體,解決方法是在Feign接口中為參數(shù)添加@RequestParam注解,或者在SpringMVC的Controller中使用@RequestBody接收參數(shù)

FeignClient Get請(qǐng)求參數(shù)接收不到的問題

場(chǎng)景:

在gateway攔截請(qǐng)求獲取token調(diào)用認(rèn)證服務(wù)認(rèn)證token正確性。

1.在auth-service服務(wù)端提供驗(yàn)證token的服務(wù)接口,它是這個(gè)樣子的

@RestController
@RequestMapping("auth")
public class AuthController {

    @RequestMapping(value = "/info", method = RequestMethod.GET)
    public CommonResponse<String> auth(String token){
        System.out.println(token);
        String s = JwtTokenUtil.parseToken(token);
        System.out.println(s);
        if("ycc".equals(s)){
            return new CommonResponse<>(CommonResultEnum.SUCCESS);
        }
        return new CommonResponse<>(CommonResultEnum.FAILED_INSUFFICIENT_AUTHORITY);

    }

}

這個(gè)接口寫起來非常簡(jiǎn)單,但實(shí)際 springmvc 做了非常多的兼容,使得這個(gè)接口可以接受多種請(qǐng)求方式。

RequestMapping 代表映射的路徑,使用 GET,POST,PUT,DELETE 方式都可以映射到該端點(diǎn)。

SpringMVC 中常用的請(qǐng)求參數(shù)注解有(@RequestParam,@RequestBody,@PathVariable)等。token 被默認(rèn)當(dāng)做 @RequestParam。

形參 String token 由框架使用字節(jié)碼技術(shù)獲取 token 這個(gè)名稱,自動(dòng)檢測(cè)請(qǐng)求參數(shù)中 key 值為 token 的參數(shù),也可以使用 @RequestParam(“token”) 覆蓋變量本身的名稱。

當(dāng)我們?cè)?url 中攜帶 token 參數(shù)或者 form 表單中攜帶 token 參數(shù)時(shí),會(huì)被獲取到。

POST /hello HTTP/1.1
Host: localhost:8987
Content-Type: application/x-www-form-urlencoded

token=xxxxxxxxxxx

GET /auth/info?token=xxxxxx HTTP/1.1
Host: localhost:8987

2.在gateway的一端需要拿到token進(jìn)行Feign調(diào)用auth服務(wù),它是這個(gè)樣子的

  • AuthService
@FeignClient(name = "auth-service")
public interface AuthService {

    @RequestMapping(value = "/auth/info", method = RequestMethod.GET)
    CommonResponse<String> getAuthInfo(String token);
}
  • TokenFilter調(diào)用處
  log.info("authenticate token start...");
            if(token.contains("Bearer")){
                token = token.substring(token.indexOf("Bearer ")+7);
            }
            CommonResponse<String> authInfo = authService.getAuthInfo(token);
            if (!"200".equals(authInfo.getCode())) {
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }

一切看上去都那么對(duì)稱美好,沒有瑕疵。

我們按照寫 SpringMVC 的 RestController 的習(xí)慣寫了一個(gè) FeignClient,按照我們的一開始的想法,由于指定了請(qǐng)求方式是 GET,那么 token 應(yīng)該會(huì)作為 QueryString 拼接到 Url 中吧?發(fā)出一個(gè)這樣的 GET 請(qǐng)求:

GET /auth/info?token=xxx HTTP/1.1
Host: localhost:8987

當(dāng)我們啟動(dòng)項(xiàng)目開始調(diào)用的時(shí)候,

  • gateway調(diào)用處斷點(diǎn)

出現(xiàn)的結(jié)果是:

并沒有按照期望使用 GET 方式發(fā)送請(qǐng)求,而是 POST 方式.

既然這樣,那我們就讓auth-service支持POST請(qǐng)求;

再來調(diào)用一次;

gateway調(diào)用處斷點(diǎn)

出現(xiàn)的結(jié)果是:

這個(gè)時(shí)候顯示成功了,那么現(xiàn)在去auth-service查看結(jié)果;

看了個(gè)寂寞,參數(shù)并未接收到。

Feign 的請(qǐng)求參數(shù)綁定機(jī)制

查看文檔發(fā)現(xiàn),如果不加默認(rèn)的注解,F(xiàn)eign 則會(huì)對(duì)參數(shù)默認(rèn)加上 @RequestBody 注解,而 RequestBody 一定是包含在請(qǐng)求體中的,GET 方式無法包含。所以上述兩個(gè)現(xiàn)象得到了解釋。Feign 在 GET 請(qǐng)求包含 RequestBody 時(shí)強(qiáng)制轉(zhuǎn)成了 POST 請(qǐng)求,而不是報(bào)錯(cuò)。

理解清楚了這個(gè)機(jī)制我們就可以在開發(fā) Feign 接口避免很多坑。而解決上述這個(gè)問題也很簡(jiǎn)單

  • 在 Feign 接口中為 token添加 @RequestParam(“token”) 注解,token必須指定,F(xiàn)eign 的請(qǐng)求參數(shù)不會(huì)利用 SpringMVC 字節(jié)碼的機(jī)制自動(dòng)給定一個(gè)默認(rèn)的名稱。
  • 由于 Feign 默認(rèn)使用 @RequestBody,也可以改造 RestController,使用 @RequestBody 接收。但是,請(qǐng)求參數(shù)通常是多個(gè),推薦使用上述的 @RequestParam,而 @RequestBody 一般只用于傳遞對(duì)象。

Feign 綁定復(fù)合參數(shù)

指定請(qǐng)求參數(shù)的類型與請(qǐng)求方式,上述問題的出現(xiàn)實(shí)際上是由于在沒有理清楚 Feign 內(nèi)部機(jī)制的前提下想當(dāng)然的和 SpringMVC 進(jìn)行了類比。同樣,在使用對(duì)象作為參數(shù)時(shí),也需要注意這樣的問題。

對(duì)于這樣的接口

@FeignClient("book")
public interface BookApi {

    @RequestMapping(value = "/book",method = RequestMethod.POST)
    Book book(@RequestBody Book book); // <1>
  
    @RequestMapping(value = "/book",method = RequestMethod.POST)
    Book book(@RequestParam("id") String id,@RequestParam("name") String name); // <2>
  
    @RequestMapping(value = "/book",method = RequestMethod.POST)
    Book book(@RequestParam Map map); // <3>
  
    // 錯(cuò)誤的寫法
      @RequestMapping(value = "/book",method = RequestMethod.POST)
    Book book(@RequestParam Book book); // <4>

}

使用 @RequestBody 傳遞對(duì)象是最常用的方式。

如果參數(shù)并不是很多,可以平鋪開使用 @RequestParam

使用 Map,這也是完全可以的,但不太符合面向?qū)ο蟮乃枷耄荒軓拇a立刻看出該接口需要什么樣的參數(shù)。

錯(cuò)誤的用法,F(xiàn)eign 沒有提供這樣的機(jī)制自動(dòng)轉(zhuǎn)換實(shí)體為 Map。

按照這個(gè)說法修改我們的接口和FeignClient

  • AuthController
@RestController
@RequestMapping("auth")
public class AuthController {

    @RequestMapping(value = "/info", method = RequestMethod.GET)
    public CommonResponse<String> auth(String token){
        System.out.println(token);
        String s = JwtTokenUtil.parseToken(token);
        System.out.println(s);
        if("ycc".equals(s)){
            return new CommonResponse<>(CommonResultEnum.SUCCESS);
        }
        return new CommonResponse<>(CommonResultEnum.FAILED_INSUFFICIENT_AUTHORITY);

    }

}
  • AuthService
@FeignClient(name = "auth-service")
public interface AuthService {

    @RequestMapping(value = "/auth/info", method = RequestMethod.GET)
    CommonResponse<String> getAuthInfo(@RequestParam("token") String token);
}

再次調(diào)試:

調(diào)用成功…

Feign 中使用 @PathVariable 與 RESTFUL 規(guī)范

這涉及到一個(gè)如何設(shè)計(jì) RESTFUL 接口的話題,我們知道在自從 RESTFUL 在 2000 年初被提出來之后,就不乏文章提到資源,契約規(guī)范,CRUD 對(duì)應(yīng)增刪改查操作等等。

下面筆者從兩個(gè)實(shí)際的接口來聊聊自己的看法。

根據(jù) id 查找用戶接口:

@FeignClient("user")
public interface UserApi {

    @RequestMapping(value = "/user/{userId}",method = RequestMethod.GET)
    String findById(@PathVariable("id") String userId);

}

這應(yīng)該是沒有爭(zhēng)議的,注意前面強(qiáng)調(diào)的,@PathVariable(“id”) 括號(hào)中的 id 不可以忘記。那如果是“根據(jù)郵箱查找用戶呢”? 很有可能下意識(shí)的寫出這樣的接口:

@FeignClient("user")
public interface UserApi {
  
    @RequestMapping(value = "/user/{email}",method = RequestMethod.GET)
    String findByEmail(@PathVariable("email") String email);

}
  • 首先看看 Feign 的問題。email 中通常包含’.‘這個(gè)特殊字符,如果在路徑中包含,會(huì)出現(xiàn)意想不到的結(jié)果。我不想探討如何去解決它(實(shí)際上可以使用 {email:.+} 的方式), 因?yàn)槲矣X得這不符合設(shè)計(jì)。
  • 再談?wù)勔?guī)范的問題。這兩個(gè)接口是否是相似的,email 是否應(yīng)該被放到 path 中?這就要聊到 RESTFUL 的初衷,為什么 userId 這個(gè)屬性被普遍認(rèn)為適合出現(xiàn)在 RESTFUL 路徑中,因?yàn)?id 本身起到了資源定位的作用,他是資源的標(biāo)記。而 email 不同,它可能是唯一的,但更多的,它是資源的屬性,所以,筆者認(rèn)為不應(yīng)該在路徑中出現(xiàn)非定位性的動(dòng)態(tài)參數(shù)。而是把 email 作為 @RequestParam 參數(shù)。

RESUFTL 結(jié)構(gòu)化查詢

筆者成功的從 Feign 的話題過度到了 RESTFUL 接口的設(shè)計(jì)問題,也導(dǎo)致了本文的篇幅變長(zhǎng)了,不過也不打算再開一片文章談了。

再考慮一個(gè)接口設(shè)計(jì),查詢某一個(gè)月某個(gè)用戶的訂單,可能還會(huì)攜帶分頁參數(shù),這時(shí)候參數(shù)變得很多,按照傳統(tǒng)的設(shè)計(jì),這應(yīng)該是一個(gè)查詢操作,也就是與 GET 請(qǐng)求對(duì)應(yīng),那是不是意味著應(yīng)當(dāng)將這些參數(shù)拼接到 url 后呢?再思考 Feign,正如本文的第二段所述,是不支持 GET 請(qǐng)求攜帶實(shí)體類的,這讓我們?cè)O(shè)計(jì)陷入了兩難的境地。而實(shí)際上參考一些 DSL 語言的設(shè)計(jì)如 elasticSearch,也是使用 POST JSON 的方式來進(jìn)行查詢的,所以在實(shí)際項(xiàng)目中,筆者并不是特別青睞 CRUD 與四種請(qǐng)求方式對(duì)應(yīng)的這種所謂的 RESTFUL 規(guī)范,如果說設(shè)計(jì) RESTFUL 應(yīng)該遵循什么規(guī)范,那大概是另一些名詞,如契約規(guī)范和領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)。

@FeignClient("order")
public interface BookApi {

    @RequestMapping(value = "/order/history",method = RequestMethod.POST)
    Page<List<Orders>> queryOrderHistory(@RequestBody QueryVO queryVO);

}

RESTFUL 行為限定

在實(shí)際接口設(shè)計(jì)中,我遇到了這樣的需求,用戶模塊的接口需要支持修改用戶密碼,修改用戶郵箱,修改用戶姓名,而筆者之前閱讀過一篇文章,也是講舍棄 CRUD 而是用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)來規(guī)范 RESTFUL 接口的定義,與項(xiàng)目中我的想法不謀而合。

看似這三個(gè)屬性是同一個(gè)實(shí)體類的三個(gè)屬性,完全可以如下設(shè)計(jì):

@FeignClient("user")
public interface UserApi {

    @RequestMapping(value = "/user",method = RequestMethod.POST)
    User update(@RequestBody User user);

}

但實(shí)際上,如果再考慮多一層,就應(yīng)該產(chǎn)生這樣的思考:這三個(gè)功能所需要的權(quán)限一致嗎?真的應(yīng)該將他們放到一個(gè)接口中嗎?

實(shí)際上,筆者并不希望接口調(diào)用方傳遞一個(gè)實(shí)體,因?yàn)檫@樣的行為是不可控的,完全不知道它到底是修改了什么屬性,如果真的要限制行為,還需要在 User 中添加一個(gè)操作類型的字段,然后在接口實(shí)現(xiàn)方加以校驗(yàn),這太麻煩了。而實(shí)際上,筆者覺得規(guī)范的設(shè)計(jì)應(yīng)當(dāng)如下:

@FeignClient("user")
public interface UserApi {

    @RequestMapping(value = "/user/{userId}/password/update",method = RequestMethod.POST)
    ResultBean<Boolean> updatePassword(@PathVariable("userId) String userId,@RequestParam("password") password);
    
    @RequestMapping(value = "/user/{userId}/email/update",method = RequestMethod.POST)
    ResultBean<Boolean> updateEmail(@PathVariable("userId) String userId,@RequestParam("email") String email);
    
    @RequestMapping(value = "/user/{userId}/username/update",method = RequestMethod.POST)
    ResultBean<Boolean> updateUsername(@PathVariable("userId) String userId,@RequestParam("username") String username);

}
  • 一般意義上 RESTFUL 接口不應(yīng)該出現(xiàn)動(dòng)詞,這里的 update 并不是一個(gè)動(dòng)作,而是標(biāo)記著操作的類型,因?yàn)獒槍?duì)某個(gè)屬性可能出現(xiàn)的操作類型可能會(huì)有很多,所以我習(xí)慣加上一個(gè) update 后綴,明確表達(dá)想要進(jìn)行的操作,而不是僅僅依賴于 GET,POST,PUT,DELETE。實(shí)際上,修改操作推薦使用的請(qǐng)求方式應(yīng)當(dāng)是 PUT,這點(diǎn)筆者的理解是,已經(jīng)使用 update 標(biāo)記了行為,實(shí)際開發(fā)中不習(xí)慣使用 PUT。
  • password,email,username 都是 user 的屬性,而 userId 是 user 的識(shí)別符號(hào),所以 userId 以 PathVariable 的形式出現(xiàn)在 url 中,而三個(gè)屬性出現(xiàn)在 ReqeustParam 中。

順帶談?wù)勥壿媱h除,如果一個(gè)需求是刪除用戶的常用地址,這個(gè) api 的操作類型,我通常也不會(huì)設(shè)計(jì)為 DELETE 請(qǐng)求,而是同樣使用 delete 來標(biāo)記操作行為

@RequestMapping(value = "/user/{userId}/address/{addressId}/delete",method = RequestMethod.POST)
    ResultBean<Boolean> updateEmail(@PathVariable("userId") String userId,@PathVariable("userId") String email);

總結(jié)

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

相關(guān)文章

  • java捕獲AOP級(jí)別的異常并將其傳遞到Controller層

    java捕獲AOP級(jí)別的異常并將其傳遞到Controller層

    如何在一個(gè)現(xiàn)代的Java應(yīng)用中,捕獲AOP(面向切面編程)級(jí)別的異常,并將這些異常傳遞到Controller層進(jìn)行合適的處理,異常處理在構(gòu)建可靠的應(yīng)用程序中起著關(guān)鍵作用,而AOP則可以幫助我們更好地管理和組織代碼,我們將深入研究如何結(jié)合AOP和異常處理來構(gòu)建健壯的應(yīng)用
    2023-09-09
  • java7鉆石語法知識(shí)點(diǎn)總結(jié)

    java7鉆石語法知識(shí)點(diǎn)總結(jié)

    在本篇文章里小編給大家整理的是關(guān)于java7鉆石語法的相關(guān)知識(shí)點(diǎn)內(nèi)容,有需要的朋友們參考下。
    2019-11-11
  • Java 重命名 Excel 工作表并設(shè)置工作表標(biāo)簽顏色的示例代碼

    Java 重命名 Excel 工作表并設(shè)置工作表標(biāo)簽顏色的示例代碼

    這篇文章主要介紹了Java 重命名 Excel 工作表并設(shè)置工作表標(biāo)簽顏色的示例代碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Spring主配置文件(applicationContext.xml) 導(dǎo)入約束詳解

    Spring主配置文件(applicationContext.xml) 導(dǎo)入約束詳解

    在本篇文章里我們給各位整理的是關(guān)于Spring主配置文件(applicationContext.xml) 導(dǎo)入約束的相關(guān)知識(shí)點(diǎn)內(nèi)容,需要參考下。
    2019-08-08
  • Java中該如何優(yōu)雅的使用線程池詳解

    Java中該如何優(yōu)雅的使用線程池詳解

    在java開發(fā)中我們對(duì)“池”的概念并不陌生,常見的有數(shù)據(jù)庫連接池、線程池、對(duì)象池、常量池等等,其作用基本上就是避免頻繁的創(chuàng)建和回收,造成資源浪費(fèi),線程池也不例外,這篇文章主要給大家介紹了關(guān)于Java中該如何優(yōu)雅的使用線程池的相關(guān)資料,需要的朋友可以參考下
    2021-12-12
  • Java中的升序和降序問題

    Java中的升序和降序問題

    這篇文章主要介紹了Java中的升序和降序問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Java中獲取年份月份的幾種常見方法

    Java中獲取年份月份的幾種常見方法

    這篇文章主要給大家介紹了關(guān)于Java中獲取年份月份的幾種常見方法,在開發(fā)應(yīng)用程序時(shí),經(jīng)常需要獲取當(dāng)前的年、月、日,并以特定格式進(jìn)行展示或處理,需要的朋友可以參考下
    2023-09-09
  • 深入理解Java設(shè)計(jì)模式之職責(zé)鏈模式

    深入理解Java設(shè)計(jì)模式之職責(zé)鏈模式

    這篇文章主要介紹了JAVA設(shè)計(jì)模式之職責(zé)鏈模式的的相關(guān)資料,文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解
    2021-11-11
  • Spring零基礎(chǔ)入門IOC

    Spring零基礎(chǔ)入門IOC

    IoC就是比方說有一個(gè)類,我們想要調(diào)用類里面的方法(不是靜態(tài)方法),就要?jiǎng)?chuàng)建該類的對(duì)象,使用對(duì)象調(diào)用方法來實(shí)現(xiàn)。但對(duì)于Spring來說,Spring創(chuàng)建對(duì)象的過程,不是在代碼里面實(shí)現(xiàn)的,而是交給Spring來進(jìn)行配置實(shí)現(xiàn)的
    2022-08-08
  • Spring Boot中捕獲異常錯(cuò)誤信息并將其保存到數(shù)據(jù)庫中的操作方法

    Spring Boot中捕獲異常錯(cuò)誤信息并將其保存到數(shù)據(jù)庫中的操作方法

    這篇文章主要介紹了Spring Boot中捕獲異常錯(cuò)誤信息并將其保存到數(shù)據(jù)庫中的操作方法,通過實(shí)例代碼介紹了使用Spring Data JPA創(chuàng)建一個(gè)異常信息的存儲(chǔ)庫接口,以便將異常信息保存到數(shù)據(jù)庫,需要的朋友可以參考下
    2023-10-10

最新評(píng)論