自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java 聲明式 Http 接口對接框架

開發(fā) 前端
與其說的是對接的Http接口,不如說是對接的第三方渠道,UniHttp可支持自定義接口渠道方HttpAPI注解以及一些自定義的對接和交互行為 ,為此擴(kuò)展了發(fā)送和響應(yīng)和反序列化一個(gè)Http請求的各種生命周期鉤子,開發(fā)者可自行去擴(kuò)展實(shí)現(xiàn)。

1、簡介

一個(gè)聲明式的Http接口對接框架,能以極快的方式完成對一個(gè)第三方Http接口的對接和使用,之后就像調(diào)用本地方法一樣自動(dòng)去發(fā)起Http請求,不需要開發(fā)者去關(guān)注如何發(fā)送一個(gè)請求,如何去傳遞Http請求參數(shù),以及如何對請求結(jié)果進(jìn)行處理和反序列化,這些框架都幫你一一實(shí)現(xiàn)

就像配置 Spring的Controller 那樣簡單,只不過相當(dāng)于是反向配置而已

該框架更注重于如何保持高內(nèi)聚和可讀性高的代碼情況下與快速第三方渠道接口進(jìn)行對接和集成,而非像傳統(tǒng)編程式的Http請求客戶端(比如HttpClient、Okhttp)那樣專注于如何去發(fā)送Http請求,雖然底層也是用的Okhttp去發(fā)送請求。

與其說的是對接的Http接口,不如說是對接的第三方渠道,UniHttp可支持自定義接口渠道方HttpAPI注解以及一些自定義的對接和交互行為 ,為此擴(kuò)展了發(fā)送和響應(yīng)和反序列化一個(gè)Http請求的各種生命周期鉤子,開發(fā)者可自行去擴(kuò)展實(shí)現(xiàn)。

2、快速開始

2.1、引入依賴

<dependency>
  <groupId>io.github.burukeyou</groupId>
  <artifactId>uniapi-http</artifactId>
  <version>0.0.4</version>
</dependency>

2.2、對接接口

首先隨便創(chuàng)建一個(gè)接口,然后在接口上標(biāo)記@HttpApi注解,然后指定請求的域名url, 然后就可以在方法上去配置對接哪個(gè)接口。

比如下面兩個(gè)方法的配置則對接了以下兩個(gè)接口

  • GET http://localhost:8080/getUser
  • POST http://localhost:8080/addUser

方法返回值定義成Http響應(yīng)body對應(yīng)的類型即可,默認(rèn)會(huì)使用fastjson反序列化Http響應(yīng)body的值為該類型對象。

@HttpApi(url = "http://localhost:8080")
interface UserHttpApi {
    
   @GetHttpInterface("/getUser")
   BaseRsp<String> getUser(@QueryPar("name") String param,@HeaderPar("userId") Integer id);
    
   @PostHttpInterface("/addUser")
   BaseRsp<Add4DTO> addUser(@BodyJsonPar Add4DTO req);
   
}
  • @QueryPar  表示將參數(shù)值放到Http請求的查詢參數(shù)內(nèi)
  • @HeaderPar   表示將參數(shù)值放到Http請求的請求頭里
  • @BodyJsonPar 表示將參數(shù)值放到Http請求body內(nèi),并且content-type是application/json
1)getUser方法最終構(gòu)建的Http請求報(bào)文為
GET http://localhost:8080/getUser?name=param
Header:
    userId: id
2)addUser最終構(gòu)建的Http請求報(bào)文為
POST:  http://localhost:8080/addUser 
Header: 
    Content-Type:   application/json
Body:
    {"id":1,"name":"jay"}

2.3、聲明定義的HttpAPI的包掃描路徑

在spring的配置類上使用@UniAPIScan注解標(biāo)記定義的@HttpAPI的包掃描路徑,會(huì)自動(dòng)為標(biāo)記了@HttpApi接口生成代理對象并且注入到Spring容器中,之后只需要像使用Spring的其他bean一樣,依賴注入使用即可

@UniAPIScan("com.xxx.demo.api")
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class,args);
    }
}

2.4、依賴注入使用即可

@Service
class UserAppService {
    
    @Autowired
    private UserHttpApi userHttpApi;
    
    public void doSomething(){
        userHttpApi.getUser("jay",3);
    }
}

