Forest - 轻量级HTTP客户端框架

Forest - 轻量级HTTP客户端框架

参考:再见,HttpClient!再见,Okhttp!

我觉得对于尤其是做对接第三方api的开发同学来说,这款开源框架能帮你提高很多效率。

Forest 底层封装了2种不同的http框架:Apache httpClient和OKhttp。所以这个开源框架并没有对底层实现进行重复造轮子,而是在易用性上面下足了功夫。

我用Forest最终完成了和多个服务商api对接的项目,这些风格迥异的API,我仅用了1个小时时间就把他们转化为了本地方法。然后项目顺利上线。

Forest作为一款更加高层的http框架,其实你并不需要写很多代码,大多数时候,你仅通过一些配置就能完成http的本地化调用。而这个框架所能覆盖的面,却非常之广,满足你绝大多数的http调用请求。

Forest有以下特点:

以Httpclient和OkHttp为后端框架
通过调用本地方法的方式去发送Http请求, 实现了业务逻辑与Http协议之间的解耦
相比Feign更轻量,不依赖Spring Cloud和任何注册中心
支持所有请求方法:GET, HEAD, OPTIONS, TRACE, POST, DELETE, PUT, PATCH
支持灵活的模板表达式
支持过滤器来过滤传入的数据
基于注解、配置化的方式定义Http请求
支持Spring和Springboot集成
实现JSON和XML的序列化和反序列化
支持JSON转换框架: Fastjson, Jackson, Gson
支持JAXB形式的XML转换
支持SSL的单向和双向加密
支持http连接池的设定
可以通过OnSuccess和OnError接口参数实现请求结果的回调
配置简单,一般只需要@Request一个注解就能完成绝大多数请求的定义
支持异步请求调用

依赖:

<dependency>

    <groupId>com.dtflys.forest</groupId>
    <artifactId>forest-spring-boot-starter</artifactId>
    <version>1.5.2-BETA2</version>
</dependency>

启动类加上扫描

@ForestScan(basePackages = "com.yymt.controller.app")

测试调用

package com.yymt.controller.app;

import com.yymt.common.dto.request.BuildingAddReqDTO;
import com.yymt.common.utils.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.util.Map;

/**
 * @Author:xielin
 * @Description:
 * @Date:2021/9/2 19:41
 * @Version: 1.0
 */
@RestController
@Api(tags = "test-testForestClient")
public class TestController {
    // 注入接口实例
    @Autowired
    private TestForestClient testForestClient;

    /**
     * 保存
     */
    @ApiOperation("getLocation")
    @PostMapping("/getLocation")
    public R save(){
        // 调用接口
        Map result = testForestClient.getLocation("121.475078", "31.223577");
        System.out.println(result);
        return R.ok(result);
    }

    /**
     *
     */
    @ApiOperation("uploadtest")
    @PostMapping("/uploadtest")
    public R upload(){
        Map result = testForestClient.upload("C:\\Users\\xielin\\Desktop\\一附院视频云智能认知项目架构流程图.png", progress -> {
            System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%");  // 已上传百分比
            if (progress.isDone()) {   // 是否上传完成
                System.out.println("--------   Upload Completed!   --------");
            }
        });
        System.out.println("end");
        return R.ok(result);
    }


}

封装的接口

package com.yymt.controller.app;

import com.dtflys.forest.annotation.*;
import com.dtflys.forest.callback.OnProgress;
import com.dtflys.forest.extensions.BasicAuth;
import com.dtflys.forest.extensions.DownloadFile;
import com.dtflys.forest.http.ForestRequest;

import java.io.File;
import java.util.List;
import java.util.Map;

/**
 * @Author:xielin
 * @Description:
 * @Date:2021/9/2 19:40
 * @Version: 1.0
 */
public interface TestForestClient {

    /**
     * 聪明的你一定看出来了@Get注解代表该方法专做GET请求
     * 在url中的${0}代表引用第一个参数,${1}引用第二个参数
     * Map result = myClient.upload("D:\\TestUpload\\xxx.jpg", progress -> {
     *     System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%");  // 已上传百分比
     *     if (progress.isDone()) {   // 是否上传完成
     *         System.out.println("--------   Upload Completed!   --------");
     *     }
     * });
     */
    @Get("http://ditu.amap.com/service/regeo?longitude=${0}&latitude=${1}")
    Map getLocation(String longitude, String latitude);

    /**
     * 用@DataFile注解修饰要上传的参数对象
     * OnProgress参数为监听上传进度的回调函数
     */
    @Post("http://localhost:1031/dataService/file/upload?file=${0}")
    Map upload(@DataFile("file") String filePath, OnProgress onProgress);


