Forest Declarative HTTP Client

repository·master·Indexed 23 days ago

https://github.com/dromara/forest

A high-level, minimalist declarative HTTP client framework for Java that decouples business logic from HTTP protocols. It allows developers to send requests via local methods using annotations such as @Get, @Post, @JSONBody, @XMLBody, and @ProtobufBody. Forest provides a Spring Boot starter for easy integration, supports OAuth 2.0, file uploads/downloads with progress monitoring, and a fluent API for programmatic requests.

Tokens
3.4K
Snippets
11
Records
15
Agent score
84%

What's inside Forest

  1. Create custom annotations with lifecycle hooks

    master

    You can extend Forest by defining custom annotations and implementing a MethodAnnotationLifeCycle. This allows you to intercept the request lifecycle (e.g., onInvokeMethod, beforeExecute, onMethodInitialized) to perform tasks like custom signature encryption.

    1. Define the Annotation: Use @MethodLifeCycle to link the annotation to its handler class.

    @Documented
    @MethodLifeCycle(MyAuthLifeCycle.class)
    @RequestAttributes
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    public @interface MyAuth {
        String username();
        String password();
    }

    2. Implement the Lifecycle Class: Implement MethodAnnotationLifeCycle<AnnotationType, ReturnType> to handle the logic.

    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) {
            String username = (String) getAttribute(request, "username");
            String password = (String) getAttribute(request, "password");
            String basic = "MyAuth " + Base64Utils.encode("{" + username + ":" + password + "}");
            request.addHeader("MyAuthorization", basic);
            return true;
        }
    
        @Override
        public void onMethodInitialized(ForestMethod method, MyAuth annotation) {
            // Initialization logic
        }
    }

    3. Use the Annotation:

    @Get("/hello/user?username={username}")
    @MyAuth(username = "{username}", password = "bar")
    String send(@DataVariable("username") String username);
  2. Scan Forest interfaces in Spring Boot

    master

    To enable Forest interface scanning, add the @ForestScan annotation to your Spring Boot application class or any @Configuration class. Specify the package containing your client interfaces using the basePackages attribute.

    @SpringBootApplication
    @Configuration
    @ForestScan(basePackages = "com.yoursite.client")
    public class MyApplication {
      public static void main(String[] args) {
          SpringApplication.run(MyApplication.class, args);
       }
    }
  3. Integrate DeepSeek using Forest in Spring Boot

    master

    This example demonstrates how to call DeepSeek APIs within a Spring Boot environment using the forest-spring-boot-starter. It utilizes Forest's declarative interface approach to handle HTTP requests and uses Fastjson2 for JSON serialization and deserialization.

    The implementation involves defining a Forest interface to map the DeepSeek API endpoints and creating data classes to model the request and response structures.

  4. Integrate ChatGPT using Forest in Spring Boot

    master

    This example demonstrates how to call OpenAI's API using the forest-spring-boot-starter package within a Spring Boot environment. It utilizes Forest's declarative interface approach to handle HTTP requests and uses Fastjson for JSON serialization and deserialization.

    The implementation consists of:

    • A Spring Boot application class (ChartGPTExampleApplication).
    • A Forest declarative interface (ChartGPT) defining the API endpoints.
    • Data classes (GPTResponse and GPTChoice) to map the OpenAI response structure.
  5. Install Forest with Maven Spring Boot Starter

    master

    To use Forest in a Spring Boot application, add the forest-spring-boot-starter dependency to your pom.xml. Replace ${LATEST_VERSION} with the current version of Forest.

    <dependency>
        <groupId>com.dtflys.forest</groupId>
        <artifactId>forest-spring-boot-starter</artifactId>
        <version>${LATEST_VERSION}</version>
    </dependency>
  6. Configure Forest global variables and timeouts

    master

    You can configure global HTTP settings and custom variables in your Spring Boot application.yml (or application.properties) under the forest prefix. Custom variables defined in the forest.variables section can be referenced within your Forest interface definitions (e.g., using ${apiKey}).

    Available configuration keys:

    • forest.connect-timeout: Connection timeout in milliseconds.
    • forest.read-timeout: Read timeout in milliseconds.
    • forest.variables: A map of custom variables available to your Forest interfaces.
    forest:
      connect-timeout: 60000
      read-timeout: 60000
      variables:
        apiKey: YOUR_API_KEY
        model: text-davinci-003
        maxTokens: 50
        temperature: 0.5
  7. Send JSON data using @JSONBody

    master

    Use the @JSONBody annotation to automatically serialize an object, a Map, or a JSON string into the request body as JSON.

    /**
     * Parses object parameter as JSON string in the request Body
     */
    @Post("/register")
    String registerUser(@JSONBody MyUser user);
    
    /**
     * Parses Map parameter as JSON string in the request Body
     */
    @Post("/test/json")
    String postJsonMap(@JSONBody Map mapObj);
    
    /**
     * Passes a JSON string directly into the request Body
     */
    @Post("/test/json")
    String postJsonText(@JSONBody String jsonText);
    /**
     * 将对象参数解析为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);
  8. Send XML data using @XMLBody

    master

    Use the @XMLBody annotation to serialize a JAXB-annotated object or a raw XML string into the request body.

    /**
     * Parses a JAXB-annotated type object into XML string in the request Body
     */
    @Post("/message")
    String sendXmlMessage(@XMLBody MyMessage message);
    
    /**
     * Passes an XML string directly into the request Body
     */
    @Post("/test/xml")
    String postXmlBodyString(@XMLBody String xml);
    /**
     * 将一个通过JAXB注解修饰过的类型对象解析为XML字符串
     * 并放在请求的Body进行传输
     */
    @Post("/message")
    String sendXmlMessage(@XMLBody MyMessage message);
    
    /**
     * 直接传入一个XML字符串,并放在请求的Body进行传输
     */
    @Post("/test/xml")
    String postXmlBodyString(@XMLBody String xml);
  9. Define a declarative HTTP client interface

    master

    Forest allows you to define HTTP requests using Java interfaces and annotations. Use annotations like @Get or @Post to specify the request method and URL. You can use placeholders like {0}, {1} in the URL to reference method arguments.

    package com.yoursite.client;
    
    import com.dtflys.forest.annotation.Request;
    import com.dtflys.forest.annotation.DataParam;
    
    public interface AmapClient {
    
        /**
         * @Get annotation specifies a GET request.
         * {0} and {1} in the URL refer to the first and second arguments respectively.
         */
        @Get("http://ditu.amap.com/service/regeo?longitude={0}&latitude={1}")
        Map getLocation(String longitude, String latitude);
    }
  10. Send Protobuf data using @ProtobufBody

    master

    To send Protobuf-encoded data, use the @ProtobufBody annotation. Note that you must specify the contentType as application/octet-stream and have the Google Protobuf dependency in your project.

    /**
     * Converts Protobuf generated data object to Protobuf byte stream
     * and places it in the request Body.
     * Note: requires google protobuf dependency
     */
    @Post(url = "/message", contentType = "application/octet-stream")
    String sendProtobufMessage(ProtobufProto.MyMessage message);
    /**
     * ProtobufProto.MyMessage 为 Protobuf 生成的数据类
     * 将 Protobuf 生成的数据对象转换为 Protobuf 格式的字节流
     * 并放在请求的Body进行传输
     * 
     * 注: 需要引入 google protobuf 依赖
     */
    @Post(url = "/message", contentType = "application/octet-stream")
    String sendProtobufMessage(ProtobufProto.MyMessage message);
  11. Perform programmatic HTTP requests

    master

    If you prefer not to use interfaces, Forest provides a fluent API for programmatic requests.

    GET request:

    String baidu = Forest.get("http://www.baidu.com").execute(String.class);

    POST request with body:

    String result = Forest.post("/user/register")
            .contentType("application/json")
            .addBody("username", "公子骏")
            .addBody("password", "12345678")
            .execute(String.class);
    // GET 请求访问百度
    String baidu = Forest.get("http://www.baidu.com").execute(String.class);
    
    // POST 请求注册用户信息
    String result = Forest.post("/user/register")
            .contentType("application/json")
            .addBody("username", "公子骏")
            .addBody("password", "12345678")
            .execute(String.class);