3、說明介紹

3.1、@HttpApi注解

用于標(biāo)記接口上,該接口上的方法會(huì)被代理到對應(yīng)的Http請求接口,可指定請求的域名,也可指定自定義的Http代理邏輯等等。

3.2、@HttpInterface注解

用于配置一個(gè)接口的參數(shù),包括請求方式、請求路徑、請求頭、請求cookie、請求查詢參數(shù)等等

并且內(nèi)置了以下請求方式的@HttpInterface,不必再每次手動(dòng)指定請求方式

  • @PostHttpInterface
  • @PutHttpInterface
  • @DeleteHttpInterface
  • @GetHttpInterface
@PostHttpInterface(
    // 請求路徑
    path = "/getUser",
    // 請求頭
    headers = {"clientType:sys-app","userId:99"},
    // url查詢參數(shù) 
    params = {"name=周杰倫","age=1"},
    // url查詢參數(shù)拼接字符串
    paramStr = "a=1&b=2&c=3&d=哈哈&e=%E7%89%9B%E9%80%BC",
    // cookie 字符串
    cookie = "name=1;sessionId=999"
)
BaseRsp<String> getUser();

3.3、@Par注解

以下各種Par后綴的注解,主要用于方法參數(shù)上,用于指定在發(fā)送請求時(shí)將參數(shù)值放到Http請求體的哪部分上。

為了方便描述,下文描述的普通值就是表示String,基本類型、基本類型的包裝類型等類型.

簡單復(fù)習(xí)下Http協(xié)議報(bào)文

圖片圖片

@QueryPar注解

標(biāo)記Http請求url的查詢參數(shù)

支持以下方法參數(shù)類型的標(biāo)記: 普通值、普通值集合、對象、Map

@PostHttpInterface
BaseRsp<String> getUser(@QueryPar("id")  String id,  //  普通值   
                        @QueryPar("ids") List<Integer> idsList, //  普通值集合
                        @QueryPar User user,  // 對象
                        @QueryPar Map<String,Object> map); // Map

如果類型是普通值或者普通值集合需要手動(dòng)指定參數(shù)名,因?yàn)槭钱?dāng)成單個(gè)查詢參數(shù)傳遞

如果類型是對象或者M(jìn)ap是當(dāng)成多個(gè)查詢參數(shù)傳遞,字段名或者map的key名就是參數(shù)名,字段值或者map的value值就是參數(shù)值。

如果是對象,參數(shù)名默認(rèn)是字段名,由于用的是fastjson序列化可以用@JSONField指定別名

@PathPar注解

標(biāo)記Http請求路徑變量參數(shù),僅支持標(biāo)記普通值類型

@PostHttpInterface("/getUser/{userId}/detail")
BaseRsp<String> getUser(@PathPar("userId")  String id);  //  普通值
@HeaderPar注解

標(biāo)記Http請求頭參數(shù)

支持以下方法參數(shù)類型:對象、Map、普通值

@PostHttpInterface
BaseRsp<String> getUser(@HeaderPar("id")  String id,  //  普通值   
                        @HeaderPar User user,  // 對象
                        @HeaderPar Map<String,Object> map); // Map

如果類型是普通值類型需要手動(dòng)指定參數(shù)名,當(dāng)成單個(gè)請求頭參數(shù)傳遞. 如果是對象或者M(jìn)ap當(dāng)成多個(gè)請求頭參數(shù)。

@CookiePar注解

用于標(biāo)記Http請求的cookie請求頭

支持以下方法參數(shù)類型: Map、Cookie對象、字符串

@PostHttpInterface
BaseRsp<String> getUser(@CookiePar("id")  String cookiePar,  //   普通值 (指定name)當(dāng)成單個(gè)cookie鍵值對處理
                        @CookiePar String cookieString,  //  普通值 (不指定name),當(dāng)成完整的cookie字符串處理
                        @CookiePar com.burukeyou.uniapi.http.support.Cookie cookieObj,  // 單個(gè)Cookie對象 
                        @CookiePar List<com.burukeyou.uniapi.http.support.Cookie> cookieList // Cookie對象列表
                        @CookiePar Map<String,Object> map); // Map

