UniHttp中HttpApiProcessor生命周期钩子介绍以及公共参数填充-以百度天气接口为例
目录
引言
一、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框架,提升网络请求处理的灵活性和可维护性,从而构建出更加健壮和易于扩展的应用程序。行文仓促,难免有许多不足之处,如有不足,在此恳请各位专家博主在评论区不吝留言指出,不胜感激。