A high-level and lightweight HTTP client framework for Java. it makes sending HTTP requests in Java easier.

Related tags

HTTP Clients forest
Overview

logo

Gitee Stars JDK Codecov License Documentation Author

Forest - 轻量级HTTP客户端框架

项目介绍:

Forest是一个高层的、极简的轻量级HTTP调用API框架。
相比于直接使用Httpclient您不再用写一大堆重复的代码了,而是像调用本地方法一样去发送HTTP请求。

文档和示例:

Forest有哪些特性?

  • 以Httpclient和OkHttp为后端框架
  • 通过调用本地方法的方式去发送Http请求, 实现了业务逻辑与Http协议之间的解耦
  • 因为针对第三方接口,所以不需要依赖Spring Cloud和任何注册中心
  • 支持所有请求方法:GET, HEAD, OPTIONS, TRACE, POST, DELETE, PUT, PATCH
  • 支持文件上传和下载
  • 支持灵活的模板表达式
  • 支持拦截器处理请求的各个生命周期
  • 支持自定义注解
  • 支持OAuth2验证
  • 支持过滤器来过滤传入的数据
  • 基于注解、配置化的方式定义Http请求
  • 支持Spring和Springboot集成
  • JSON字符串到Java对象的自动化解析
  • XML文本到Java对象的自动化解析
  • JSON、XML或其他类型转换器可以随意扩展和替换
  • 支持JSON转换框架: Fastjson, Jackson, Gson
  • 支持JAXB形式的XML转换
  • 可以通过OnSuccess和OnError接口参数实现请求结果的回调
  • 配置简单,一般只需要@Request一个注解就能完成绝大多数请求的定义
  • 支持异步请求调用

极速开始

以下例子基于Spring Boot

第一步:添加Maven依赖

直接添加以下maven依赖即可

<dependency>
    <groupId>com.dtflys.forestgroupId>
    <artifactId>forest-spring-boot-starterartifactId>
    <version>1.5.0-RC6version>
dependency>

第二步:创建一个interface

就以高德地图API为栗子吧

package com.yoursite.client;

import com.dtflys.forest.annotation.Request;
import com.dtflys.forest.annotation.DataParam;

public interface AmapClient {

    /**
     * 聪明的你一定看出来了@Get注解代表该方法专做GET请求
     * 在url中的${0}代表引用第一个参数,${1}引用第二个参数
     */
    @Get("http://ditu.amap.com/service/regeo?longitude=${0}&latitude=${1}")
    Map getLocation(String longitude, String latitude);
}

第三步:扫描接口

在Spring Boot的配置类或者启动类上加上@ForestScan注解,并在basePackages属性里填上远程接口的所在的包名

@SpringBootApplication
@Configuration
@ForestScan(basePackages = "com.yoursite.client")
public class MyApplication {
  public static void main(String[] args) {
      SpringApplication.run(MyApplication.class, args);
   }
}

第四步:调用接口

OK,我们可以愉快地调用接口了

// 注入接口实例
@Autowired
private AmapClient amapClient;
...
// 调用接口
Map result = amapClient.getLocation("121.475078", "31.223577");
System.out.println(result);

发送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);

发送XML数据

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

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

文件上传

/**
 * 用@DataFile注解修饰要上传的参数对象
 * OnProgress参数为监听上传进度的回调函数
 */
@Post("/upload")
Map upload(@DataFile("file") String filePath, OnProgress onProgress);

可以用一个方法加Lambda同时解决文件上传和上传的进度监听

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!   --------");
    }
});

多文件批量上传

/**
 * 上传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 byteArrayList);

文件下载

下载文件也是同样的简单

/**
 * 在方法上加上@DownloadFile注解
 * dir属性表示文件下载到哪个目录
 * OnProgress参数为监听上传进度的回调函数
 * ${0}代表引用第一个参数
 */
@Get("http://localhost:8080/images/xxx.jpg")
@DownloadFile(dir = "${0}")
File downloadFile(String dir, OnProgress onProgress);

调用下载接口以及监听下载进度的代码如下:

