Forest声明式HTTP客户端框架
背景说明
项目间互调RestFul接口时,需要写一堆代码,就算封装工具后也不轻松,尤其涉及到不同项目的权限问题,不同项目对外的权限认证不统一,有的是token认证,有的是密码认证,还有OAuth2认证,不管是调用还是处理返回数据,都会消耗大量时间精力,无意中发现开源项目Forest,很是惊喜,通过简单的注解并声明相关变量,即可调用外部接口,而且返回值可以根据具体情况进行自动封装,大大节约了开发成本。项目介绍:
Forest是一个高层的、极简的声明式HTTP调用API框架。相比于直接使用Httpclient您不再用写一大堆重复的代码了,而是像调用本地方法一样去发送HTTP请求。
Forest能够帮助您使用更简单的方式编写Java的HTTP客户端,快速接入第三方RESTful接口Forest特性以Httpclient和OkHttp为后端框架通过调用本地方法的方式去发送Http请求,实现了业务逻辑与Http协议之间的解耦因为针对第三方接口,所以不需要依赖SpringCloud和任何注册中心支持所有请求方法:GET,HEAD,OPTIONS,TRACE,POST,DELETE,PUT,PATCH支持文件上传和下载支持灵活的模板表达方式支持拦截器处理请求的各个生命周期支持自定义注解支持OAuth2验证支持过滤器来过滤传入的数据基于注解、配置化的方式定义Http请求支持Spring和Springboot集成JSON格式数据序列化和反序列化XML格式数据序列化和反序列化Protobuf格式数据序列化和反序列化JSON、XML或其他类型转换器可以随意扩展和替换支持JSON转换框架:Fastjson,Jackson,Gson支持JAXB形式的XML转换可以通过OnSuccess和OnError接口参数实现请求结果的回调配置简单,一般只需要Request一个注解就能完成绝大多数请求的定义支持异步请求调用极速开始
以下例子基于SpringBoot第一步:添加Maven依赖
直接添加以下maven依赖即可dependencygroupIdcom。dtflys。forestgroupIdforestspringbootstarterartifactIdversion1。5。17versiondependency第二步:创建一个interface
就以高德地图API为栗子吧packagecom。yoursite。client;importcom。dtflys。forest。annotation。Request;importcom。dtflys。forest。annotation。DataParam;publicinterfaceAmapClient{聪明的你一定看出来了Get注解代表该方法专做GET请求在url中的{0}代表引用第一个参数,{1}引用第二个参数Get(http:ditu。amap。comserviceregeo?longitude{0}latitude{1})MapgetLocation(Stringlongitude,Stringlatitude);}第三步:扫描接口
在SpringBoot的配置类或者启动类上加上ForestScan注解,并在basePackages属性里填上远程接口的所在的包名SpringBootApplicationConfigurationForestScan(basePackagescom。yoursite。client)publicclassMyApplication{publicstaticvoidmain(String〔〕args){SpringApplication。run(MyApplication。class,args);}}第四步:调用接口
OK,我们可以愉快地调用接口了注入接口实例AutowiredprivateAmapClientamapClient;。。。调用接口MapresultamapClient。getLocation(121。475078,31。223577);System。out。println(result);发送JSON数据将对象参数解析为JSON字符串,并放在请求的Body进行传输Post(register)StringregisterUser(JSONBodyMyUseruser);将Map类型参数解析为JSON字符串,并放在请求的Body进行传输Post(testjson)StringpostJsonMap(JSONBodyMapmapObj);直接传入一个JSON字符串,并放在请求的Body进行传输Post(testjson)StringpostJsonText(JSONBodyStringjsonText);发送XML数据将一个通过JAXB注解修饰过的类型对象解析为XML字符串并放在请求的Body进行传输Post(message)StringsendXmlMessage(XMLBodyMyMessagemessage);直接传入一个XML字符串,并放在请求的Body进行传输Post(testxml)StringpostXmlBodyString(XMLBodyStringxml);发送Protobuf数据ProtobufProto。MyMessage为Protobuf生成的数据类将Protobuf生成的数据对象转换为Protobuf格式的字节流并放在请求的Body进行传输注:需要引入googleprotobuf依赖Post(urlmessage,contentTypeapplicationoctetstream)StringsendProtobufMessage(ProtobufBodyProtobufProto。MyMessagemessage);文件上传用DataFile注解修饰要上传的参数对象OnProgress参数为监听上传进度的回调函数Post(upload)Mapupload(DataFile(file)StringfilePath,OnProgressonProgress);
可以用一个方法加Lambda同时解决文件上传和上传的进度监听MapresultmyClient。upload(D:TestUploadxxx。jpg,progress{System。out。println(progress:Math。round(progress。getRate()100));已上传百分比if(progress。isDone()){是否上传完成System。out。println(UploadCompleted!);}});多文件批量上传上传Map包装的文件列表,其中{key}代表Map中每一次迭代中的键值Post(upload)ForestRequestMapuploadByteArrayMap(DataFile(valuefile,fileName{key})MapString,byte〔〕byteArrayMap);上传List包装的文件列表,其中{index}代表每次迭代List的循环计数(从零开始计)Post(upload)ForestRequestMapuploadByteArrayList(DataFile(valuefile,fileNametestimg{index}。jpg)Listbyte〔〕byteArrayList);文件下载
下载文件也是同样的简单在方法上加上DownloadFile注解dir属性表示文件下载到哪个目录OnProgress参数为监听上传进度的回调函数{0}代表引用第一个参数Get(http:localhost:8080imagesxxx。jpg)DownloadFile(dir{0})FiledownloadFile(Stringdir,OnProgressonProgress);
调用下载接口以及监听下载进度的代码如下:FilefilemyClient。downloadFile(D:TestDownload,progress{System。out。println(progress:Math。round(progress。getRate()100));已下载百分比if(progress。isDone()){是否下载完成System。out。println(DownloadCompleted!);}});基本签名验证Post(hellouser?username{username})BasicAuth(username{username},passwordbar)Stringsend(DataVariable(username)Stringusername);OAuth2。0OAuth2(tokenUriauthoauthtoken,clientIdpassword,clientSecretxxxxxyyyyyzzzzz,grantTypeOAuth2。GrantType。PASSWORD,scopeany,usernameroot,passwordxxxxxx)Get(testdata)StringgetData();自定义注解
Forest允许您根据需要自行定义注解,不但让您可以简单优雅得解决各种需求,而且极大得扩展了Forest的能力。定义一个注解用Forest自定义注解实现一个自定义的签名加密注解凡用此接口修饰的方法或接口,其对应的所有请求都会执行自定义的签名加密过程而自定义的签名加密过程,由这里的MethodLifeCycle注解指定的生命周期类进行处理可以将此注解用在接口类和方法上Documented重点:MethodLifeCycle注解指定该注解的生命周期类MethodLifeCycle(MyAuthLifeCycle。class)RequestAttributesRetention(RetentionPolicy。RUNTIME)指定该注解可用于类上或方法上Target({ElementType。TYPE,ElementType。METHOD})publicinterfaceMyAuth{自定义注解的属性:用户名所有自定注解的属性可以在生命周期类中被获取到Stringusername();自定义注解的属性:密码所有自定注解的属性可以在生命周期类中被获取到Stringpassword();}定义注解生命周期类MyAuthLifeCycle为自定义的MyAuth注解的生命周期类因为MyAuth是针对每个请求方法的,所以它实现自MethodAnnotationLifeCycle接口MethodAnnotationLifeCycle接口带有泛型参数第一个泛型参数是该生命周期类绑定的注解类型第二个泛型参数为请求方法返回的数据类型,为了尽可能适应多的不同方法的返回类型,这里使用ObjectpublicclassMyAuthLifeCycleimplementsMethodAnnotationLifeCycleMyAuth,Object{当方法调用时调用此方法,此时还没有执行请求发送次方法可以获得请求对应的方法调用信息,以及动态传入的方法调用参数列表OverridepublicvoidonInvokeMethod(ForestRequestrequest,ForestMethodmethod,Object〔〕args){System。out。println(InvokeMethodmethod。getMethodName()Arguments:args);}发送请求前执行此方法,同拦截器中的一样OverridepublicbooleanbeforeExecute(ForestRequestrequest){通过getAttribute方法获取自定义注解中的属性值getAttribute第一个参数为request对象,第二个参数为自定义注解中的属性名Stringusername(String)getAttribute(request,username);Stringpassword(String)getAttribute(request,password);使用Base64进行加密StringbasicMyAuthBase64Utils。encode({username:password});调用addHeader方法将加密结构加到请求头MyAuthorization中request。addHeader(MyAuthorization,basic);returntrue;}此方法在请求方法初始化的时候被调用OverridepublicvoidonMethodInitialized(ForestMethodmethod,BasicAuthannotation){System。out。println(Methodmethod。getMethodName()Initialized,Arguments:args);}}使用自定义的注解在请求接口上加上自定义的MyAuth注解注解的参数可以是字符串模板,通过方法调用的时候动态传入也可以是写死的字符串Get(hellouser?username{username})MyAuth(username{username},passwordbar)Stringsend(DataVariable(username)Stringusername);源码地址
关注回复forest获取源码地址。