如果類型是字符串時(shí),當(dāng)指定參數(shù)名時(shí),當(dāng)成單個(gè)cookie鍵值對處理,如果不指定參數(shù)名時(shí)當(dāng)成完整的cookie字符串處理比如a=1;b=2;c=3 這樣

如果是Map當(dāng)成多個(gè)cookie鍵值對處理。

如果類型是內(nèi)置的 com.burukeyou.uniapi.http.support.Cookie對象當(dāng)成單個(gè)cookie鍵值對處理

@BodyJsonPar注解

用于標(biāo)記Http請求體內(nèi)容為json形式: 對應(yīng)content-type為 application/json

支持以下方法參數(shù)類型: 對象、對象集合、Map、普通值、普通值集合

@PostHttpInterface
BaseRsp<String> getUser(@BodyJsonPar  String id,                //  普通值
                        @BodyJsonPar  String[] id               //  普通值集合
                        @BodyJsonPar List<User> userList,       // 對象集合
                        @BodyJsonPar User user,                  // 對象
                        @BodyJsonPar Map<String,Object> map);    // Map

序列化和反序列化默認(rèn)用的是fastjson,所以如果想指定別名,可以在字段上標(biāo)記 @JSONField 注解取別名

@BodyFormPar注解

用于標(biāo)記Http請求體內(nèi)容為普通表單形式: 對應(yīng)content-type為 application/x-www-form-urlencoded

支持以下方法參數(shù)類型:對象、Map、普通值

@PostHttpInterface
BaseRsp<String> getUser(@BodyFormPar("name") String value,         //  普通值
                        @BodyFormPar User user,                   // 對象
                        @BodyFormPar Map<String,Object> map);    // Map

如果類型是普通值類型需要手動(dòng)指定參數(shù)名,當(dāng)成單個(gè)請求表單鍵值對傳遞

@BodyMultiPartPar注解

用于標(biāo)記Http請求體內(nèi)容為復(fù)雜形式: 對應(yīng)content-type為 multipart/form-data

支持以下方法參數(shù)類型: 對象、Map、普通值、File對象

@PostHttpInterface
BaseRsp<String> getUser(@BodyMultiPartPar("name") String value,         //  單個(gè)表單文本值
                        @BodyMultiPartPar User user,                   // 對象
                        @BodyMultiPartPar Map<String,Object> map,      // Map
                        @BodyMultiPartPar("userImg") File file);     // 單個(gè)表單文件值

如果參數(shù)類型是普通值或者File類型,當(dāng)成單個(gè)表單鍵值對處理,需要手動(dòng)指定參數(shù)名。

如果參數(shù)類型是對象或者M(jìn)ap,當(dāng)成多個(gè)表單鍵值對處理。如果字段值或者map的value參數(shù)值是File類型,則自動(dòng)當(dāng)成是文件表單字段傳遞處理

@BodyBinaryPar注解

用于標(biāo)記Http請求體內(nèi)容為二進(jìn)制形式: 對應(yīng)content-type為 application/octet-stream

支持以下方法參數(shù)類型: InputStream、File、InputStreamSource

@PostHttpInterface
BaseRsp<String> getUser(@BodyBinaryPar InputStream value,         
                        @BodyBinaryPar File user,                   
                        @BodyBinaryPar InputStreamSource map);
@ComposePar注解

這個(gè)注解本身不是對Http請求內(nèi)容的配置,僅用于標(biāo)記一個(gè)對象,然后會(huì)對該對象內(nèi)的所有標(biāo)記了其他@Par注解的字段進(jìn)行嵌套解析處理, 目的是減少方法參數(shù)數(shù)量,支持都內(nèi)聚到一起傳遞

支持以下方法參數(shù)類型: 對象

@PostHttpInterface
BaseRsp<String> getUser(@ComposePar UserReq req);

比如UserReq里面的字段可以嵌套標(biāo)記其他@Par注解,具體支持的標(biāo)記類型和處理邏輯與前面一致

class UserReq {

    @QueryPar
    private Long id;

    @HeaderPar
    private String name;

    @BodyJsonPar
    private Add4DTO req;

    @CookiePar
    private String cook;
}

3.4、原始的HttpResponse

HttpResponse表示Http請求的原始響應(yīng)對象,如果業(yè)務(wù)需要關(guān)注拿到完整的Http響應(yīng),只需要在方法返回值包裝返回即可。