File file = myClient.downloadFile("D:\\TestDownload", progress -> {
    System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%");  // 已下载百分比
    if (progress.isDone()) {   // 是否下载完成
        System.out.println("--------   Download Completed!   --------");
    }
});

基本签名验证

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

OAuth 2.0

@OAuth2(
        tokenUri = "/auth/oauth/token",
        clientId = "password",
        clientSecret = "xxxxx-yyyyy-zzzzz",
        grantType = OAuth2.GrantType.PASSWORD,
        scope = "any",
        username = "root",
        password = "xxxxxx"
)
@Get("/test/data")
String getData();

自定义注解

Forest允许您根据需要自行定义注解,不但让您可以简单优雅得解决各种需求,而且极大得扩展了Forest的能力。

定义一个注解

/**
 * 用Forest自定义注解实现一个自定义的签名加密注解
 * 凡用此接口修饰的方法或接口,其对应的所有请求都会执行自定义的签名加密过程
 * 而自定义的签名加密过程,由这里的@MethodLifeCycle注解指定的生命周期类进行处理
 * 可以将此注解用在接口类和方法上
 */
@Documented
/** 重点: @MethodLifeCycle注解指定该注解的生命周期类*/
@MethodLifeCycle(MyAuthLifeCycle.class)
@RequestAttributes
@Retention(RetentionPolicy.RUNTIME)
/** 指定该注解可用于类上或方法上 */
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyAuth {

    /** 
     * 自定义注解的属性:用户名
     * 所有自定注解的属性可以在生命周期类中被获取到
     */
    String username();

    /** 
     * 自定义注解的属性:密码
     * 所有自定注解的属性可以在生命周期类中被获取到
     */
    String password();
}

定义注解生命周期类

/**
 *  MyAuthLifeCycle 为自定义的 @MyAuth 注解的生命周期类
 * 因为 @MyAuth 是针对每个请求方法的,所以它实现自 MethodAnnotationLifeCycle 接口
 * MethodAnnotationLifeCycle 接口带有泛型参数
 * 第一个泛型参数是该生命周期类绑定的注解类型
 * 第二个泛型参数为请求方法返回的数据类型,为了尽可能适应多的不同方法的返回类型,这里使用 Object
 */
public class MyAuthLifeCycle implements MethodAnnotationLifeCycle<MyAuth, Object> {

 
    /**
     * 当方法调用时调用此方法,此时还没有执行请求发送
     * 次方法可以获得请求对应的方法调用信息,以及动态传入的方法调用参数列表
     */
    @Override
    public void onInvokeMethod(ForestRequest request, ForestMethod method, Object[] args) {
        System.out.println("Invoke Method '" + method.getMethodName() + "' Arguments: " + args);
    }

    /**
     * 发送请求前执行此方法,同拦截器中的一样
     */
    @Override
    public boolean beforeExecute(ForestRequest request) {
        // 通过getAttribute方法获取自定义注解中的属性值
        // getAttribute第一个参数为request对象,第二个参数为自定义注解中的属性名
        String username = (String) getAttribute(request, "username");
        String password = (String) getAttribute(request, "password");
        // 使用Base64进行加密
        String basic = "MyAuth " + Base64Utils.encode("{" + username + ":" + password + "}");
        // 调用addHeader方法将加密结构加到请求头MyAuthorization中
        request.addHeader("MyAuthorization", basic);
        return true;
    }

    /**
     * 此方法在请求方法初始化的时候被调用
     */
    @Override
    public void onMethodInitialized(ForestMethod method, BasicAuth annotation) {
        System.out.println("Method '" + method.getMethodName() + "' Initialized, Arguments: " + args);
    }
}

使用自定义的注解

/**
 * 在请求接口上加上自定义的 @MyAuth 注解
 * 注解的参数可以是字符串模板,通过方法调用的时候动态传入
 * 也可以是写死的字符串
 */
@Get("/hello/user?username=${username}")
@MyAuth(username = "${username}", password = "bar")
String send(@DataVariable("username") String username);

