This issue recommends a declarative HTTP framework – Forest.
Forest is an open-source Java HTTP client framework that can bind all HTTP request information (including URL, Header, Body, etc.) to your custom Interface method and send HTTP requests by calling local interface methods.
Using Forest is like using an RPC framework like Dubbo, where you only need to define and call the interface, without worrying about the details of sending HTTP requests. At the same time, decoupling HTTP request information from business code facilitates unified management of a large amount of HTTP URLs, headers, and other information. And the caller of the request does not need to pay attention to the specific content of the HTTP, even if the HTTP request information changes, in most cases there is no need to modify the code that calls and sends the request.
Functional characteristics
- Declarative interface: Encapsulates HTTP requests through defined interfaces and annotations, achieving decoupling between business logic and HTTP protocol
- Multiple underlying HTTP frameworks: using Httpclient and OkHttp as backend frameworks (optional)
- Not dependent on middleware: As it targets third-party interfaces, there is no need to rely on Spring Cloud or any registry
- Supports all request methods: GET, HEAD, OPTIONS, TRACE, POST, DELETE, PUT, PATCH
- Agreement is greater than configuration: as long as dependencies are added, basically nothing can be configured and it can run immediately
- Automatic JSON Conversion: Built in Jackson, Fastjson, and Gson JSON converters
- Automatic XML Conversion: Built in JAXB based XML Converter
- Automatic Protobuf Conversion: Built in Protobuf Format Data Converter
- Multiple verification methods: Basic Auth, OAuth2, and verification through custom interceptors or annotations
- Spring/Spring boot: Supports integration of Spring and Springboot
- Upload and download: supports multiple upload and download methods, and can monitor data transmission progress
- Template expression: Supports flexible template expressions to be used in conjunction with declarative annotations
- Interceptor: Supports interceptors to handle various lifecycles of requests
- Custom Annotations: Supports custom annotations to greatly enhance scalability
- Callback function: supports callback of request results through OnSuccess and OnError interface parameters
- Asynchronous request: supports convenient asynchronous request invocation methods
- Programming interfaces: In addition to declarative interfaces, intuitive programming interfaces are also supported
working principle
Forest will generate a concrete implementation class through dynamic proxy based on the interface you have defined, and then organize and validate HTTP request information, bind dynamic data, convert data forms, SSL verify signatures, call backend HTTP APIs (such as httpclient) to execute actual requests, wait for responses, retry failures, convert response data to Java types, and other dirty and tiring tasks are all covered by this dynamic proxy implementation class. When requesting the sender to call this interface, it is actually calling the implementation class that does the dirty and tiring work.
Quick Start
1 Add Maven dependency
Springboot environment:
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot-starter</artifactId>
<version>1.5.25</version>
</dependency>
Spring environment:
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-core</artifactId>
<version>1.5.25</version>
</dependency>
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring</artifactId>
<version>1.5.25</version>
</dependency>
Native Java environment:
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-core</artifactId>
<version>1.5.25</version>
</dependency>
2 Create an interface
Taking Gaode Map API as an example:
package com.yoursite.client;
import com.dtflys.forest.annotation.Request;
import com.dtflys.forest.annotation.DataParam;
public interface AmapClient {
/**
* You must have noticed that the @ Get annotation represents that this method is specifically designed for GET requests
* {0} in the URL represents referencing the first parameter, and {1} refers to the second parameter
*/
@Get("http://ditu.amap.com/service/regeo?longitude={0}&latitude={1}")
Map getLocation(String longitude, String latitude);
}
3 Scan interface
Add the @ ForestScan annotation to the configuration or boot class of Spring Boot, and fill in the package name of the remote interface in the basePackages property
@SpringBootApplication
@Configuration
@ForestScan(basePackages = "com.yoursite.client")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
4 Call interface
// Inject interface instance
@Autowired
private AmapClient amapClient;
...
// Call interface
Map result = amapClient.getLocation("121.475078", "31.223577");
System.out.println(result);
Send JSON data
/**
* Parse object parameters into JSON strings and transmit them in the requested Body
*/
@Post("/register")
String registerUser(@JSONBody MyUser user);
/**
* Parse Map type parameters into JSON strings and transmit them in the requested Body
*/
@Post("/test/json")
String postJsonMap(@JSONBody Map mapObj);
/**
* Directly input a JSON string and place it in the requested body for transmission
*/
@Post("/test/json")
String postJsonText(@JSONBody String jsonText);
Sending XML data
/**
* Parse a type object modified with JAXB annotations into an XML string
* And put it in the requested body for transmission
*/
@Post("/message")
String sendXmlMessage(@XMLBody MyMessage message);
/**
* Directly input an XML string and transmit it in the requested body
*/
@Post("/test/xml")
String postXmlBodyString(@XMLBody String xml);
Send Protobuf data
/**
* ProtobufProto.MyMessage is the data class generated by Protobuf
* Convert the data objects generated by Protobuf into a byte stream in Protobuf format
* And put it in the requested body for transmission
*
* Note: Need to introduce Google Protobuf dependency
*/
@Post(url = "/message", contentType = "application/octet-stream")
String sendProtobufMessage(@ProtobufBody ProtobufProto.MyMessage message);
File upload
/**
* Decorate the parameter object to be uploaded with @ DataMile annotation
* The PnP rogress parameter is a callback function that listens for upload progress
*/
@Post("/upload")
Map upload(@DataFile("file") String filePath, OnProgress onProgress);
A method can be used to add Lambda to simultaneously solve file upload and upload progress monitoring
Map result = myClient.upload("D:\\TestUpload\\xxx.jpg", progress -> {
System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%"); // Percentage uploaded
if (progress.isDone()) { // Is the upload completed
System.out.println("-------- Upload Completed! --------");
}
});
Batch upload of multiple files
/**
* Upload a list of files packaged in Map, where {_key} represents the key values in each iteration of the Map
*/
@Post("/upload")
ForestRequest<Map> uploadByteArrayMap(@DataFile(value = "file", fileName = "{_key}") Map<String, byte[]> byteArrayMap);
/**
* Upload the file list packaged with List, where {_index} represents the loop count of List for each iteration (starting from zero)
*/
@Post("/upload")
ForestRequest<Map> uploadByteArrayList(@DataFile(value = "file", fileName = "test-img-{_index}.jpg") List<byte[]> byteArrayList);
File Download
Downloading files is equally simple
/**
* Add @ DownloadFile annotation on the method
* The dir attribute indicates which directory the file is downloaded to
* The PnP rogress parameter is a callback function that listens for upload progress
* {0} represents referencing the first parameter
*/
@Get("http://localhost:8080/images/xxx.jpg")
@DownloadFile(dir = "{0}")
File downloadFile(String dir, OnProgress onProgress);
The code for calling the download interface and monitoring the download progress is as follows:
File file = myClient.downloadFile("D:\\TestDownload", progress -> {
System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%"); // Downloaded percentage
if (progress.isDone()) { // Is the download complete
System.out.println("-------- Download Completed! --------");
}
});
—END—
Open source license: MIT