如下面所示,此時(shí)HttpResponse<Add4DTO>里的泛型Add4DTO才是代表接口實(shí)際返回的響應(yīng)內(nèi)容,后續(xù)可直接手動(dòng)獲取

@PostHttpInterface("/user-web/get")
HttpResponse<Add4DTO> get();

通過它我們就可以拿到響應(yīng)的Http狀態(tài)碼、響應(yīng)頭、響應(yīng)cookie等等,當(dāng)然也可以拿到我們的響應(yīng)body的內(nèi)容通過getBodyResult方法

3.5、處理文件下載接口

對于若是下載文件的類型的接口,可將方法返回值定義為 HttpBinaryResponse、HttpFileResponse、HttpInputStreamResponse 的任意一種,這樣就可以拿到下載后的文件。

  • HttpBinaryResponse: 表示下載的文件內(nèi)容以二進(jìn)制形式返回,如果是大文件請謹(jǐn)慎處理,因?yàn)闀?huì)存放在內(nèi)存中
  • HttpFileResponse:  表示下載的文件內(nèi)容以File對象返回,這時(shí)文件已經(jīng)被下載到了本地磁盤
  • HttpInputStreamResponse: 表示下載的文件內(nèi)容輸入流的形式返回,這時(shí)文件其實(shí)還沒被下載到客戶端,調(diào)用者可以自行讀取該輸入流進(jìn)行文件的下載

3.6、HttpApiProcessor 生命周期鉤子

HttpApiProcessor是一個(gè)Http請求接口的各種生命周期鉤子,開發(fā)者可以實(shí)現(xiàn)它在里面自定義編寫各種對接邏輯。然后可以配置到@HttpApi注解或者@HttpInterface注解上, 然后框架內(nèi)部默認(rèn)會(huì)從SpringContext獲取,獲取不到則手動(dòng)new一個(gè)。

通常一個(gè)Http請求需要經(jīng)歷 構(gòu)建請求參數(shù)、發(fā)送Http請求時(shí),Http響應(yīng)后獲取響應(yīng)內(nèi)容、反序列化Http響應(yīng)內(nèi)容成具體對象。

目前提供了4種鉤子,執(zhí)行順序流程如下:

postBeforeHttpMetadata  (請求發(fā)送前)在發(fā)送請求之前,對Http請求體后置處理
           |
           V
postSendingHttpRequest  (請求發(fā)送時(shí))在Http請求發(fā)送時(shí)處理
           |
           V
postAfterHttpResponseBodyString  (請求響應(yīng)后)對響應(yīng)body文本字符串進(jìn)行后置處理
           |
           V
postAfterHttpResponseBodyResult  (請求響應(yīng)后)對響應(yīng)body反序列化后的結(jié)果進(jìn)行后置處理
           |
           V
postAfterMethodReturnValue  (請求響應(yīng)后)對代理的方法的返回值進(jìn)行后置處理,類似aop的后置處理
  • postBeforeHttpMetadata: 可在發(fā)送http請求之前對請求體進(jìn)行二次處理,比如加簽之類
  • postSendHttpRequest:   Http請求發(fā)送時(shí)會(huì)回調(diào)該方法,可以在該方法執(zhí)行自定義的發(fā)送邏輯或者打印發(fā)送日志
  • postAfterHttpResponseBodyString:  Http請求響應(yīng)后,對響應(yīng)body字符串進(jìn)行進(jìn)行后置處理,比如如果是加密數(shù)據(jù)可以進(jìn)行解密
  • postAfterHttpResponseBodyResult:  Http請求響應(yīng)后,對響應(yīng)body反序列化后的對象進(jìn)行后置處理,比如填充默認(rèn)返回值
  • postAfterMethodReturnValue:   Http請求響應(yīng)后,對代理的方法的返回值進(jìn)行后置處理,類似aop的后置處理
回調(diào)參數(shù)說明:
  • HttpMetadata: 表示此次Http請求的請求體,包含請求url,請求頭、請求方式、請求cookie、請求體、請求參數(shù)等等。
  • HttpApiMethodInvocation: 繼承自MethodInvocation, 表示被代理的方法調(diào)用上下文,可以拿到被代理的類,被代理的方法,被代理的HttpAPI注解、HttpInterface注解等信息

