1
This commit is contained in:
122
doc/腾讯文档API_404问题诊断.md
Normal file
122
doc/腾讯文档API_404问题诊断.md
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# 腾讯文档 API 404 问题诊断与修复
|
||||||
|
|
||||||
|
## 问题现象
|
||||||
|
调用腾讯文档 API 时返回 404 Not Found 错误:
|
||||||
|
```
|
||||||
|
Caused by: java.lang.RuntimeException: 请求被代理拦截,返回了HTML页面。请检查系统代理设置或网络配置。响应: <html><head><title>404 Not Found</title></head><body><center><h1>404 Not Found</h1></center><hr><center>nginx</center></body></html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 可能的原因分析
|
||||||
|
|
||||||
|
### 1. API 基础 URL 可能不正确
|
||||||
|
|
||||||
|
我们当前使用的基础 URL 是:`https://docs.qq.com/openapi/v3`
|
||||||
|
|
||||||
|
但根据腾讯文档API的文档路径结构,可能的正确基础 URL 有以下几种:
|
||||||
|
|
||||||
|
| 候选URL | 说明 |
|
||||||
|
|---------|------|
|
||||||
|
| `https://docs.qq.com/open/api/v3` | 推测1:/open/api/v3 |
|
||||||
|
| `https://docs.qq.com/openapi/v3` | 推测2:/openapi/v3(当前使用)|
|
||||||
|
| `https://docs.qq.com/v3` | 推测3:直接 /v3 |
|
||||||
|
| `https://api.docs.qq.com/v3` | 推测4:使用 api 子域名 |
|
||||||
|
|
||||||
|
### 2. 可能需要使用不同的接口路径
|
||||||
|
|
||||||
|
腾讯文档 V3 API 可能不支持直接的 REST 风格的 ranges 路径,而是使用:
|
||||||
|
- **批量更新接口(batchUpdate)**:用于写入数据
|
||||||
|
- **批量查询接口(getGridData 或类似)**:用于读取数据
|
||||||
|
|
||||||
|
## 诊断步骤
|
||||||
|
|
||||||
|
### 步骤1:测试不同的基础 URL
|
||||||
|
|
||||||
|
建议创建一个测试方法,尝试不同的基础URL:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 测试代码示例
|
||||||
|
public void testApiBaseUrls() {
|
||||||
|
String[] candidateUrls = {
|
||||||
|
"https://docs.qq.com/open/api/v3",
|
||||||
|
"https://docs.qq.com/openapi/v3",
|
||||||
|
"https://docs.qq.com/v3",
|
||||||
|
"https://api.docs.qq.com/v3"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String baseUrl : candidateUrls) {
|
||||||
|
try {
|
||||||
|
String testUrl = baseUrl + "/spreadsheets/{fileId}";
|
||||||
|
log.info("测试URL: {}", testUrl);
|
||||||
|
// 发送GET请求测试
|
||||||
|
JSONObject result = callApi(accessToken, testUrl, "GET", null);
|
||||||
|
log.info("成功!正确的基础URL是: {}", baseUrl);
|
||||||
|
return;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("URL {} 失败: {}", baseUrl, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 步骤2:检查腾讯文档开放平台的实际API文档
|
||||||
|
|
||||||
|
访问以下链接查看实际的API调用示例:
|
||||||
|
1. 腾讯文档开放平台首页:https://docs.qq.com/open/
|
||||||
|
2. 开发文档总览:https://docs.qq.com/open/document/app/
|
||||||
|
3. 查找实际的API调用示例(cURL命令或SDK示例)
|
||||||
|
|
||||||
|
### 步骤3:检查是否需要使用 batchUpdate 接口
|
||||||
|
|
||||||
|
如果直接的 ranges 路径不可用,可能需要使用批量操作接口:
|
||||||
|
|
||||||
|
**读取数据**可能需要使用类似的接口:
|
||||||
|
- POST `/open/api/v3/spreadsheets/{fileId}:getGridData`
|
||||||
|
- 或 POST `/open/api/v3/spreadsheets/{fileId}/values:batchGet`
|
||||||
|
|
||||||
|
**写入数据**可能需要使用:
|
||||||
|
- POST `/open/api/v3/spreadsheets/{fileId}:batchUpdate`
|
||||||
|
- 或 POST `/open/api/v3/spreadsheets/{fileId}/values:batchUpdate`
|
||||||
|
|
||||||
|
## 临时解决方案
|
||||||
|
|
||||||
|
### 方案1:修改基础URL为 /open/api/v3
|
||||||
|
|
||||||
|
尝试修改配置:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# application-dev.yml 和 application-prod.yml
|
||||||
|
tencent:
|
||||||
|
doc:
|
||||||
|
api-base-url: https://docs.qq.com/open/api/v3
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
// TencentDocConfig.java
|
||||||
|
private String apiBaseUrl = "https://docs.qq.com/open/api/v3";
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方案2:联系腾讯文档技术支持
|
||||||
|
|
||||||
|
由于官方文档可能没有详细的API调用示例,建议:
|
||||||
|
1. 在腾讯文档开放平台提交工单
|
||||||
|
2. 咨询实际的API基础URL和调用方式
|
||||||
|
3. 获取完整的API调用示例代码
|
||||||
|
|
||||||
|
## 后续行动
|
||||||
|
|
||||||
|
1. 首先尝试修改基础URL为 `/open/api/v3`
|
||||||
|
2. 如果仍然404,需要查看腾讯文档开放平台控制台的SDK示例或API文档
|
||||||
|
3. 考虑使用腾讯文档提供的官方SDK(如果有)
|
||||||
|
4. 联系腾讯文档技术支持获取准确的API地址
|
||||||
|
|
||||||
|
## 参考信息
|
||||||
|
|
||||||
|
- 文档页面路径:`/open/document/app/openapi/v3/...`(这是文档站点)
|
||||||
|
- 可能的API路径:`/open/api/v3/...`(这可能是实际API)
|
||||||
|
- 当前使用路径:`/openapi/v3/...`(返回404)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**更新时间**:2025-11-05
|
||||||
|
**状态**:待测试验证
|
||||||
|
|
||||||
@@ -199,8 +199,8 @@ tencent:
|
|||||||
redirect-uri: https://jarvis.van333.cn/tendoc-callback
|
redirect-uri: https://jarvis.van333.cn/tendoc-callback
|
||||||
# API基础地址(V3版本 - 2023年推荐使用,V2版本已废弃)
|
# API基础地址(V3版本 - 2023年推荐使用,V2版本已废弃)
|
||||||
# 完整API文档:https://docs.qq.com/open/document/app/openapi/v3/
|
# 完整API文档:https://docs.qq.com/open/document/app/openapi/v3/
|
||||||
# 注意:正确的URL是 /openapi/v3 而不是 /open/v3
|
# 注意:根据文档路径结构,API路径应该是 /open/api/v3
|
||||||
api-base-url: https://docs.qq.com/openapi/v3
|
api-base-url: https://docs.qq.com/open/api/v3
|
||||||
# OAuth授权地址(用于生成授权链接,引导用户授权)
|
# OAuth授权地址(用于生成授权链接,引导用户授权)
|
||||||
oauth-url: https://docs.qq.com/oauth/v2/authorize
|
oauth-url: https://docs.qq.com/oauth/v2/authorize
|
||||||
# 获取Token地址(用于通过授权码换取access_token)
|
# 获取Token地址(用于通过授权码换取access_token)
|
||||||
|
|||||||
@@ -198,8 +198,8 @@ tencent:
|
|||||||
redirect-uri: https://jarvis.van333.cn/tendoc-callback
|
redirect-uri: https://jarvis.van333.cn/tendoc-callback
|
||||||
# API基础地址(V3版本 - 2023年推荐使用,V2版本已废弃)
|
# API基础地址(V3版本 - 2023年推荐使用,V2版本已废弃)
|
||||||
# 完整API文档:https://docs.qq.com/open/document/app/openapi/v3/
|
# 完整API文档:https://docs.qq.com/open/document/app/openapi/v3/
|
||||||
# 注意:正确的URL是 /openapi/v3 而不是 /open/v3
|
# 注意:根据文档路径结构,API路径应该是 /open/api/v3
|
||||||
api-base-url: https://docs.qq.com/openapi/v3
|
api-base-url: https://docs.qq.com/open/api/v3
|
||||||
# OAuth授权地址(用于生成授权链接,引导用户授权)
|
# OAuth授权地址(用于生成授权链接,引导用户授权)
|
||||||
oauth-url: https://docs.qq.com/oauth/v2/authorize
|
oauth-url: https://docs.qq.com/oauth/v2/authorize
|
||||||
# 获取Token地址(用于通过授权码换取access_token)
|
# 获取Token地址(用于通过授权码换取access_token)
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ public class TencentDocConfig {
|
|||||||
/** 授权回调地址 */
|
/** 授权回调地址 */
|
||||||
private String redirectUri;
|
private String redirectUri;
|
||||||
|
|
||||||
/** API基础地址 - V3版本(注意:是 /openapi/v3 不是 /open/v3) */
|
/** API基础地址 - V3版本(注意:根据文档路径推测应该是 /open/api/v3) */
|
||||||
private String apiBaseUrl = "https://docs.qq.com/openapi/v3";
|
private String apiBaseUrl = "https://docs.qq.com/open/api/v3";
|
||||||
|
|
||||||
/** OAuth授权地址 */
|
/** OAuth授权地址 */
|
||||||
private String oauthUrl = "https://docs.qq.com/oauth/v2/authorize";
|
private String oauthUrl = "https://docs.qq.com/oauth/v2/authorize";
|
||||||
|
|||||||
@@ -264,11 +264,11 @@ public class TencentDocApiUtil {
|
|||||||
* @param fileId 文件ID(在线表格的唯一标识)
|
* @param fileId 文件ID(在线表格的唯一标识)
|
||||||
* @param sheetId 工作表ID(可从表格链接中获取,如 ?tab=BB08J2 中的 BB08J2)
|
* @param sheetId 工作表ID(可从表格链接中获取,如 ?tab=BB08J2 中的 BB08J2)
|
||||||
* @param range 范围,例如 "A1:Z100"(行列从0开始,遵循左闭右开原则)
|
* @param range 范围,例如 "A1:Z100"(行列从0开始,遵循左闭右开原则)
|
||||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/openapi/v3)
|
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/open/api/v3)
|
||||||
* @return 表格数据(JSON格式,包含values数组)
|
* @return 表格数据(JSON格式,包含values数组)
|
||||||
*/
|
*/
|
||||||
public static JSONObject readSheetData(String accessToken, String fileId, String sheetId, String range, String apiBaseUrl) {
|
public static JSONObject readSheetData(String accessToken, String fileId, String sheetId, String range, String apiBaseUrl) {
|
||||||
// V3版本API路径格式:/openapi/v3/spreadsheets/{spreadsheetId}/sheets/{sheetId}/ranges/{range}
|
// V3版本API路径格式:/open/api/v3/spreadsheets/{spreadsheetId}/sheets/{sheetId}/ranges/{range}
|
||||||
String apiUrl = String.format("%s/spreadsheets/%s/sheets/%s/ranges/%s", apiBaseUrl, fileId, sheetId, range);
|
String apiUrl = String.format("%s/spreadsheets/%s/sheets/%s/ranges/%s", apiBaseUrl, fileId, sheetId, range);
|
||||||
log.info("读取表格数据 - fileId: {}, sheetId: {}, range: {}, apiUrl: {}", fileId, sheetId, range, apiUrl);
|
log.info("读取表格数据 - fileId: {}, sheetId: {}, range: {}, apiUrl: {}", fileId, sheetId, range, apiUrl);
|
||||||
return callApi(accessToken, apiUrl, "GET", null);
|
return callApi(accessToken, apiUrl, "GET", null);
|
||||||
@@ -284,11 +284,11 @@ public class TencentDocApiUtil {
|
|||||||
* @param values 要写入的数据,支持两种格式:
|
* @param values 要写入的数据,支持两种格式:
|
||||||
* 1. 简单二维数组:[["值1", "值2"], ["值3", "值4"]]
|
* 1. 简单二维数组:[["值1", "值2"], ["值3", "值4"]]
|
||||||
* 2. V3 API完整格式(包含CellData结构)
|
* 2. V3 API完整格式(包含CellData结构)
|
||||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/openapi/v3)
|
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/open/api/v3)
|
||||||
* @return 写入结果
|
* @return 写入结果
|
||||||
*/
|
*/
|
||||||
public static JSONObject writeSheetData(String accessToken, String fileId, String sheetId, String range, Object values, String apiBaseUrl) {
|
public static JSONObject writeSheetData(String accessToken, String fileId, String sheetId, String range, Object values, String apiBaseUrl) {
|
||||||
// V3版本API路径格式:/openapi/v3/spreadsheets/{spreadsheetId}/sheets/{sheetId}/ranges/{range}
|
// V3版本API路径格式:/open/api/v3/spreadsheets/{spreadsheetId}/sheets/{sheetId}/ranges/{range}
|
||||||
String apiUrl = String.format("%s/spreadsheets/%s/sheets/%s/ranges/%s", apiBaseUrl, fileId, sheetId, range);
|
String apiUrl = String.format("%s/spreadsheets/%s/sheets/%s/ranges/%s", apiBaseUrl, fileId, sheetId, range);
|
||||||
|
|
||||||
// 构建V3 API规范的请求体
|
// 构建V3 API规范的请求体
|
||||||
@@ -311,13 +311,13 @@ public class TencentDocApiUtil {
|
|||||||
* @param fileId 文件ID(在线表格的唯一标识)
|
* @param fileId 文件ID(在线表格的唯一标识)
|
||||||
* @param sheetId 工作表ID(可从表格链接中获取,如 ?tab=BB08J2 中的 BB08J2)
|
* @param sheetId 工作表ID(可从表格链接中获取,如 ?tab=BB08J2 中的 BB08J2)
|
||||||
* @param values 要追加的数据,二维数组格式,例如:[["值1", "值2"], ["值3", "值4"]]
|
* @param values 要追加的数据,二维数组格式,例如:[["值1", "值2"], ["值3", "值4"]]
|
||||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/openapi/v3)
|
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/open/api/v3)
|
||||||
* @return 追加结果
|
* @return 追加结果
|
||||||
*/
|
*/
|
||||||
public static JSONObject appendSheetData(String accessToken, String fileId, String sheetId, Object values, String apiBaseUrl) {
|
public static JSONObject appendSheetData(String accessToken, String fileId, String sheetId, Object values, String apiBaseUrl) {
|
||||||
try {
|
try {
|
||||||
// 先获取工作表信息,找到最后一行(V3版本路径)
|
// 先获取工作表信息,找到最后一行(V3版本路径)
|
||||||
// V3版本API路径格式:/openapi/v3/spreadsheets/{spreadsheetId}/sheets/{sheetId}
|
// V3版本API路径格式:/open/api/v3/spreadsheets/{spreadsheetId}/sheets/{sheetId}
|
||||||
String infoUrl = String.format("%s/spreadsheets/%s/sheets/%s", apiBaseUrl, fileId, sheetId);
|
String infoUrl = String.format("%s/spreadsheets/%s/sheets/%s", apiBaseUrl, fileId, sheetId);
|
||||||
log.info("获取工作表信息以追加数据 - apiUrl: {}", infoUrl);
|
log.info("获取工作表信息以追加数据 - apiUrl: {}", infoUrl);
|
||||||
|
|
||||||
@@ -366,12 +366,12 @@ public class TencentDocApiUtil {
|
|||||||
*
|
*
|
||||||
* @param accessToken 访问令牌
|
* @param accessToken 访问令牌
|
||||||
* @param fileId 文件ID(在线表格的唯一标识)
|
* @param fileId 文件ID(在线表格的唯一标识)
|
||||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/openapi/v3)
|
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/open/api/v3)
|
||||||
* @return 文件信息(JSON格式,包含metadata、sheets等信息)
|
* @return 文件信息(JSON格式,包含metadata、sheets等信息)
|
||||||
* 返回格式示例:{ "fileId": "xxx", "metadata": {...}, "sheets": [...] }
|
* 返回格式示例:{ "fileId": "xxx", "metadata": {...}, "sheets": [...] }
|
||||||
*/
|
*/
|
||||||
public static JSONObject getFileInfo(String accessToken, String fileId, String apiBaseUrl) {
|
public static JSONObject getFileInfo(String accessToken, String fileId, String apiBaseUrl) {
|
||||||
// V3版本API路径格式:/openapi/v3/spreadsheets/{spreadsheetId}
|
// V3版本API路径格式:/open/api/v3/spreadsheets/{spreadsheetId}
|
||||||
String apiUrl = String.format("%s/spreadsheets/%s", apiBaseUrl, fileId);
|
String apiUrl = String.format("%s/spreadsheets/%s", apiBaseUrl, fileId);
|
||||||
log.info("获取文件信息 - fileId: {}, apiUrl: {}", fileId, apiUrl);
|
log.info("获取文件信息 - fileId: {}, apiUrl: {}", fileId, apiUrl);
|
||||||
return callApi(accessToken, apiUrl, "GET", null);
|
return callApi(accessToken, apiUrl, "GET", null);
|
||||||
@@ -382,12 +382,12 @@ public class TencentDocApiUtil {
|
|||||||
*
|
*
|
||||||
* @param accessToken 访问令牌
|
* @param accessToken 访问令牌
|
||||||
* @param fileId 文件ID(在线表格的唯一标识)
|
* @param fileId 文件ID(在线表格的唯一标识)
|
||||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/openapi/v3)
|
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/open/api/v3)
|
||||||
* @return 工作表列表(JSON格式,包含所有sheet的properties信息)
|
* @return 工作表列表(JSON格式,包含所有sheet的properties信息)
|
||||||
* 返回格式示例:{ "sheets": [{ "properties": { "sheetId": "xxx", "title": "工作表1", ... } }] }
|
* 返回格式示例:{ "sheets": [{ "properties": { "sheetId": "xxx", "title": "工作表1", ... } }] }
|
||||||
*/
|
*/
|
||||||
public static JSONObject getSheetList(String accessToken, String fileId, String apiBaseUrl) {
|
public static JSONObject getSheetList(String accessToken, String fileId, String apiBaseUrl) {
|
||||||
// V3版本API路径格式:/openapi/v3/spreadsheets/{spreadsheetId}/sheets
|
// V3版本API路径格式:/open/api/v3/spreadsheets/{spreadsheetId}/sheets
|
||||||
String apiUrl = String.format("%s/spreadsheets/%s/sheets", apiBaseUrl, fileId);
|
String apiUrl = String.format("%s/spreadsheets/%s/sheets", apiBaseUrl, fileId);
|
||||||
log.info("获取工作表列表 - fileId: {}, apiUrl: {}", fileId, apiUrl);
|
log.info("获取工作表列表 - fileId: {}, apiUrl: {}", fileId, apiUrl);
|
||||||
return callApi(accessToken, apiUrl, "GET", null);
|
return callApi(accessToken, apiUrl, "GET", null);
|
||||||
|
|||||||
Reference in New Issue
Block a user