详细文档请看:http://forest.dtflyx.com/

贡献者列表

Giteye chart 正因为有他们的贡献,Forest才得以走下去

联系作者

亲,进群前记得先star一下哦~

扫描二维码关注公众号,点击菜单中的 进群 按钮即可进群

avatar

Gitee上的Star趋势图

Giteye chart

参与贡献

  1. 进群讨论,可以在群里抛出您遇到的问题,或许已经有人解决了您的问题。
  2. 提issue,如果在gitee的issue中已经有您想解决的问题,可以直接将该issue分配给您自己。如若没有,可以自己在gitee上创建一个issue。
  3. Fork 本项目的仓库
  4. 新建分支,如果是加新特性,分支名格式为feat_${issue的ID号},如果是修改bug,则命名为fix_${issue的ID号}
  5. 本地自测,提交前请通过所有的已经单元测试,以及为您要解决的问题新增单元测试。
  6. 提交代码
  7. 新建 Pull Request
  8. 我会对您的PR进行验证和测试,如通过测试,我会合到dev分支上随新版本发布时再合到master分支上。

欢迎小伙伴们多提issue和PR,被接纳PR的小伙伴我会列在README上的贡献者列表中:)

项目协议

The MIT License (MIT)

Copyright (c) 2016 Jun Gong