3.7、配置自定義的Http客戶端

默認(rèn)使用的是Okhttp客戶端,如果要重新配置Okhttp客戶端,注入spring的bean即可,如下

@Configuration
public class CusotmConfiguration {

    @Bean
    public OkHttpClient myOHttpClient(){
        return new OkHttpClient.Builder()
                .readTimeout(50, TimeUnit.SECONDS)
                .writeTimeout(50, TimeUnit.SECONDS)
                .connectTimeout(10, TimeUnit.SECONDS)
                .connectionPool(new ConnectionPool(20,10, TimeUnit.MINUTES))
                .build();
    }
}

4、企業(yè)級渠道對接實(shí)戰(zhàn)

案例背景:

假設(shè)現(xiàn)在需要對接一個(gè)某天氣服務(wù)的所有接口,需要在請求cookie帶上一個(gè)token字段和sessionId字段,這兩個(gè)字段的值需要每次接口調(diào)用前先手動(dòng)調(diào)渠道方的一個(gè)特定的接口申請獲取,token值在該接口返回值中返回,sessionId在該接口的響應(yīng)頭中返回。

然后還需要在請求頭上帶上一個(gè)sign簽名字段, 該sign簽名字段生成規(guī)則需要用渠道方提供的公鑰對所有請求體和請求參數(shù)進(jìn)行加簽生成。

然后還需要在每個(gè)接口的查詢參數(shù)上都帶上一個(gè)渠道方分配的客戶端appId。

4.1、在application.yml中配置對接渠道方的信息

channel:
  mtuan:
    # 請求域名
    url: http://127.0.0.1:8999
    # 分配的渠道appId
    appId: UUU-asd-01
    # 分配的公鑰
    publicKey: fajdkf9492304jklfahqq

4.2、自定義該渠道方的HttpAPI注解

假設(shè)現(xiàn)在對接的是某團(tuán),所以自定義注解叫@MTuanHttpApi吧,然后需要在該注解上標(biāo)記@HttpApi注解,并且需要配置processor字段,需要去自定義實(shí)現(xiàn)一個(gè)HttpApiProcessor這個(gè)具體實(shí)現(xiàn)后續(xù)講。

有了這個(gè)注解后就可以自定義該注解與對接渠道方相關(guān)的各種字段配置,當(dāng)然也可以不定義。

注意這里url的字段是使用 @AliasFor(annotation = HttpApi.class),這樣構(gòu)建的HttpMetadata中會(huì)默認(rèn)解析填充要請求體,不標(biāo)記則也可自行處理。

@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@HttpApi(processor = MTuanHttpApiProcessor.class)
public @interface MTuanHttpApi {

    /**
     * 渠道方域名地址
     */
    @AliasFor(annotation = HttpApi.class)
    String url() default "${channel.mtuan.url}";

    /**
     * 渠道方分配的appId
     */
    String appId() default "${channel.mtuan.appId}";
}
@Slf4j
@Component
public class MTuanHttpApiProcessor implements HttpApiProcessor<MTuanHttpApi> {
    
}

注意實(shí)現(xiàn)的HttpApiProcessor泛型要指定為剛才定義的注解@MTuanHttpApi類型,因?yàn)檫@個(gè)HttpApiProcessor配置到它上面,如果需要通用處理可以定義為Annocation類型

4.3、對接接口

有了@MTuanHttpApi注解之后就可以開始對接接口了,比如假設(shè)有兩個(gè)接口要對接。一個(gè)就是前面說的獲取令牌的接口。一個(gè)是獲取天氣情況的接口。

為什么getToken方法返回值是 HttpResponse,這是UniHttp內(nèi)置的原始Http響應(yīng)對象,方便我們?nèi)ツ玫皆糎ttp響應(yīng)體的一些內(nèi)容(比如響應(yīng)狀態(tài)碼、響應(yīng)cookie)。

其中的泛型BaseRsp才是實(shí)際的Http響應(yīng)體反序列化后的內(nèi)容。而getCityWeather方法沒有使用HttpResponse包裝,BaseRsp只是單純Http響應(yīng)體反序列化后的內(nèi)容,這是兩者的區(qū)別。

前面介紹過 HttpResponse,其實(shí)大部份接口是不關(guān)注HttpResponse的可以不用去配置。

