调用服务端 API
本文档介绍如何通过 Java SDK,自行构建 API Client、构造 API 请求、最终成功调用服务端 API。你可前往 API 调试台,直接获取指定服务端 API 相关示例代码,然后参考本文档了解调用 API 的全面流程。

步骤一:构建 API Client
通过 SDK 调用飞书开放接口之前,你需要先在代码中创建一个 API Client。该 API Client 支持指定当前使用的应用信息、日志级别、HTTP 请求超时时间等基本信息。以下为支持的配置项及其具体含义。
Client client=Client.newBuilder("appId","appSecret") // 默认配置为自建应用
.marketplaceApp() // 设置应用类型为商店应用
.openBaseUrl(BaseUrlEnum.FeiShu) // 设置域名,默认为飞书
.helpDeskCredential("helpDeskId","helpDeskSecret") // 服务台应用才需要设置
.requestTimeout(3,TimeUnit.SECONDS) // 设置httpclient 超时时间,默认永不超时
.logReqAtDebug(true) // 在 debug 模式下会打印 http 请求和响应的 headers、body 等信息。
.build();| 配置选项 | 配置方式 | 是否必填 | 描述 |
|---|---|---|---|
| app_id 和 app_secret | Client.newBuilder("appId","appSecret") | 是 | 应用凭证 App ID 和 App Secret。可在开发者后台 > 应用详情页 > 凭证与基础信息 > 应用凭证 区域获取。![]() |
| appType | client.marketplaceApp() | 否 | 设置 App 类型为商店应用。如果你是 ISV 开发者,则必须设置该选项。关于商店应用的开发指南,可参见 ISV(商店应用)开发指南-oapi-sdk-java。 |
| logReqAtDebug | client.logReqAtDebug(boolean logReqAtDebug) | 否 | 设置是否开启 HTTP 请求参数和响应参数的日志打印开关。开启后,在 debug 模式下会打印 HTTP 请求和响应的 headers、body 等信息。在排查问题时开启该选项,有利于问题的排查。 |
| BaseUrl | client.openBaseUrl(BaseUrlEnum baseUrl) | 否 | 设置飞书域名,默认为 FeishuBaseUrl。可用域名如下: java public enum BaseUrlEnum `{ FeiShu("https://open.feishu.cn"), LarkSuite("https://open.larksuite.com"), ; }` |
| tokenCache | client.tokenCache(ICache cache) | 否 | 设置 Token 缓存器,用于缓存 Token 和 appTIcket,默认实现为内存。 java public interface ICache { // 获取缓存值 String get(String key); // 设置缓存值 void set(String key, String value, int expire, TimeUnit timeUnit); } 对于商店应用的开发者而言,如果需要 SDK 来缓存 appTicket,则需要实现该接口,以提供分布式缓存。 |
| disableTokenCache | client.disableTokenCache() | 否 | 设置是否开启 TenantAccessToken (应用访问凭证)的自动获取与缓存。 若配置该选项,表示关闭自动获取与缓存 TenantAccessToken;若不配置则为开启。 |
| helpDeskId、helpDeskToken | client.helpDeskCredential(String helpDeskId, String helpDeskToken) | 否 | 服务台的 ID 和 token。仅在调用服务台业务的 API 时需要配置。可在服务台管理后台设置中心 > API 凭证 处获取,详情参见 服务台接入指南。 注意:服务台的 ID、Token 只有服务台创建者可以查看到。![]() |
| requestTimeout | client.requestTimeout(long timeout, TimeUnit timeUnit) | 否 | 设置 SDK 内置的 Http Client 的请求超时时间。默认为 0 表示永不超时。 |
| httpTransport | client.httpTransport(IHttpTransport httpTransport) | 否 | 设置传输层实现,用于替换 SDK 提供的默认实现。你可通过实现下面的 IHttpTransport 接口来设置自定义的传输实现: java public interface IHttpTransport { RawResponse execute(RawRequest request) throws Exception; } 目前提供了以下两种实现: - 基于 OKhttp 的实现,使用方式参见示例代码。 - 基于 Apache HttpClient 的实现,使用方式参见示例代码。 |
示例配置:
对于自建应用,使用以下代码创建 API Client。
javaClient client=Client.newBuilder("appId","appSecret").build(); // 默认配置为自建应用对于商店应用,需在创建 API Client 时,使用
marketplaceApp()方法指定 AppType 为商店应用,代码配置如下。了解更多可参考 ISV(商店应用)开发指南-oapi-sdk-java。javaClient client = Client.newBuilder("appId", "appSecret") .marketplaceApp() // 设置应用为商店应用 .build();
步骤二:构造 API 请求
在项目内创建好一个 API Client 后,即可开始调用飞书开放平台接口。SDK 使用 client. 业务域.版本.资源 .方法名称 来定位具体的 API 方法。如下图示例,你可前往 API 调试台,选择指定 API,在示例代码处直接获取 API 方法。

如下代码示例,你可通过 client 调用文档资源的 create 方法,创建一个文档。
Note 该示例需要你在开发者后台为应用开通[创建及编辑新版文档]或[创建新版文档]权限,否则接口将报 99991672 错误码。
import com.lark.oapi.Client;
import com.lark.oapi.core.utils.Jsons;
import com.lark.oapi.service.docx.v1.model.CreateDocumentReq;
import com.lark.oapi.service.docx.v1.model.CreateDocumentReqBody;
import com.lark.oapi.service.docx.v1.model.CreateDocumentResp;
public class DocxSample {
public static void main(String arg[]) throws Exception {
// 构建client
Client client = Client.newBuilder("appId", "appSecret").build();
// 发起请求
CreateDocumentResp resp = client.docx().document()
.create(CreateDocumentReq.newBuilder()
.createDocumentReqBody(CreateDocumentReqBody.newBuilder()
.title("title") // 文档标题
.folderToken("") // 文件夹 token,传空表示在根目录创建文档
.build())
.build()
);
// 处理服务端错误
if (!resp.success()) {
System.out.println(String.format("code:%s,msg:%s,reqId:%s"
, resp.getCode(), resp.getMsg(), resp.getRequestId()));
return;
}
// 业务数据处理
System.out.println(Jsons.DEFAULT.toJson(resp.getData()));
}
}其他 API 调用示例请参考 GitHub 代码仓库中的 Im Java 示例。
(可选)步骤三:设置请求选项
在每次发起 API 调用时,你可以设置请求级别的相关参数,例如传递 userAccessToken(用户访问凭证)、自定义 headers 等。所有请求级别可设置的选项如下表所示。
| 配置选项 | 配置方式 | 描述 |
|---|---|---|
| headers | requestOptions.headers(Map<String, List<String>> headers) | 设置自定义请求头。在发起请求时,这些请求头会被透传到飞书开放平台服务端。 |
| userAccessToken | requestOptions.userAccessToken(String userAccessToken) | 设置用户 token,当你需要以用户身份发起 API 调用时,需要设置该选项的值。 |
| tenantAccessToken | requestOptions.tenantAccessToken(String tenantAccessToken) | 设置企业或组织 token,当你自己维护企业或组织 token 时(即创建 client 时 EnableTokenCache 设置为 false),需通过该选项传递企业或组织 token。 |
| tenantKey | requestOptions.tenantKey(tenantKey string) | 设置企业或组织 key, 当你开发商店应用时,必须设置该选项。 |
| requestId | requestOptions.requestId(requestId string) | 设置请求 ID,作为请求的唯一标识。该 ID 会被透传到飞书开放平台服务端。 |
设置自定义请求头的示例代码如下所示。
import com.lark.oapi.Client;
import com.lark.oapi.core.request.RequestOptions;
import com.lark.oapi.core.utils.Jsons;
import com.lark.oapi.core.utils.Lists;
import com.lark.oapi.service.docx.v1.model.CreateDocumentReq;
import com.lark.oapi.service.docx.v1.model.CreateDocumentReqBody;
import com.lark.oapi.service.docx.v1.model.CreateDocumentResp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DocxSample {
public static void main(String arg[]) throws Exception {
// 创建 API Client。你需在此传入你的应用的实际 App ID 和 App Secret
Client client = Client.newBuilder("appId", "appSecret").build();
// 设置自定义请求头
Map<String, List<String>> headers = new HashMap<>();
headers.put("key1", Lists.newArrayList("value1"));
headers.put("key2", Lists.newArrayList("value2"));
// 发起请求
CreateDocumentResp resp = client.docx().document()
.create(CreateDocumentReq.newBuilder()
.createDocumentReqBody(CreateDocumentReqBody.newBuilder()
.title("title") // 文档标题
.folderToken("") // 文件夹 token,传空表示在根目录创建文档
.build())
.build()
, RequestOptions.newBuilder()
.userAccessToken("u-2GxFH7ysh8E9lj9UJp8XAG0k0gh1h5KzM800khEw2G6e") // 传递用户token
.headers(headers) // 传递自定义请求头
.build());
// 处理服务端错误
if (!resp.success()) {
System.out.println(String.format("code:%s,msg:%s,reqId:%s"
, resp.getCode(), resp.getMsg(), resp.getRequestId()));
return;
}
// 业务数据处理
System.out.println(Jsons.DEFAULT.toJson(resp.getData()));
}
}步骤四:运行代码
完成以上步骤后,即可运行代码调用创建文档 API。若请求成功,预计将返回以下数据。若失败,将返回错误码、错误信息和 Log ID,你可前往开发文档搜索解决方案。
{
Document: {
DocumentId: "IPI4dqnbfoPxL3xhAEhcjXabcef",
RevisionId: 1,
Title: "title"
}
}常见问题
如何调用历史版本 API ?
服务端 API 中存在部分历史版本的开放接口,由于没有元数据信息,所以不能使用 SDK 内封装好的方法快速调用,此时你可以使用 SDK 提供的原生模式调用 API。以发送消息接口为例,调用示例如下所示:
package com.lark.oapi.sample.rawapi;
import com.lark.oapi.Client;
import com.lark.oapi.core.enums.AppType;
import com.lark.oapi.core.response.RawResponse;
import com.lark.oapi.core.token.AccessTokenType;
import com.lark.oapi.core.utils.Jsons;
import java.util.HashMap;
import java.util.Map;
/**
* 原生http 调用方式
*/
public class RawApiCall {
public static void main(String arg[]) throws Exception {
// 构建client
Client client = Client.newBuilder("appId", "appSecret").build();
// 构建http body
Map<String, Object> body = new HashMap<>();
body.put("receive_id", "ou_c245b0a7dff2725cfa2fb104f8b48b9d");
body.put("content", MessageText.newBuilder()
.atUser("ou_155184d1e73cbfb8973e5a9e698e74f2", "Tom")
.text("test content")
.build());
body.put("msg_type", MsgTypeEnum.MSG_TYPE_TEXT);
// 发起请求
RawResponse resp = client.post(
"https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=open_id"
, body
, AccessTokenType.Tenant);
// 处理结果
System.out.println(resp.getStatusCode());
System.out.println(Jsons.DEFAULT.toJson(resp.getHeaders()));
System.out.println(new String(resp.getBody()));
System.out.println(resp.getRequestID());
}
}了解更多 API 调用示例,参考 GitHub 代码仓库中的 RawApiCall Java 示例。
如何快速获取接口对应的示例代码?
飞书开放平台提供了 API 调试台,通过该平台可以快速调试服务端 API,快速获取资源 ID 及生成多语言示例代码的能力,为您节省开发成本。例如,通过 API 调试台调用 发送消息 接口,在调试台成功完成测试后,可通过 示例代码 页面查阅 Java SDK 对应的接口调用代码。

如何准确选择 API?
使用 API Client 调用 API 时,对应的方法建议你借助 API 调试台获取,可通过指定接口的地址栏参数拼接方法,也可以直接参考接口提供的示例代码。以通过手机号或邮箱获取用户 ID 接口为例,获取方式如下图所示。

开放接口的 HTTP GET 请求需要携带请求体 Body 参数,如何传参?
由于默认的客户端实现(OkHttp3Client)不支持这种方式,因此你需要切换成 ApacheHttpClient。参考以下代码:
Client.newBuilder(appId, appSecret)
.httpTransport(
ApacheHttpClientTransport.newBuilder().httpclient(HttpClients.createDefault()).build()
)接口超时并报错 ClientTimeoutException,如何解决?
该报错是因为构建 API Client 时未配置超时时间引起的,你需要在 Client 内配置超时时间,参考如下代码配置:
@Test
void init() {
Client client = Client.newBuilder("appId", "appSecret")
.httpTransport(new OkHttpTransport(
new OkHttpClient().newBuilder()
.readTimeout(3, TimeUnit.MINUTES) // 设置超时时间,单位必须为分钟
.callTimeout(3, TimeUnit.MINUTES) // 设置超时时间,单位必须为分钟
.build()
))
.tokenCache(LocalCache.getInstance()) // 默认实现,本地带时间过期的缓存;可以自己实现ICache的接口,例如Redis缓存等
.logReqAtDebug(true) // 在 debug 模式下会打印 http 请求和响应的 headers,body 等信息。
.build();
}示例代码运行后,Client 正常发起请求并返回响应结果,但程序仍然一直运行了一段时间才自动停止是什么原因?
在使用 OkHttp 作为 HTTP 客户端库时,OkHttp 会在内部维护一个连接池(Connection Pool),用于复用已经建立的 HTTP 连接,以提高性能。连接池中的连接有 5 分钟的存活时间(TTL),进程可能不会立即结束,而是会保持活跃一段时间,直到所有连接的 TTL 到期或被手动关闭。

如果希望进程立即结束,可以通过设置 Connection: close 请求头来禁用 OkHttp 的连接复用能力,但该方式会导致网络性能下降,请谨慎操作。

MessageCardElement 缺少 tag 为 table 的实现类。
回答:
Java SDK 中的 MessageCardElement 类已停止维护。建议使用 飞书卡片搭建工具 设计卡片,并导出 JSON 模板,直接通过 JSON 字符串或 Map 结构构建卡片内容。
com.lark.oapi.core.exception.IllegalAccessTokenTypeException: null 错误
回答:
先核查接口是否仅支持 UserAccessToken;若为仅支持的类型,需进一步检查调用接口时是否已传入该 Token,未传入则补充传入即可。
本地启动长连接后,没有打印 "connected to wss://xxxxx"信息,也没有其他报错。
回答:
检查 logback.xml 配置文件,确认是否关闭了 oapi 包的日志打印
上传审批文件示例代码报错,或不知道如何构造请求?
参考文档:上传审批文件
正确的示例代码:
FormData formData = new FormData();
formData.addField("name", file.getName());
formData.addField("type", "attachment");
FormDataFile formDataFile = new FormDataFile();
formDataFile.setFieldName("content");
formDataFile.setFile(file);
formData.addFile("content", formDataFile);ERROR com.lark.oapi.ws.Listener [Listener.java:83] - java.io.EOFException错误
回答:
该错误通常由网络连接中断或服务端主动断开连接导致。SDK 内部已实现自动重连机制,偶发该错误可忽略;如频繁出现,请检查服务器网络稳定性。
批量请求接口超时、client time out、SocketTimeoutException: timeout、java.io.IOException: Canceled:
- 批量接口超时示例:

- Client time out示例:
com.lark.oapi.core.exception.ClientTimeoutException: client time out
at com.lark.oapi.core.Transport.send(Transport.java:189)
at com.lark.oapi.service.contact.v3.resource.User.batchGetId(User.java:162)- SocketTimeoutException: timeout 示例:
原因:
OkHttp 中 CallTimeout 未手动设置时理论上无超时限制,但实际请求的connect(连接)、read(读取)、write(写入) 各阶段均有默认超时时间(均为 10s);服务端接口调用的超时问题,一般均发生在 readtimeout(读取)阶段,可通过以下方式调整各阶段超时配置:
import com.lark.oapi.Client;
import com.lark.oapi.core.cache.LocalCache;
import com.lark.oapi.core.httpclient.OkHttpTransport;
import com.lark.oapi.okhttp.OkHttpClient;
import java.net.Proxy;
import java.util.concurrent.TimeUnit;
@Test
void init() {
Client client = Client.newBuilder("appId", "appSecret")
.httpTransport(
new OkHttpTransport(
new OkHttpClient().newBuilder()
.readTimeout(3, TimeUnit.MINUTES) // 等待服务端返回响应数据阶段的超时设置
.callTimeout(3, TimeUnit.MINUTES) // 从请求发起到响应完全接收的全生命周期的超时时间设置
.build()
))
.tokenCache(LocalCache.getInstance()) // 默认实现,本地带时间过期的缓存;可以自己实现ICache的接口,例如Redis缓存等
.logReqAtDebug(true) // 在 debug 模式下会打印 http 请求和响应的 headers,body 等信息。
.build();
}下载素材接口 Java 示例代码运行报错:java.lang.IllegalArgumentException: The result returned by the server is illegal
参考文档:下载素材
示例代码:
public static void main(String arg[]) throws Exception {
Client client = Client.newBuilder("xxx", "xxx").build();
DownloadMediaReq req = DownloadMediaReq.newBuilder()
.fileToken("xxx")
.extra("%7B%22bitablePerm%22%3A%7B%22tableId%22%3A%22tblQ7MgglSH9q3NE%22%2C%22rev%22%3A5%7D%7D")
.build();
DownloadMediaResp resp = client.drive().v1().media().download(req);
if(!resp.success()) {
System.out.println(String.format("code:%s,msg:%s,reqId:%s, resp:%s",
resp.getCode(), resp.getMsg(), resp.getRequestId(), Jsons.createGSON(true, false).toJson(JsonParser.parseString(new String(resp.getRawResponse().getBody(), StandardCharsets.UTF_8)))));
return;
}
// 业务数据处理
resp.writeFile("c:/filepath/filename");
}原因:
用户传入的 extra 为已编码的参数,但 Java SDK 内部会对 extra 字段值自动执行一次编码操作,导致该参数被重复编码,最终引发参数传入异常。
解决方式:
传入 extra 参数时,请直接使用编码前的原始参数,示例传入方式如下:
DownloadMediaReq req = DownloadMediaReq.newBuilder()
.fileToken("xxxx")
.extra("{\"bitablePerm\":{\"tableId\":\"xxxx\",\"rev\":5}}")
.build();

