目錄
引言
一、UniHttp與HttpApiProcessor簡介
1、生命周期鉤子的重要性
2、公共參數填充的需求
3、生命周期鉤子相關介紹
二、HttpApiProcessor的實際應用
1、在Yml中定義相關參數
2、自定義HttpAPI注解?
3、對接接口的定義
4、HttpApiProcessor的具體實現
5、實際調用
三、結語
引言
????????在當今的軟件開發領域,網絡請求幾乎是每一個應用程序不可或缺的一部分。無論是獲取遠程服務器的數據,還是向服務器發送請求以更新信息,高效且靈活地處理HTTP請求對于提升應用性能和用戶體驗至關重要。UniHttp作為一個強大的HTTP客戶端框架,提供了豐富的功能來簡化網絡請求的處理。其中,HttpApiProcessor
及其生命周期鉤子是UniHttp框架中的一大亮點,它們為開發者提供了在請求發送前后以及響應處理等關鍵時機進行干預的能力。
????????本文將以百度天氣接口為例,深入介紹HttpApiProcessor
的生命周期鉤子以及如何利用這些鉤子來填充公共參數,幫助開發者更好地理解和運用UniHttp框架,以實現更加高效和靈活的網絡請求處理。
一、UniHttp與HttpApiProcessor簡介
????????UniHttp是一個基于Java的HTTP客戶端框架,旨在為開發者提供簡單易用、功能強大的HTTP請求處理能力。它支持同步和異步請求,能夠自動處理JSON、XML等數據的序列化與反序列化,并且可以輕松集成到各種Java應用中,如Spring Boot、Spring Cloud等微服務架構中。HttpApiProcessor
是UniHttp中的一個核心組件,它負責處理HTTP請求的整個生命周期,包括請求的構建、發送、響應的接收以及結果的解析等。通過使用HttpApiProcessor
,開發者可以更加專注于業務邏輯的實現,而不必過多地關注底層的網絡通信細節。
1、生命周期鉤子的重要性
????????在HTTP請求的處理過程中,往往需要在不同的階段執行一些特定的操作。例如,在請求發送前,可能需要添加一些公共的請求頭,如API密鑰、用戶代理等;在請求發送后,可能需要對響應進行一些預處理,如檢查響應狀態碼、解析響應數據等。如果每個請求都手動編寫這些邏輯,不僅代碼冗余,而且難以維護。而生命周期鉤子提供了一種優雅的解決方案,允許開發者在請求處理的不同階段插入自定義的邏輯,從而實現代碼的復用和邏輯的集中管理。通過合理利用生命周期鉤子,可以大大提高開發效率,降低代碼的復雜度,并且使得整個網絡請求的處理流程更加清晰和可控。
2、公共參數填充的需求
????????在調用許多第三方API時,通常需要傳遞一些公共參數,這些參數對于每個請求都是必需的。以百度天氣接口為例,可能需要傳遞API密鑰、城市代碼等參數。如果每個請求方法中都手動添加這些參數,不僅繁瑣,而且容易出錯。因此,將這些公共參數的填充邏輯集中管理顯得尤為重要。通過HttpApiProcessor
的生命周期鉤子,可以在請求發送前自動填充這些公共參數,確保每個請求都包含必要的信息,從而簡化請求方法的編寫,提高代碼的可維護性。
3、生命周期鉤子相關介紹
????????HttpApiProcessor表示是一個發送和響應和反序列化一個Http請求接口的各種生命周期鉤子,開發者可以在里面自定義編寫各種對接邏輯。 支持配置到 @HttpApi注解 或者 具體的@HttpInterface注解上, 在調用時就會拿到這個對象去執行自定義的鉤子(這個對象如果Spring容器存在則從容器拿,如果不存在則會手動new一個請確保有無參構造)。目前提供了8種鉤子,執行順序流程如下:
postBeforeHttpRequest (請求發送前)在發送請求之前,對Http請求進行二次處理|VpostBeforeSendHttpRequest (請求發送前) 請求真正發送前回調|VpostSendingHttpRequest (請求發送時)在同步Http請求發送時回調處理 |VpostAfterHttpResponse (請求之后)發送請求后,不管成功還是失敗都會回調此方法|VpostAfterHttpResponseBodyString (請求響應后)對響應body文本字符串進行后置處理|VpostAfterHttpResponseBodyStringDeserialize (請求響應后) 對響應body字符串自定義反序列化后置處理|VpostAfterHttpResponseBodyResult (請求響應后)對響應body反序列化后的結果進行后置處理|VpostAfterMethodReturnValue (請求響應后)對代理的方法的返回值進行后置處理,類似aop的后置處理
1、
postBeforeHttpRequest:
可在發送http請求之前對請求體進行二次處理,比如加簽之類2、
postBeforeSendHttpRequest:
?可在真正發送請求前進行回調處理,比如修改請求信息、執行自定義邏輯3、
postSendHttpRequest:
?同步Http請求發送時會回調該方法,可以在該方法執行自定義的發送邏輯, 注意如果是異步請求不會回調此方法。4、
postAfterHttpResponse:
?發送請求后,不管成功還是失敗都會回調此方法, 如果失敗會返回異常信息,沒有異常信息則為成功. 建議在此處打印請求的日志、耗時、和執行情況5、
postAfterHttpResponseBodyString
: Http請求響應后,對響應body字符串進行進行后置處理,比如如果是加密數據可以進行解密,修改返回的json結構,移除無用字段。6、
postAfterHttpResponseBodyStringDeserialize
: 對響應body字符串進行反序列化, 默認框架自動處理, 也可覆蓋實現自定義邏輯7、
postAfterHttpResponseBodyResult:
?Http請求響應后,對響應body反序列化后的對象進行后置處理,比如填充默認值之類8、
postAfterMethodReturnValue:
?Http請求響應后,對代理的方法的返回值進行后置處理,類似aop的后置處理方法參數介紹:
- UniHttpRequest: 表示此次Http請求的請求體,包含請求url,請求頭、請求方式、請求cookie、請求體、請求參數等等。
- UniHttpResponse: 表示此次Http請求的響應信息
- HttpApiMethodInvocation: 繼承自MethodInvocation, 表示被代理的方法調用上下文,可以拿到被代理的類,被代理的方法,被代理的HttpAPI注解、HttpInterface注解等信息
?????????以上信息是來源于UniHttp官方網站的相關信息介紹,想了解更詳細的信息的可以自行去搜索相關信息。
二、HttpApiProcessor的實際應用
????????百度天氣接口是一個提供天氣信息查詢服務的API,開發者可以通過調用該接口獲取指定城市的天氣情況。在使用該接口時,需要傳遞一些公共參數,如API密鑰、城市代碼等。通過UniHttp的HttpApiProcessor
及其生命周期鉤子,可以方便地實現這些公共參數的填充。例如,在請求發送前,可以通過beforeRequest
鉤子自動將API密鑰和城市代碼添加到請求參數中;在響應接收后,可以通過afterResponse
鉤子對返回的天氣數據進行解析和封裝,使其更易于在應用中使用。這種集中管理公共參數的方式,不僅使得代碼更加簡潔,而且當API密鑰或城市代碼發生變化時,只需在鉤子邏輯中進行修改,無需在每個請求方法中逐一更新,大大提高了代碼的可維護性和靈活性。接下來我們跟著UniHttp的官方文檔來進行逐一的學習,仿照著改造成符合我們的實現。
1、在Yml中定義相關參數
????????在這里,我們將百度的對接地址和公共的ak進行單獨的設置,在常規的SpringBoot項目中,一般都會有yml文件,因為我們在yml中定義相關的配置信息,比如需要代理的url地址和公共的可以,當然,你也可以根據實際的需求來進行調整,增加一些符合自己業務需要的字段。這里只定義令人url和ak信息。關鍵代碼如下:
#unihttp對接接口配置
channel:baidu:#天氣查詢接口weather:# 請求域名url: https://api.map.baidu.com/weather/v1# 分配的akak: ak_value
2、自定義HttpAPI注解?
? ? ? ? 這里我們需要創建一個注解對象,假設叫@BaiduHttpApi吧,然后需要在該注解上標記@HttpApi注解,并且需要配置processor字段,需要去自定義實現一個HttpApiProcessor這個具體實現后續講。 有了這個注解后就可以自定義該注解與對接渠道方相關的各種字段配置,當然也可以不定義。 注意這里url的字段是使用 @AliasFor(annotation = HttpApi.class), 這樣構建的UniHttpRequest中會默認解析填充要請求體,不標記則也可自行處理。關鍵代碼如下:
package com.yelang.project.thridinterface;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import com.burukeyou.uniapi.http.annotation.HttpApi;
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@HttpApi(processor = BaiduHttpApiProcessor.class)
public @interface BaiduHttpApi {/*** -渠道方域名地址*/@AliasFor(annotation = HttpApi.class)String url() default "${channel.baidu.weather.url}";/*** -申請分配的ak*/String ak() default "${channel.baidu.weather.ak}";
}
????????這里請注意在注解類中,url其實也是httpApi類的一個屬性,源代碼的設置如下:
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface HttpApi {/*** Configure global HTTP request URL* Support taking values from environmental variables, such as ${xx.url}*/String url() default "";
}
?????????在前面的注解類定義中,我們已經設置了一個BaiduHttpApiProcessor,為了讓代碼編譯通過,因此先來創建一個空的類,里面可以沒有任何實現方法,關鍵代碼如下:
package com.yelang.project.thridinterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import com.burukeyou.uniapi.http.core.channel.HttpApiMethodInvocation;
import com.burukeyou.uniapi.http.core.channel.HttpSender;
import com.burukeyou.uniapi.http.core.request.UniHttpRequest;
import com.burukeyou.uniapi.http.core.response.UniHttpResponse;
import com.burukeyou.uniapi.http.extension.processor.HttpApiProcessor;
@Component
public class BaiduHttpApiProcessor implements HttpApiProcessor<BaiduHttpApi>{
}
????????關于這個Processor的具體實現在后續的介紹中會說明。
3、對接接口的定義
????????接下來對對接的接口的進行定義,由于我們期望使用公共參數的方式來定義對接接口以及設置對應的公共參數,因此與之前的定義方式不一樣的是,在設置了公共配置之后,對于url和ak不用在每個接口中進行統一傳遞,如果有不一樣的,請大家按照管網的要求進行調整。下面是我演示的簡單對接j接口:
package com.yelang.project.thridinterface;
import com.burukeyou.uniapi.http.annotation.param.QueryPar;
import com.burukeyou.uniapi.http.annotation.request.GetHttpInterface;
import com.burukeyou.uniapi.http.core.response.HttpResponse;
@BaiduHttpApi()
public interface BaiduWeatherApiServcie {@GetHttpInterface("/")public HttpResponse<String> getWeather(@QueryPar("district_id") String district_id,@QueryPar("data_type") String data_type);
}
????????在這里需要注意的是,在接口的聲明處,配置的注解要替換成前面定義過的。?
4、HttpApiProcessor的具體實現
? ? ? ? 本節重點描述BaiduHttpApiProcessor的具體實現,與常規的接口實現一樣,使用java語言進行調用。這里我們只涉及三個簡單的狗子鉤子函數,其它的暫時用不上暫且用不上。
@Component
public class BaiduHttpApiProcessor implements HttpApiProcessor<BaiduHttpApi>{/*** 渠道方分配的key,可以直接配置讀取*/@Value("${channel.baidu.weather.ak}")private String ak;@Autowiredprivate Environment environment;/** 實現-postBeforeHttpMetadata: 發送Http請求之前會回調該方法,可對Http請求體的內容進行二次處理** @param uniHttpRequest 原來的請求體* @param methodInvocation 被代理的方法* @return 新的請求體*/@Overridepublic UniHttpRequest postBeforeHttpRequest(UniHttpRequest uniHttpRequest,HttpApiMethodInvocation<BaiduHttpApi> methodInvocation) {// 獲取BaiduHttpApi注解BaiduHttpApi apiAnnotation = methodInvocation.getProxyApiAnnotation();System.out.println(apiAnnotation.url());System.out.println(apiAnnotation.ak());// 獲取BaiduHttpApi注解的appId,由于該appId是環境變量所以我們從environment中解析取出來String akVar = apiAnnotation.ak();System.out.println("打印相關參數:");akVar = environment.resolvePlaceholders(akVar);System.out.println("從環境對象中獲取akVar===>" + akVar);System.out.println("本類中的注解獲取AK===>" + ak);System.out.println("系統獲取url: " + environment.resolvePlaceholders(apiAnnotation.url()));// 添加到查詢參數中,必須要添加,可以從uniHttpRequest.putQueryParam("ak",akVar);return uniHttpRequest;}@Overridepublic void postBeforeSendHttpRequest(UniHttpRequest uniHttpRequest, HttpSender httpSender,HttpApiMethodInvocation<BaiduHttpApi> methodInvocation) {HttpApiProcessor.super.postBeforeSendHttpRequest(uniHttpRequest, httpSender, methodInvocation);}@Overridepublic UniHttpResponse postSendingHttpRequest(HttpSender httpSender, UniHttpRequest uniHttpRequest,HttpApiMethodInvocation<BaiduHttpApi> methodInvocation) {return HttpApiProcessor.super.postSendingHttpRequest(httpSender, uniHttpRequest, methodInvocation);}/*** 實現-postAfterHttpResponseBodyResult: 反序列化后Http響應體的內容后回調,可對該結果進行二次處理返回* @param bodyResult Http響應體反序列化后的結果* @param rsp 原始Http響應對象* @param methodInvocation 被代理的方法* @return*/@Overridepublic Object postAfterHttpResponseBodyResult(Object bodyResult, UniHttpResponse rsp,HttpApiMethodInvocation<BaiduHttpApi> methodInvocation) {System.out.println("bodyResult===>"+bodyResult);System.out.println("在這里可以做一些時候的處理,比如格式化和解析等,做成插件似的......");System.out.println("rsp===>" + rsp);/*if (bodyResult instanceof BaseRsp){BaseRsp baseRsp = (BaseRsp) bodyResult;// 設置baseRsp.setCode(999);}*/return bodyResult;}
}
????????這里主要就是在發送請求前設置公共參數,在接口響應后對返回值進行序列化等轉換。通過這樣的實例基本就符合了我們數據處理要求。上面我們分別重寫了postBeforeHttpRequest、postSendHttpRequest、postAfterHttpResponseBodyResult三個生命周期的鉤子方法去完成我們的需求。
5、實際調用
????????最后基于自來定義的ApiProcessor對象來進行相應的處理,最后我們使用main函數來模擬程序的調用,調用代碼和實現邏輯如下:
package com.yelang.project.unihttp;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.burukeyou.uniapi.http.core.response.HttpResponse;
import com.yelang.project.thridinterface.BaiduWeatherApiServcie;
@SpringBootTest
@RunWith(SpringRunner.class)
public class UniHttpProcessorCase {@Autowiredprivate BaiduWeatherApiServcie baiduWeatherApiService;@Testpublic void getWeather4Baidu() {String district_id = "431226";//表示String data_type = "all";HttpResponse<String> result = baiduWeatherApiService.getWeather(district_id, data_type);System.out.println(result.getBodyResult());}
}
????????在Eclipse中運行上述程序后就可以看到有以下的數據:
????????可以在控制臺中看到相應的接口返回,說明集成成功。通過這個例子可以看到,通過統一設置能極大的簡化。
三、結語
????????通過本文的介紹,讀者可以深入了解UniHttp中HttpApiProcessor
的生命周期鉤子以及如何利用這些鉤子來填充公共參數。以百度天氣接口為例,展示了生命周期鉤子在實際開發中的應用場景和優勢。掌握這些知識,將有助于開發者更加高效地使用UniHttp框架,提升網絡請求處理的靈活性和可維護性,從而構建出更加健壯和易于擴展的應用程序。行文倉促,難免有許多不足之處,如有不足,在此懇請各位專家博主在評論區不吝留言指出,不勝感激。