Update parser documentation with PanBase inheritance and WebClient flow

Co-authored-by: qaiu <29825328+qaiu@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-10-17 23:03:39 +00:00
parent e6672a51c5
commit c7e6d68fbd
2 changed files with 142 additions and 43 deletions

View File

@@ -60,11 +60,20 @@ mvn -pl parser test
本模块支持用户自定义解析器扩展。通过简单的配置和注册,你可以添加自己的网盘解析实现:
```java
// 1. 实现 IPanTool 接口
public class MyPanTool implements IPanTool {
public MyPanTool(ShareLinkInfo info) { /* 必须提供此构造器 */ }
// 1. 继承 PanBase 抽象类(推荐)
public class MyPanTool extends PanBase {
public MyPanTool(ShareLinkInfo info) {
super(info);
}
@Override
public Future<String> parse() { /* 实现解析逻辑 */ }
public Future<String> parse() {
// 使用 PanBase 提供的 HTTP 客户端
client.getAbs("https://api.example.com")
.send()
.onSuccess(res -> complete(asJson(res).getString("url")))
.onFailure(handleFail("请求失败"));
return future();
}
}
// 2. 注册到系统

View File

@@ -1,5 +1,7 @@
# 自定义解析器扩展指南
> 最后更新2025-10-17
## 概述
本模块支持用户自定义解析器扩展。用户在依赖本项目的 Maven 坐标后,可以实现自己的网盘解析器并注册到系统中使用。
@@ -27,49 +29,50 @@
</dependency>
```
### 步骤2: 实现 IPanTool 接口
### 步骤2: 继承 PanBase 抽象类
创建自己的解析工具类,必须实现 `IPanTool` 接口
创建自己的解析工具类,**必须继承 `PanBase` 抽象类**(而不是直接实现 IPanTool 接口。PanBase 提供了丰富的工具方法和 HTTP 客户端,简化解析器的开发。
```java
package com.example.parser;
import cn.qaiu.entity.ShareLinkInfo;
import cn.qaiu.parser.IPanTool;
import cn.qaiu.parser.PanBase;
import io.vertx.core.Future;
import io.vertx.core.Promise;
/**
* 自定义网盘解析器示例
*/
public class MyCustomPanTool implements IPanTool {
private final ShareLinkInfo shareLinkInfo;
public class MyCustomPanTool extends PanBase {
/**
* 必须提供 ShareLinkInfo 单参构造器
*/
public MyCustomPanTool(ShareLinkInfo shareLinkInfo) {
this.shareLinkInfo = shareLinkInfo;
super(shareLinkInfo);
}
@Override
public Future<String> parse() {
Promise<String> promise = Promise.promise();
// 实现你的解析逻辑
// 使用 PanBase 提供的 HTTP 客户端发起请求
String shareKey = shareLinkInfo.getShareKey();
String sharePassword = shareLinkInfo.getSharePassword();
try {
// 调用你的网盘API获取下载链接
String downloadUrl = callYourPanApi(shareKey, sharePassword);
promise.complete(downloadUrl);
} catch (Exception e) {
promise.fail(e);
}
// 示例:使用 client 发起 GET 请求
client.getAbs("https://your-pan-domain.com/api/share/" + shareKey)
.send()
.onSuccess(res -> {
// 使用 asJson 方法将响应转换为 JSON
var json = asJson(res);
String downloadUrl = json.getString("download_url");
return promise.future();
// 使用 complete 方法完成 Promise
complete(downloadUrl);
})
.onFailure(handleFail("请求下载链接失败"));
// 返回 Future
return future();
}
/**
@@ -78,7 +81,7 @@ public class MyCustomPanTool implements IPanTool {
@Override
public Future<List<FileInfo>> parseFileList() {
// 实现文件列表解析逻辑
return IPanTool.super.parseFileList();
return super.parseFileList();
}
/**
@@ -87,16 +90,96 @@ public class MyCustomPanTool implements IPanTool {
@Override
public Future<String> parseById() {
// 实现根据ID解析的逻辑
return IPanTool.super.parseById();
}
private String callYourPanApi(String shareKey, String password) {
// 实现你的网盘API调用逻辑
return "https://your-pan-domain.com/download/" + shareKey;
return super.parseById();
}
}
```
### PanBase 提供的核心方法
PanBase 为解析器开发提供了以下工具和方法:
#### HTTP 客户端
- **`client`**: 标准 WebClient 实例,支持自动重定向
- **`clientSession`**: 带会话管理的 WebClient自动处理 Cookie
- **`clientNoRedirects`**: 不自动重定向的 WebClient用于需要手动处理重定向的场景
#### 响应处理
- **`asJson(HttpResponse)`**: 将 HTTP 响应转换为 JsonObject自动处理 gzip 压缩和异常
- **`asText(HttpResponse)`**: 将 HTTP 响应转换为文本,自动处理 gzip 压缩
#### Promise 管理
- **`complete(String)`**: 完成 Promise 并返回结果
- **`future()`**: 获取 Promise 的 Future 对象
- **`fail(String, Object...)`**: Promise 失败时记录错误信息
- **`fail(Throwable, String, Object...)`**: Promise 失败时记录错误信息和异常
- **`handleFail(String)`**: 生成失败处理器,用于请求的 onFailure 回调
#### 其他工具
- **`nextParser()`**: 调用下一个解析器,用于通用域名解析转发
- **`getDomainName()`**: 获取域名名称
- **`shareLinkInfo`**: 分享链接信息对象,包含 shareKey、sharePassword 等
- **`log`**: 日志记录器
### WebClient 请求流程
WebClient 是基于 Vert.x 的异步 HTTP 客户端,其请求流程如下:
1. **初始化 Vert.x 实例**
```java
Vertx vertx = Vertx.vertx();
WebClientVertxInit.init(vertx);
```
2. **创建解析器实例**
- 继承 PanBase 的解析器会自动获得配置好的 WebClient 实例
3. **发起异步请求**
```java
client.getAbs("https://api.example.com/endpoint")
.putHeader("User-Agent", "MyParser/1.0")
.send()
.onSuccess(res -> {
// 处理成功响应
JsonObject json = asJson(res);
complete(json.getString("url"));
})
.onFailure(handleFail("请求失败"));
```
4. **请求流程说明**
- **GET 请求**: 使用 `client.getAbs(url).send()`
- **POST 请求**: 使用 `client.postAbs(url).sendJson(body)` 或 `.sendForm(form)`
- **会话请求**: 使用 `clientSession` 自动管理 Cookie
- **禁用重定向**: 使用 `clientNoRedirects` 手动处理 302/301
- **代理支持**: PanBase 构造器会自动处理 shareLinkInfo 中的代理配置
5. **响应处理**
```java
.onSuccess(res -> {
// 检查状态码
if (res.statusCode() != 200) {
fail("请求失败,状态码:" + res.statusCode());
return;
}
// 解析 JSON 响应
JsonObject json = asJson(res);
// 或解析文本响应
String text = asText(res);
// 完成 Promise
complete(result);
})
.onFailure(handleFail("网络请求异常"));
```
6. **错误处理**
- 使用 `fail()` 方法标记解析失败
- 使用 `handleFail()` 生成统一的失败处理器
- 所有异常会自动记录到日志
### 步骤3: 注册自定义解析器
在应用启动时注册你的解析器:
@@ -173,10 +256,10 @@ public class Example {
- 注册时会自动检查冲突
### 2. 构造器要求
自定义解析器类必须提供 `ShareLinkInfo` 单参构造器:
自定义解析器类必须提供 `ShareLinkInfo` 单参构造器,并调用父类构造器
```java
public MyCustomPanTool(ShareLinkInfo shareLinkInfo) {
this.shareLinkInfo = shareLinkInfo;
super(shareLinkInfo);
}
```
@@ -230,8 +313,8 @@ import cn.qaiu.parser.CustomParserConfig;
import cn.qaiu.parser.CustomParserRegistry;
import cn.qaiu.parser.IPanTool;
import cn.qaiu.parser.ParserCreate;
import cn.qaiu.parser.PanBase;
import io.vertx.core.Future;
import io.vertx.core.Promise;
public class CompleteExample {
@@ -298,24 +381,31 @@ public class CompleteExample {
}
}
// 自定义解析器实现
static class MyCustomPanTool implements IPanTool {
private final ShareLinkInfo shareLinkInfo;
// 自定义解析器实现(继承 PanBase
static class MyCustomPanTool extends PanBase {
public MyCustomPanTool(ShareLinkInfo shareLinkInfo) {
this.shareLinkInfo = shareLinkInfo;
super(shareLinkInfo);
}
@Override
public Future<String> parse() {
Promise<String> promise = Promise.promise();
// 模拟解析逻辑
// 使用 PanBase 提供的 HTTP 客户端
String shareKey = shareLinkInfo.getShareKey();
String downloadUrl = "https://mypan.com/download/" + shareKey;
promise.complete(downloadUrl);
return promise.future();
client.getAbs("https://mypan.com/api/share/" + shareKey)
.send()
.onSuccess(res -> {
// 使用 asJson 解析响应
var json = asJson(res);
String downloadUrl = json.getString("download_url");
// 使用 complete 完成 Promise
complete(downloadUrl);
})
.onFailure(handleFail("获取下载链接失败"));
return future();
}
}
}