JQuick-Curl
JQuick-Curl 是一个轻量级 Java HTTP 客户端,它把 curl 的用法原样搬进了 Java:你用一条普通的 curl 命令描述请求,再把它当作一个普通的 Java 方法调用。命令由 ANTLR 语法解析,最终运行在带连接池的高性能传输层之上,因此不再需要手写请求构建代码。
⭐ 本项目已被 Awesome Java 收录。
- 原生 curl 风格 API:用 curl 命令描述请求,用 Java 方法调用。
- 注解与 XML 双配置:
@JCurlCommand直接写在接口上,或集中维护在apis.xml。 - 动态代理客户端:
JCurlInvoker.createProxy(UserApi.class)把接口变成可调用的客户端。 - 变量替换:运行时解析
${name}/#{name}占位符。 - 条件渲染:XML
<if test="...">表达式为真时才渲染对应的 Header 或选项。 - Cookie 支持:手动 Cookie、Netscape Cookie 文件(Cookie Jar)与
Set-Cookie持久化。 - HTTP 方法:
GET、POST、PUT、PATCH、DELETE、HEAD、OPTIONS、TRACE。 - 文件传输:
-F多部分上传,-o/--output下载。 - 批量执行:一次调用执行类中所有
@JCurlCommand方法。 - 超时 / 重试 / 重定向 / 连接池:通过
JQuickCurlConfig统一配置。 - 拦截器:全局追加认证、日志或请求预处理。
- 代理与 SSL:支持 HTTP / SOCKS5 代理,
-k/--insecure跳过证书校验。
<dependency> <groupId>io.github.paohaijiao</groupId> <artifactId>jquick-curl</artifactId> <version>2.6.0</version></dependency>implementation 'io.github.paohaijiao:jquick-curl:2.6.0'1. GET 请求
Section titled “1. GET 请求”在接口方法上声明 curl 命令,创建代理后即可调用。
import com.github.paohaijiao.anno.JCurlCommand;import com.github.paohaijiao.domain.req.JQuickCurlReq;import com.github.paohaijiao.executor.JCurlInvoker;
public interface UserApi {
// GET 请求:返回响应体字符串 / GET request: returns the response body as a String @JCurlCommand("curl -X GET https://httpbin.org/get") String list(JQuickCurlReq request);}
class GetDemo { public static void main(String[] args) throws Exception { UserApi api = JCurlInvoker.createProxy(UserApi.class); String body = api.list(new JQuickCurlReq()); System.out.println(body); }}2. POST + JSON 请求体
Section titled “2. POST + JSON 请求体”import com.github.paohaijiao.anno.JCurlCommand;import com.github.paohaijiao.domain.req.JQuickCurlReq;import com.github.paohaijiao.executor.JCurlInvoker;
public interface OrderApi {
// POST 请求,携带 JSON 请求体 / POST with a JSON body @JCurlCommand("curl -X POST https://httpbin.org/post " + "-H 'Content-Type: application/json' " + "-d '{\"sku\":\"A-1001\",\"count\":2}'") String create(JQuickCurlReq request);}
class PostDemo { public static void main(String[] args) throws Exception { OrderApi api = JCurlInvoker.createProxy(OrderApi.class); System.out.println(api.create(new JQuickCurlReq())); }}${...} 占位符在调用时从 JQuickCurlReq 中解析,凭据请放在变量里,不要硬编码。
public interface AuthApi {
@JCurlCommand("curl -X GET https://api.example.com/me -u ${user}:${password}") String currentUser(JQuickCurlReq request);}JQuickCurlReq request = new JQuickCurlReq();request.put("user", "ada");request.put("password", System.getenv("API_PASSWORD"));
String me = JCurlInvoker.createProxy(AuthApi.class).currentUser(request);Cookie 与登录会话
Section titled “Cookie 与登录会话”-b 发送 Cookie,-c / --cookie-jar 把响应中的 Set-Cookie 按 Netscape 格式落盘,-b @file 在后续请求中复用。
import com.github.paohaijiao.anno.JCurlCommand;import com.github.paohaijiao.domain.req.JQuickCurlReq;import com.github.paohaijiao.executor.JCurlInvoker;import com.github.paohaijiao.responseBody.JQuickCurlResponseBody;
public interface SessionApi {
// 第一步:登录并把所有 Set-Cookie 写入 cookies.txt @JCurlCommand("curl -X POST https://httpbin.org/post " + "-H 'Content-Type: application/json' " + "-d '{\"user\":\"ada\",\"password\":\"secret\"}' " + "-c ./cookies.txt") JQuickCurlResponseBody login(JQuickCurlReq request);
// 第二步:后续请求自动带上持久化的 Cookie @JCurlCommand("curl -X GET https://httpbin.org/cookies -b @./cookies.txt") String profile(JQuickCurlReq request);}返回值声明为 JQuickCurlResponseBody 即可访问响应头与原始报文。
JQuickCurlResponseBody response = api.setCookie(new JQuickCurlReq());
String first = response.header("Set-Cookie"); // 第一个 Set-CookieList<String> all = response.headers("Set-Cookie"); // 全部 Set-Cookie(重定向可能返回多个)String body = response.asString(); // 响应报文文件上传与下载
Section titled “文件上传与下载”public interface FileApi {
// 多部分上传:-F 'file=@/path/to/file' @JCurlCommand("curl -X POST https://api.example.com/files -F 'file=@./report.pdf'") String upload(JQuickCurlReq request);
// 表单字段与文件混合上传 @JCurlCommand("curl -X POST https://api.example.com/import " + "-F 'userId=1001' -F 'file=@./report.pdf'") String uploadWithForm(JQuickCurlReq request);
// 下载:-o / --output 把响应字节写入本地文件 @JCurlCommand("curl -X GET https://api.example.com/files/report.pdf --output './download/report.pdf'") byte[] download(JQuickCurlReq request);}JQuickCurlBatchRunner 会扫描类中所有带 @JCurlCommand 的 public 方法并顺序执行。
import com.github.paohaijiao.anno.JCurlCommand;import com.github.paohaijiao.responseBody.JQuickCurlResponseBody;import com.github.paohaijiao.support.JQuickCurlBatchRunner;
import java.util.List;
public class BatchCommands {
@JCurlCommand("curl -X GET https://httpbin.org/get") public String first() { return null; }
@JCurlCommand("curl -X GET https://httpbin.org/uuid") public String second() { return null; }}
class BatchDemo { public static void main(String[] args) throws Exception { JQuickCurlBatchRunner runner = new JQuickCurlBatchRunner(); List<JQuickCurlResponseBody> results = runner.runCurlCommands(new BatchCommands(), JQuickCurlResponseBody.class); results.forEach(r -> System.out.println(r.asString())); }}超时、连接池与拦截器
Section titled “超时、连接池与拦截器”import com.github.paohaijiao.config.JQuickCurlConfig;import okhttp3.Interceptor;
import java.util.concurrent.TimeUnit;
JQuickCurlConfig.getInstance() .connectTimeout(3, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .connectionPool(50, 5, TimeUnit.MINUTES) .maxRetryCount(2) .followRedirects(true);
// 全局拦截器:统一追加 Bearer TokenInterceptor auth = chain -> chain.proceed( chain.request().newBuilder() .addHeader("Authorization", "Bearer " + System.getenv("API_TOKEN")) .build());
JQuickCurlConfig.getInstance().addInterceptor(auth);XML 集中配置
Section titled “XML 集中配置”请求模板可以放在 XML 中,与 Java 代码彻底解耦。
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE curls PUBLIC "-//PAOHAIJIAO//DTD API CURL 1.0//EN" "classpath:paohaijiao/dtd/Jquick-curl.dtd"><curls namespace="com.example.UserApi"> <curl name="getUser" returnClass="java.lang.String"> curl -X GET https://api.example.com/users/#{id} -b @./cookies.txt </curl></curls>import com.github.paohaijiao.xml.JQuickCurlXmlParseFactory;import com.github.paohaijiao.xml.factory.JQuickFactory;import com.github.paohaijiao.xml.factory.JQuickXmlFactory;import com.github.paohaijiao.xml.handler.JQuickParseHandler;
JQuickParseHandler parser = new JQuickCurlXmlParseFactory();JQuickFactory factory = new JQuickXmlFactory(parser, "apis.xml");UserApi api = factory.createApi(UserApi.class);已实现的 curl 选项
Section titled “已实现的 curl 选项”| 分类 | 支持的写法 | 用途 |
|---|---|---|
| 请求方法 | -X <METHOD>、--request <METHOD> |
指定 HTTP 方法 |
| 请求头 | -H 'Name: value'、--header 'Name: value' |
追加请求头 |
| Cookie | -b 'name=value'、--cookie、-b @file |
发送 Cookie 或加载 Cookie 文件 |
| Cookie Jar | -c <file>、--cookie-jar <file> |
把 Set-Cookie 持久化到文件 |
| 请求数据 | -d、--data、--data-ascii、--data-binary、--data-raw |
发送请求体 |
| 表单编码 | --data-urlencode 'key=value' |
发送 URL 编码表单 |
| Basic 认证 | -u 'user:password'、--user |
生成 Basic Authorization 头 |
| 重定向 | -L、--location、--max-redirs <N> |
跟随重定向并限制次数 |
| 文件上传 | -F 'file=@/path/to/file'、--form 'key=value' |
多部分上传或表单字段 |
| 文件下载 | -o './file'、--output './file' |
把响应字节写入本地文件 |
| 代理 | -x 'host:port'、--proxy、--socks5-hostname |
使用 HTTP / SOCKS5 代理 |
| 协议与日志 | --http2、-k、--insecure、-v、--verbose、-s、--silent |
HTTP/2、跳过证书校验、详细或静默输出 |