@MTuanHttpApi
public interface WeatherApi {
    
    /**
     * 根據(jù)城市名獲取天氣情況
     */
    @GetHttpInterface("/getCityByName")
    BaseRsp<WeatherDTO> getCityWeather(@QueryPar("city") String cityName);

    /**
     *  根據(jù)appId和公鑰獲取令牌
     */
    @PostHttpInterface("/getToken")
    HttpResponse<BaseRsp<TokenDTO>> getToken(@HeaderPar("appId") String appId, @HeaderPar("publicKey")String publicKey);

}

4.4、自定義HttpApiProcessor

在之前我們自定義了一個(gè)@MTuanHttpApi注解上指定了一個(gè)MTuanHttpApiProcessor,接下來我們?nèi)?shí)現(xiàn)他的具體內(nèi)容為了實(shí)現(xiàn)我們案例背景里描述的功能。

@Slf4j
@Component
public class MTuanHttpApiProcessor implements HttpApiProcessor<MTuanHttpApi> {

    /**
     *  渠道方分配的公鑰
     */
    @Value("${channel.mtuan.publicKey}")
    private String publicKey;

    @Value("${channel.mtuan.appId}")
    private String appId;

    @Autowired
    private Environment environment;
    
    @Autowired
    private WeatherApi weatherApi;

    /** 實(shí)現(xiàn)-postBeforeHttpMetadata: 發(fā)送Http請求之前會(huì)回調(diào)該方法,可對Http請求體的內(nèi)容進(jìn)行二次處理
     *
     * @param httpMetadata              原來的請求體
     * @param methodInvocation          被代理的方法
     * @return                          新的請求體
     */
    @Override
    public HttpMetadata postBeforeHttpMetadata(HttpMetadata httpMetadata, HttpApiMethodInvocation<MTuanHttpApi> methodInvocation) {
        /**
         * 在查詢參數(shù)中添加提供的appId字段
         */
        // 獲取MTuanHttpApi注解
        MTuanHttpApi apiAnnotation = methodInvocation.getProxyApiAnnotation();

        // 獲取MTuanHttpApi注解的appId,由于該appId是環(huán)境變量所以我們從environment中解析取出來
        String appIdVar = apiAnnotation.appId();
        appIdVar = environment.resolvePlaceholders(appIdVar);

        // 添加到查詢參數(shù)中
        httpMetadata.putQueryParam("appId",appIdVar);

        /**
         *  生成簽名sign字段
         */
        // 獲取所有查詢參數(shù)
        Map<String, Object> queryParam = httpMetadata.getHttpUrl().getQueryParam();

        // 獲取請求體參數(shù)
        HttpBody body = httpMetadata.getBody();

        // 生成簽名
        String signKey = createSignKey(queryParam,body);

        // 將簽名添加到請求頭中
        httpMetadata.putHeader("sign",signKey);

        return httpMetadata;
    }