Comments
  • build(deps): bump jackson-databind from 2.9.10 to 2.9.10.7 in /spring-boot-starter-forest

    build(deps): bump jackson-databind from 2.9.10 to 2.9.10.7 in /spring-boot-starter-forest

    Bumps jackson-databind from 2.9.10 to 2.9.10.7.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 33
  • 重试配置maxRetryCount不生效

    重试配置maxRetryCount不生效

    版本: com.dtflys.forest forest-spring-boot-starter 1.5.16

    配置 @Retry(maxRetryCount = "1", condition = MyRetryCondition.class)

    当业务返回码为异常值时,MyRetryCondition返回true,请求会一直重试 跟踪进代码发现,每次重试记录重试次数的变量都为一个新的对象

    private AtomicInteger currentRetryCount = new AtomicInteger(0);

    opened by AlexPfbkdzy 5
  • @OAuth2 注解可配置化

    @OAuth2 注解可配置化

    场景

    项目是spring boot,@OAuth2 中的的配置信息需要做到配置化,如果再代码中硬编码灵活性太差。于是想通过forest变量来解决,如下

    @OAuth2(
        tokenUri = "${testaouth2.tokenUri}",
        clientId = "${testaouth2.clientId}",
        clientSecret = "${testaouth2.clientSecret}",
        grantType = OAuth2.GrantType.PASSWORD,
        scope = "${testaouth2.scope}",
        username = "${testaouth2.username}"
        password = "${testaouth2.password}"
    )
    

    问题

    grantType 是一个枚举,如何做到外部传参呢?

    opened by John-Chan 5
  • Get请求不支持消息体中带Json Body,发送时消息体被忽略?

    Get请求不支持消息体中带Json Body,发送时消息体被忽略?

    RT。 类似的接口:

    public interface Client {
        @Get(url = "http://127.0.0.1:8090/xxx", contentType = "application/json")
        public String saveInfo(@Body("username") MsgInfo info);
    }
    

    别人提供的接口,所以对接口合理性没有讨论的余地,最新版的forest也是不支持,通过打印内容排查了大概是忽略了消息体进行发送的。 查了下Get请求通过JSON BODY传参,发现HTTP协议并没有禁止,大体的讨论可以参考:https://zhuanlan.zhihu.com/p/456921996

    opened by CodingOX 4
  • 自定义注解优化建议

    自定义注解优化建议

    当使用自定义注解时,注解变量的值引用Forest全局变量进行赋值,当全局变量定义不存在时, 会报空指针异常。此处建议优化为,当全局变量的引用值为空时,使用自定义注解的对应字段的默认值, 没有默认值时再抛异常。 举例如下:

    public @interface ForestPorxy { String isProxy() default "false"; } @GetRequest(url = "https:.//www.baidu.com") @ForestPorxy(isProxy = "${proxy.baidui}") ForestResponse evtBaidu();

    如果proxy.baidui 在配置文件中不存在,现在的版本为直接抛出异常,且不会进入自定义注解的生命周期。

    opened by chinagoldline 4
  • forest-spring-boot-starter version:1.5.0 url param not encode.

    forest-spring-boot-starter version:1.5.0 url param not encode.

    使用forest-spring-boot-starter执行HTTP请求时,默认backend为okhttp3。 debug发现并未对Query参数进行URL Encode, 调用链路 com.dtflys.forest.backend.okhttp3.executor.AbstractOkHttp3Executor#execute(com.dtflys.forest.handler.LifeCycleHandler, int) com.dtflys.forest.backend.url.SimpleURLBuilder#buildUrl

    backend修改为httpclient调用的com.dtflys.forest.backend.url.QueryableURLBuilder则进行了正常URL Encode。 image image

    请修复

    opened by usami-muzugi 4
  • Error creating bean with name 'getForestBeanRegister'

    Error creating bean with name 'getForestBeanRegister'

    Hi, Thank you for making such an excellent framework.

    Now my new project still chose forest.

    But now I have encountered some mistakes and need your help. As the title indicates.

    My development environment:

    1. Spring boot 3.0.0-m1
    2. OpenJDK 17

    There are detailed error logs here:

    2022-03-08 13:51:14.848  INFO 2345 --- [           main] c.a.***.Application       : Starting Application using Java 17.0.2 on ***-Mac.local with PID 2345 (/Users/loadClass/IdeaProjects/target/classes started by *** in /Users/***/IdeaProjects/***)
    2022-03-08 13:51:14.849  INFO 2345 --- [           main] c.a.***.Application       : No active profile set, falling back to default profiles: default
    2022-03-08 13:51:15.207  WARN 2345 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'getForestBeanRegister' defined in class path resource [com/dtflys/forest/springboot/ForestAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.dtflys.forest.springboot.ForestBeanRegister]: Factory method 'getForestBeanRegister' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'forestConfiguration': Bean instantiation via factory method failed; nested exception is java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
    2022-03-08 13:51:15.220  INFO 2345 --- [           main] ConditionEvaluationReportLoggingListener : 
    
    Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
    2022-03-08 13:51:15.232 ERROR 2345 --- [           main] o.s.boot.SpringApplication               : Application run failed
    
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'getForestBeanRegister' defined in class path resource [com/dtflys/forest/springboot/ForestAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.dtflys.forest.springboot.ForestBeanRegister]: Factory method 'getForestBeanRegister' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'forestConfiguration': Bean instantiation via factory method failed; nested exception is java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
    	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:634) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:622) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1310) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1161) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:566) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:526) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:266) ~[spring-context-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:762) ~[spring-context-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:567) ~[spring-context-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-3.0.0-M1.jar:3.0.0-M1]
    	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:719) ~[spring-boot-3.0.0-M1.jar:3.0.0-M1]
    	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:401) ~[spring-boot-3.0.0-M1.jar:3.0.0-M1]
    	at org.springframework.boot.SpringApplication.run(SpringApplication.java:302) ~[spring-boot-3.0.0-M1.jar:3.0.0-M1]
    	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290) ~[spring-boot-3.0.0-M1.jar:3.0.0-M1]
    	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1279) ~[spring-boot-3.0.0-M1.jar:3.0.0-M1]
    	at com.allinprogram.***.Application.main(Application.java:12) ~[classes/:na]
    Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.dtflys.forest.springboot.ForestBeanRegister]: Factory method 'getForestBeanRegister' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'forestConfiguration': Bean instantiation via factory method failed; nested exception is java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
    	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:161) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:630) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	... 19 common frames omitted
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'forestConfiguration': Bean instantiation via factory method failed; nested exception is java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
    	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:634) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:622) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1310) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1161) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:566) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:526) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1138) ~[spring-context-6.0.0-M2.jar:6.0.0-M2]
    	at com.dtflys.forest.springboot.ForestBeanRegister.registerForestConfiguration(ForestBeanRegister.java:134) ~[forest-spring-boot-starter-1.5.19.jar:na]
    	at com.dtflys.forest.springboot.ForestAutoConfiguration.getForestBeanRegister(ForestAutoConfiguration.java:42) ~[forest-spring-boot-starter-1.5.19.jar:na]
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:130) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	... 20 common frames omitted
    Caused by: java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
    	at com.dtflys.forest.config.ForestConfiguration.createConfiguration(ForestConfiguration.java:336) ~[forest-core-1.5.19.jar:na]
    	at com.dtflys.forest.config.ForestConfiguration.configuration(ForestConfiguration.java:320) ~[forest-core-1.5.19.jar:na]
    	at com.dtflys.forest.config.ForestConfiguration.configuration(ForestConfiguration.java:306) ~[forest-core-1.5.19.jar:na]
    	at com.dtflys.forest.config.ForestConfiguration.<clinit>(ForestConfiguration.java:99) ~[forest-core-1.5.19.jar:na]
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:130) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:630) ~[spring-beans-6.0.0-M2.jar:6.0.0-M2]
    	... 37 common frames omitted
    Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBException
    	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[na:na]
    	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]
    	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[na:na]
    	... 47 common frames omitted
    
    Disconnected from the target VM, address: '127.0.0.1:50713', transport: 'socket'
    
    Process finished with exit code 1
    

    I may have found some helpful information: https://stackoverflow.com/questions/43574426/how-to-resolve-java-lang-noclassdeffounderror-javax-xml-bind-jaxbexception

    Please contact me if you need more information about the exception.

    opened by allinprogram 3
  • The singleton of httpclient

    The singleton of httpclient

    I think the httpclient/okhttpclient should singleton in connectionManager. Now the per request has a client. It’s so heavy and the most import is that the connection pool of client doesn‘t work.

    opened by dawn2zhang 3
  • 参数为空,导致空指针问题

    参数为空,导致空指针问题

    版本: com.dtflys.forest forest-spring-boot-starter 1.5.26

    报错如下: ERROR 21924 --- [nio-8081-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.dtflys.forest.exceptions.ForestRuntimeException: java.lang.NullPointerException] with root cause

    java.lang.NullPointerException: null at com.dtflys.forest.reflection.ForestMethod.getNameValueListFromObjectWithJSON(ForestMethod.java:1357) ~[forest-core-1.5.26.jar:na] at com.dtflys.forest.reflection.ForestMethod.makeRequest(ForestMethod.java:1033) ~[forest-core-1.5.26.jar:na] at com.dtflys.forest.reflection.ForestMethod.invoke(ForestMethod.java:1377) ~[forest-core-1.5.26.jar:na] at com.dtflys.forest.proxy.InterfaceProxyHandler.invoke(InterfaceProxyHandler.java:231) ~[forest-core-1.5.26.jar:na] at com.sun.proxy.$Proxy81.sendJsonMessageByPost(Unknown Source) ~[na:na] at com.example.http.forest.ForestServiceImpl.sendJsonMessageByPost(ForestServiceImpl.java:66) ~[classes/:na] ......

    代码如下: ForestResponse response = forestHttpApi.sendJsonMessageByPost(url, ObjectUtils.defaultIfNull(headerMap, Maps.newHashMap()), null, ObjectUtils.defaultIfNull(body, Maps.newHashMap()));

    @Post("{url}") ForestResponse sendJsonMessageByPost(@Var("url") String url, @Header Object headerMap, @Query Object queryMap, @JSONBody Object body);

    问题原因: @Query 对应的参数,传入了null,就会导致空指针问题。而且其他参数传入null,同样存在此问题,比如@Header, @JSONBody等

    解决方案建议: 希望内部添加空处理

    opened by MrCaiwei 2
  • 请求的@Body字段中的值有特殊符号被漏掉

    请求的@Body字段中的值有特殊符号被漏掉

    接口中定义: @Post(url="authenticate", contentType = "application/x-www-form-urlencoded") Result authenticate(@Body("username") String username, @Body("password") String password);

    调用: String username = "zhangsan"; String password = "123465+654321"; Result resp = portalAuthenticateClient.authenticate( username,password) 在服务器上收到的password时+变成了空格 困扰了很久,在线上排查了很久,通过wireshark才发现问题所在

    opened by misswolf 2
  • 我不太懂,spring版本问题嘛

    我不太懂,spring版本问题嘛

    Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.dtflys.forest.springboot.ForestBeanRegister]: Factory method 'getForestBeanRegister' threw exception; nested exception is java.lang.NoSuchMethodError: org.springframework.beans.factory.config.BeanDefinition.setRole(I)V at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:583) ... 41 more Caused by: java.lang.NoSuchMethodError: org.springframework.beans.factory.config.BeanDefinition.setRole(I)V

    opened by AlfredNing 2
  • response content type中的boundary信息丢失

    response content type中的boundary信息丢失

    服务端响应multipart type类型时,使用response.getContentType()拿不到boundary信息。debug结果如下: image 从上图可以看出,okhttp的mediaType中还是有boundary信息的,但是转成new ContentType之后boundary信息就丢了。

    opened by itzhoujun 0
  • @JSONQuery没对特殊字符转义,导致违反RFC 7230、RFC 3986

    @JSONQuery没对特殊字符转义,导致违反RFC 7230、RFC 3986

    客户端

     @Request("http://localhost:8080/params")
            String jsonQueryListCall(@JSONQuery("ids") List ids);
    
    2022-12-15 10:27:23,576 INFO c.d.f.c.ForestConfiguration [main] : [Forest] Http Backend: okhttp3
    2022-12-15 10:27:23,692 INFO c.d.f.l.ForestLogger [main] : [Forest] Request (okhttp3): 
    	GET http://localhost:8080/params?ids=[1,2] HTTP
    2022-12-15 10:27:23,709 INFO c.d.f.l.ForestLogger [main] : [Forest] Response: Status = 400, Time = 13ms
    2022-12-15 10:27:23,709 INFO c.d.f.l.ForestLogger [main] : [Forest] Response Content:
    	<!doctype html><html lang="en"><head><title>HTTP Status 400 – Bad Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 – Bad Request</h1></body></html
    

    服务端

    java.lang.IllegalArgumentException: Invalid character found in the request target [/params?ids=[1,2] ]. The valid characters are defined in RFC 7230 and RFC 3986
    
    opened by huang-zs 0
  • retryWhen 多执行一次

    retryWhen 多执行一次

    当前使用Forest版本以及所使用的后端及版本

    Forest: 1.5.28

    该问题是如何引起的?

    重现步骤(如果有请写完整)

    执行项目中 master 分支的单元测试 TestRetryClient.testRetry_base,可以看到 retryWhen 方法多调用了一次 xx

    报错信息/完整请求日志(如果没有请求日志请把开关打开)

    接口定义(必要时请提供)

    opened by hncboy 0
  • Releases(v1.5.28)
    • v1.5.28(Dec 7, 2022)

      v1.5.28版本发布了,此次版本发布是小版本更新,主要是修复一些BUG,并添加了onBodyEncode拦截器回调函数

      新增特性

      • feat: 拦截器新增 onBodyEncode 回调函数 (#I4WF5Q)
      • feat: 隐式转换带 BasicAuth 认证信息的请求 (#I62BTW)
      • feat: 声明式接口可返回 ForestFuture<T> 类型

      修复问题

      • fix: 下载文件,从URL中取得文件名时前后双引号的问题 (#I61NPK)
      • fix: 参数为空,导致空指针问题 (110)
      • fix: @HttpClient 和 @OkHttp3 注解在 1.5.27 失效的问题
      • fix: SpringSSLKeyStore构造方法缺少参数导致SpringBoot项目启动失败
      • fix: Null Pointer Error 当指定RetryWhen (#I5WEBC)
      • fix: @BaseRequest里的 connectTimeout和readTimeout没效果 (#I5WC6U)
      • fix: 接口上@Address注解的basePath设置为完整URL时,request.basePath() 结果不正确

      特别鸣谢

      • @idevmo
      Source code(tar.gz)
      Source code(zip)
    • v1.5.26(Aug 8, 2022)

      Forest v1.5.26版本发布了!此次更新主要对一些BUG进行修复

      新增特性

      • feat: 在使用 OkHttp3 后端情况下,允许Query参数不转义大括号 (#I5ITW9)
      • feat: 在使用 OkHttp3 时绕过空 Multipart 错误 (#I5I1AC)

      修复问题

      • fix: 默认自动绕过SSL验证
      • fix: 声明的接口返回类型如果是String(或其他Charsequencel类型)导致自定义converter (#I5L2P6)
      • fix: okhttp后端自动将charset=UTF-8转成了小写 (#I5L4AS)
      • fix: url域名信息参数赋值会自动参数后添加”/“符号路径导致错误 (#I5I62P)
      • fix: URL路径中的$字符会被转义
      • fix: 请求的ForestURL的ssl属性没有继承类里@BaseRequest的ssl信息 (#I5HXHX)

      其它改动

      • update: 更新 spring 版本到5.3.19
      • update: 更新 spring boot 版本到2.6.7
      Source code(tar.gz)
      Source code(zip)
    • v1.5.23(Jun 21, 2022)

      Forest v1.5.23 版本发布了!此次版本更新对请求执行的性能进行了彻底的优化!

      将 OkHttp3 后端的请求 QPS 提升了 3 倍多!

      将 Httpclient 后端的请求 QPS 提升了 1 倍多!

      优化内容

      1. 引入了 ForestRoute 路由的概念,每个 Host + Port 的不同组合对应不同的路由
      2. 将 OkHttpClient 以及 HttpClient 后端 Client 对象归类到不同的路由,并进行缓存
      3. 可以从外部注入自定义的后端 Client 对象
      4. 接口的后端 Client 对象是否缓存是可选项,可通过@BackendClient注解进行配置

      新增特性

      • feat: 快速接口支持快速下载文件
      • feat: 可配置注入OkHttpClient和HttpClient对象 (#I5CWAL)
      • feat: 可配置接口的后端Client对象是否缓存 (#I5D818)

      BUG修复

      • fix: springboot配置binary转换器初始化失败 (#I5D07S)
      • fix: ForestResponse.statusIs(xxx)会出现空指针 (#I5CWQL)
      • fix: 在@Address注解的basePath中写的端口号会失效 (#I5CR15)
      • fix: 使用@HTTPProxy注解对https请求设置http代理后出现java.lang.IllegalArgumentException:Socket may not be null
      • fix: 在不设置ContentType和BodyType的情况下无法正常发送请求 (#I5CML4)
      • fix: ForestRequest.addBody(List) 循环中只执行一次

      代码改动

      • refactor: 重构Cookie (#I5C26U)
      • refactor: 重构OkHttpClient
      • add: 添加拦截器到请求中方法ForestRequest#addInterceptor(Class<? extends Interceptor>)
      • add: HttpClientFactory
      • add: OkHttpClientFactory

      特别鸣谢

      • @CHMing
      • @tanglinyan
      Source code(tar.gz)
      Source code(zip)
    Owner
    dromara
    The girl on the prairie
    dromara
    Feign makes writing java http clients easier

    Feign makes writing java http clients easier Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSocket. Feign's first goal

    null 8.5k Jan 1, 2023
    http-kit is a minimalist, event-driven, high-performance Clojure HTTP server/client library with WebSocket and asynchronous support

    HTTP Kit A simple, high-performance event-driven HTTP client+server for Clojure CHANGELOG | API | current Break Version: [http-kit "2.5.3"] ; Publish

    HTTP Client/Server for Clojure 2.3k Dec 31, 2022
    Unirest in Java: Simplified, lightweight HTTP client library.

    Unirest for Java Install With Maven: <!-- Pull in as a traditional dependency --> <dependency> <groupId>com.konghq</groupId> <artifactId>unire

    Kong 2.4k Jan 5, 2023
    Unirest in Java: Simplified, lightweight HTTP client library.

    Unirest for Java Install With Maven: <!-- Pull in as a traditional dependency --> <dependency> <groupId>com.konghq</groupId> <artifactId>unire

    Kong 2.4k Jan 5, 2023
    ⚗️ Lightweight HTTP extensions for Java 11

    Methanol A lightweight library that complements java.net.http for a better HTTP experience. Overview Methanol provides useful lightweight HTTP extensi

    Moataz Abdelnasser 175 Dec 17, 2022
    Asynchronous Http and WebSocket Client library for Java

    Async Http Client Follow @AsyncHttpClient on Twitter. The AsyncHttpClient (AHC) library allows Java applications to easily execute HTTP requests and a

    AsyncHttpClient 6k Jan 8, 2023
    Square’s meticulous HTTP client for the JVM, Android, and GraalVM.

    OkHttp See the project website for documentation and APIs. HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP

    Square 43.4k Jan 5, 2023
    Google HTTP Client Library for Java

    Google HTTP Client Library for Java Description Written by Google, the Google HTTP Client Library for Java is a flexible, efficient, and powerful Java

    Google APIs 1.3k Jan 4, 2023
    A Java event based WebSocket and HTTP server

    Webbit - A Java event based WebSocket and HTTP server Getting it Prebuilt JARs are available from the central Maven repository or the Sonatype Maven r

    null 808 Jan 3, 2023
    Konas Client de-obfuscated and manually remaped by Gopro336, Perry, and other based people

    Konas-Deobf-Remap This project doesent really have a purpose anymore now that the real source code has leaked (this is a higher version tho) Deobfusca

    null 52 Dec 13, 2022
    Java HTTP Request Library

    Http Request A simple convenience library for using a HttpURLConnection to make requests and access the response. This library is available under the

    Kevin Sawicki 3.3k Dec 30, 2022
    Tiny, easily embeddable HTTP server in Java.

    NanoHTTPD – a tiny web server in Java NanoHTTPD is a light-weight HTTP server designed for embedding in other applications, released under a Modified

    NanoHttpd 6.5k Jan 5, 2023
    Share the chat messages across Minecraft Servers via HTTP backend powered by Spring Boot, this is the backend part of the project.

    InterconnectedChat-Backend Share the chat messages across Minecraft Servers via HTTP backend powered by Spring Boot, this is the backend part of the p

    贺兰星辰 3 Oct 6, 2021
    Fast_Responder is a service that lets you quickly create an Http request responder

    Fast_Responder is a service that lets you quickly create an Http request responder. The transponder can receive any request path configured and determine the request parameters according to your configuration to return different results. In addition to processing requests, the transponder can also make Http requests to any request address based on the latency, request headers, and parameters you configure. In essence, fast_responder is a dynamic mock service.

    null 8 Jan 26, 2022
    httpx - CLI to run HTTP file

    httpx: CLI for run http file httpx is a CLI to execute requests from JetBrains Http File. How to install? Mac : brew install httpx-sh/tap/httpx Other

    httpx 105 Dec 15, 2022
    AltiriaSmsJavaClient, the official Java client of Altiria

    ¡Atención! Este proyecto aún se encuentra en desarrollo. Pronto se publicará la versión final para su uso. Altiria, cliente SMS Java Altiria SMS Java

    Altiria 4 Dec 5, 2022
    Koios Java Client Library is based on Koios Elastic Query Layer for Cardano Node by Cardano Community Guild Operators

    Koios Java Client What is Koios? Koios Java Client Library is based on Koios Elastic Query Layer for Cardano Node by Cardano Community Guild Operators

    Dudi Edri 13 Dec 4, 2022
    Client-side response routing for Spring

    Riptide: A next generation HTTP client Riptide noun, /ˈrɪp.taɪd/: strong flow of water away from the shore Riptide is a library that implements client

    Zalando SE 235 Jan 3, 2023