    // 发送JSON数据
//    /**
//     * 将对象参数解析为JSON字符串,并放在请求的Body进行传输
//     */
//    @Post("/register")
//    String registerUser(@JSONBody MyUser user);

    /**
     * 将Map类型参数解析为JSON字符串,并放在请求的Body进行传输
     */
    @Post("/test/json")
    String postJsonMap(@JSONBody Map mapObj);

    /**
     * 直接传入一个JSON字符串,并放在请求的Body进行传输
     */
    @Post("/test/json")
    String postJsonText(@JSONBody String jsonText);

    /**
     * 将一个通过JAXB注解修饰过的类型对象解析为XML字符串
     * 并放在请求的Body进行传输
     */
//    @Post("/message")
//    String sendXmlMessage(@XMLBody MyMessage message);

    /**
     * 直接传入一个XML字符串,并放在请求的Body进行传输
     */
    @Post("/test/xml")
    String postXmlBodyString(@XMLBody String xml);

    /**
     * 上传Map包装的文件列表,其中 ${_key} 代表Map中每一次迭代中的键值
     */
    @Post("/upload")
    ForestRequest<Map> uploadByteArrayMap(@DataFile(value = "file", fileName = "${_key}") Map<String, byte[]> byteArrayMap);

    /**
     * 上传List包装的文件列表,其中 ${_index} 代表每次迭代List的循环计数(从零开始计)
     */
    @Post("/upload")
    ForestRequest<Map> uploadByteArrayList(@DataFile(value = "file", fileName = "test-img-${_index}.jpg") List<byte[]> byteArrayList);

    /**
     * 在方法上加上@DownloadFile注解
     * dir属性表示文件下载到哪个目录
     * OnProgress参数为监听上传进度的回调函数
     * ${0}代表引用第一个参数
     *
     * 调用:
     * File file = myClient.downloadFile("D:\\TestDownload", progress -> {
     *     System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%");  // 已下载百分比
     *     if (progress.isDone()) {   // 是否下载完成
     *         System.out.println("--------   Download Completed!   --------");
     *     }
     * });
     */
    @Get("http://localhost:8080/images/xxx.jpg")
    @DownloadFile(dir = "${0}")
    File downloadFile(String dir, OnProgress onProgress);

    @Post("/hello/user?username=${username}")
    @BasicAuth(username = "${username}", password = "bar")
    String send(@DataVariable("username") String username);
}

上传图片测试结果:
在这里插入图片描述

@Request(
    url = "${0}/send?un=${1}&pw=${2}&ph=${3}&ct=${4}",
    type = "get",
    dataType = "json"
)
public Map send(
    String base,
    String userName,
    String password,
    String phone,
    String content
);
@Request(
    url = "${base}/send?un=${un}&pw=${pw}&ph=${3}&ct=${ct}",
    type = "get",
    dataType = "json"
)
public Map send(
    @DataVariable("base") String base,
    @DataVariable("un") String userName,
    @DataVariable("pw") String password,
    @DataVariable("ph") String phone,
    @DataVariable("ct") String content
@Request(
    url = "${base}/send",
    type = "get",
    dataType = "json"
)
public Map send(
    @DataVariable("base") String base,
    @DataParam("un") String userName,
    @DataParam("pw") String password,
    @DataParam("ph") String phone,
    @DataParam("ct") String content
);

以上三种写法是等价的

当然你也可以把参数绑定到header和body里去,你甚至于可以用一些表达式简单的把对象序列化成json或者xml:

@Request(
    url = "${base}/pay",
  	contentType = "application/json",
    type = "post",
    dataType = "json",
    headers = {"Authorization: ${1}"},
    data = "${json($0)}"
)
public PayResponse pay(PayRequest request, String auth);

4.2 对HTTPS的支持
以前用其他http框架处理https的时候,总觉得特别麻烦,尤其是双向证书。每次碰到问题也只能去baidu。然后根据别人的经验来修改自己的代码。

Forest对于这方面也想的很周到,底层完美封装了对https单双向证书的支持。也是只要通过简单的配置就能迅速完成。举个双向证书栗子:

@Request(
    url = "${base}/pay",
  	contentType = "application/json",
    type = "post",
    dataType = "json",
  	keyStore = "pay-keystore",
  	data = "${json($0)}"
)
public PayResponse pay(PayRequest request);

其中pay-keystore对应着application.yml里的ssl-key-stores

forest:
  ...
  ssl-key-stores:
    - id: pay-keystore
      file: test.keystore
      keystore-pass: 123456
      cert-pass: 123456
      protocols: SSLv3
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值