    private String createSignKey(Map<String, Object> queryParam, HttpBody body) {
        // todo 偽代碼
        // 1、將查詢參數(shù)拼接成字符串
        String queryParamString = queryParam.entrySet()
                .stream().map(e -> e.getKey() + "="+e.getValue())
                .collect(Collectors.joining(";"));

        // 2、將請求體參數(shù)拼接成字符串
        String bodyString = "";
        if (body instanceof HttpBodyJSON){
            // application/json  類型的請求體
            bodyString = body.toStringBody();
        }else if (body instanceof HttpBodyFormData){
            // application/x-www-form-urlencoded 類型的請求體
            bodyString = body.toStringBody();
        }else if (body instanceof HttpBodyMultipart){
            // multipart/form-data 類型的請求體
            bodyString =  body.toStringBody();
        }

        // 使用公鑰publicKey 加密拼接起來
        String sign = publicKey + queryParamString + bodyString;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] digest = md.digest(sign.getBytes());
            return new String(digest);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     *  實(shí)現(xiàn)-postBeforeHttpMetadata: 發(fā)送Http請求時(shí),可定義發(fā)送請求的行為 或者打印請求和響應(yīng)日志。
     */
    @Override
    public HttpResponse<?> postSendHttpRequest(HttpSender httpSender, HttpMetadata httpMetadata) {
        //  忽略 weatherApi.getToken的方法回調(diào),否則該方法也會(huì)回調(diào)此方法會(huì)遞歸死循環(huán)。 或者該接口指定自定義的HttpApiProcessor重寫postSendingHttpRequest
        Method getTokenMethod = ReflectionUtils.findMethod(WeatherServiceApi.class, "getToken",String.class,String.class);
        if (getTokenMethod == null || getTokenMethod.equals(methodInvocation.getMethod())){
            return httpSender.sendHttpRequest(httpMetadata);
        }
        
        // 1、動(dòng)態(tài)獲取token和sessionId
        HttpResponse<String> httpResponse = weatherApi.getToken(appId, publicKey);

        // 從響應(yīng)體獲取令牌token
        String token = httpResponse.getBodyResult();
        // 從響應(yīng)頭中獲取sessionId
        String sessionId = httpResponse.getHeader("sessionId");

        // 把這兩個(gè)值放到此次的請求cookie中
        httpMetadata.addCookie(new Cookie("token",token));
        httpMetadata.addCookie(new Cookie("sessionId",sessionId));
        
        log.info("開始發(fā)送Http請求 請求接口:{} 請求體:{}",httpMetadata.getHttpUrl().toUrl(),httpMetadata.toHttpProtocol());

        // 使用框架內(nèi)置工具實(shí)現(xiàn)發(fā)送請求
        HttpResponse<?> rsp =  httpSender.sendHttpRequest(httpMetadata);

        log.info("開始發(fā)送Http請求 響應(yīng)結(jié)果:{}",rsp.toHttpProtocol());
        
        return rsp;
    }

    /**
     *  實(shí)現(xiàn)-postAfterHttpResponseBodyResult: 反序列化后Http響應(yīng)體的內(nèi)容后回調(diào),可對該結(jié)果進(jìn)行二次處理返回
     * @param bodyResult                     Http響應(yīng)體反序列化后的結(jié)果
     * @param rsp                            原始Http響應(yīng)對象
     * @param method                         被代理的方法
     * @param httpMetadata                   Http請求體
     */
    @Override
    public Object postAfterHttpResponseBodyResult(Object bodyResult, HttpResponse<?> rsp, Method method, HttpMetadata httpMetadata) {
        if (bodyResult instanceof BaseRsp){
            BaseRsp baseRsp = (BaseRsp) bodyResult;
            // 設(shè)置
            baseRsp.setCode(999);
        }
        
        return bodyResult;
    }
}

上面我們分別重寫了postBeforeHttpMetadata、postSendHttpRequest、postAfterHttpResponseBodyResult三個(gè)生命周期的鉤子方法去完成我們的需求,在發(fā)送請求前對請求體進(jìn)行加簽、在發(fā)送請求時(shí)動(dòng)態(tài)獲取令牌重新構(gòu)建請求體和打印日志、在發(fā)送請求后給響應(yīng)對象設(shè)置code為999。

責(zé)任編輯:武曉燕 來源: 一安未來
相關(guān)推薦

2020-12-17 07:59:46

聲明式代碼命令式代碼代碼

2009-06-22 09:01:57

Spring聲明式事務(wù)

2022-06-21 08:12:17

K8sAPI對象Kubernetes

2009-09-02 14:18:08

C#聲明COM接口

2024-11-13 19:03:14

2013-06-27 09:31:37

聲明式編程命令式編程編程

2009-09-02 14:26:50

C#對接口成員訪問

2020-09-04 06:27:22

編碼命令式聲明式代碼

2024-02-28 08:37:28

Lambda表達(dá)式Java函數(shù)式接口

2021-09-06 13:42:14

Spring聲明式事務(wù)

2014-10-30 18:44:45

暢捷通T+平臺(tái)

2012-03-15 11:21:19

Java

2019-07-04 15:13:16

分布式緩存Redis

2010-05-26 13:56:59

Tomcat 7Servlet 3.0

2022-08-23 16:07:02

ArkUI鴻蒙

2009-09-01 10:49:28

C#具有隱式類型聲明

2023-08-30 13:22:00

測試框架工具

2025-01-16 08:45:48

2009-07-07 17:32:31

HTTP Servle

2021-01-08 07:48:19

Pipeline腳本式聲明式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號