为简化开发者接入飞书开放平台的操作步骤,我们提供了服务端 SDK,开发者可使用 SDK,快捷地开发功能。
运行环境:JDK 1.8及以上
最新版本 maven 坐标
-
com.larksuite.oapi -
oapi-sdk -
2.0.2-rc7
开发者在调用 API 前,需要先创建一个 API Client,然后才可以基于 API Client 发起 API 调用。
对于自建应用,可使用下面代码来创建一个 API Client
- // 默认配置为自建应用
- Client client=Client.newBuilder("appId","appSecret").build();
对于商店应用,需在创建 API Client 时,使用 marketplaceApp() 方法指定 AppType 为商店应用
- Client client = Client.newBuilder("appId", "appSecret")
- .marketplaceApp() // 设置App为商店应用
- .build();
创建 API Client 时,可对 API Client 进行一定的配置,比如我们可以在创建 API Client 时设置日志级别、设置 http 请求超时时间等等:
- Client client=Client.newBuilder("appId","appSecret")
- .marketplaceApp() // 设置 app 类型为商店应用
- .openBaseUrl(BaseUrlEnum.FeiShu) // 设置域名,默认为飞书
- .helpDeskCredential("helpDeskId","helpDeskSecret") // 服务台应用才需要设置
- .requestTimeout(3,TimeUnit.SECONDS) // 设置httpclient 超时时间,默认永不超时
- .disableTokenCache() // 禁用token管理,禁用后需要开发者自己传递token
- .logReqAtDebug(true) // 在 debug 模式下会打印 http 请求和响应的 headers,body 等信息。
- .build();
每个配置选项的具体含义,如下表格:
创建完毕 API Client,我们可以使用 Client.业务域.资源.方法名称
来定位具体的 API 方法,然后对具体的 API 发起调用。
飞书开放平台开放的所有 API 列表,可点击这里查看
如下示例我们通过 client 调用文档业务的 Create 方法,创建一个文档:
- 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("fldcniHf40Vcv1DoEc8SXeuA0Zd")
- .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 调用示例:ImSample.java
开发者在每次发起 API 调用时,可以设置请求级别的一些参数,比如传递 userAccessToken ,自定义 headers 等:
- 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 {
- // 构建client
- Client client = Client.newBuilder("appId", "appSecret").build();
-
- // 创建自定义 Headers
- Map
> 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("fldcniHf40Vcv1DoEc8SXeuA0Zd")
- .build())
- .build()
- , RequestOptions.newBuilder()
- .userAccessToken("u-2GxFH7ysh8E9lj9UJp8XAG0k0gh1h5KzM800khEw2G6e") // 传递用户token
- .headers(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()));
- }
- }
如上使用 RequestOptions 的 Builder 模式构建请求级别的参数。如下表格,展示了所有请求级别可设置的选项:
有些老版本的开放接口,不能生成结构化的 API, 导致 SDK 内无法提供结构化的使用方式,这时可使用原生模式进行调用:
- 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
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 调用示例:RawApiCall.java
关于消息订阅相关的知识,可以点击这里查看
飞书开放平台开放的所有事件列表,可点击这里查看
要处理消息事件,开发者需要启动一个 Web 服务,然后把 Web 服务的 URL 注册到飞书开放平台。飞书开放平台则把事件推送到开发者配置的 URL地址。
在 Java 中,比如常见的 Tomcat 容器、Jboss 容器是基于 Servlet 技术栈实现的; 为方便开发者集成这两种常用的 Web 技术栈实现的 Web 服务,飞书开放平台提供了集成方案。
本节我们介绍,如何集成基于 Servlet 技术栈实现的 SpringBoot Web 框架。
要想把 SDK 集成已有 SpringBoot 框架,开发者需要引入集成包 oapi-sdk-java-ext
需在项目 pom 文件中引入下面 maven 坐标
-
oapi-sdk-servlet-ext -
com.larksuite.oapi -
1.0.0-rc2 -
-
-
oapi-sdk -
com.larksuite.oapi -
-
注入 ServletAdapter 实例到 IOC 容器
- import com.lark.oapi.sdk.servlet.ext.ServletAdapter;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.context.annotation.Bean;
-
- @SpringBootApplication
- public class AppStartup {
-
- public static void main(String[] args) {
- SpringApplication.run(AppStartup.class, args);
- }
-
- // 注入扩展实例到 IOC 容器
- @Bean
- public ServletAdapter getServletAdapter() {
- return new ServletAdapter();
- }
- }
编写 Controller 注册事件处理器
- import com.lark.oapi.core.utils.Jsons;
- import com.lark.oapi.event.EventDispatcher;
- import com.lark.oapi.service.contact.v3.ContactService;
- import com.lark.oapi.service.contact.v3.model.P2UserCreatedV3;
- import com.lark.oapi.service.im.v1.ImService;
- import com.lark.oapi.service.im.v1.model.P1MessageReadV1;
- import com.lark.oapi.service.im.v1.model.P2MessageReadV1;
- import com.lark.oapi.service.im.v1.model.P2MessageReceiveV1;
- import com.lark.oapi.sdk.servlet.ext.ServletAdapter;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class EventController {
-
- //1. 注册消息处理器
- private final EventDispatcher EVENT_DISPATCHER = EventDispatcher.newBuilder("verificationToken",
- "encryptKey")
- .onP2MessageReceiveV1(new ImService.P2MessageReceiveV1Handler() {
- @Override
- public void handle(P2MessageReceiveV1 event) {
- System.out.println(Jsons.DEFAULT.toJson(event));
- System.out.println(event.getRequestId());
- }
- }).onP2UserCreatedV3(new ContactService.P2UserCreatedV3Handler() {
- @Override
- public void handle(P2UserCreatedV3 event) {
- System.out.println(Jsons.DEFAULT.toJson(event));
- System.out.println(event.getRequestId());
- }
- })
- .onP2MessageReadV1(new ImService.P2MessageReadV1Handler() {
- @Override
- public void handle(P2MessageReadV1 event) {
- System.out.println(Jsons.DEFAULT.toJson(event));
- System.out.println(event.getRequestId());
- }
- }).onP1MessageReadV1(new ImService.P1MessageReadV1Handler() {
- @Override
- public void handle(P1MessageReadV1 event) {
- System.out.println(Jsons.DEFAULT.toJson(event));
- System.out.println(event.getRequestId());
- }
- })
- .build();
-
- //2. 注入 ServletAdapter 实例
- @Autowired
- private ServletAdapter servletAdapter;
-
- //3. 创建路由处理器
- @RequestMapping("/webhook/event")
- public void event(HttpServletRequest request, HttpServletResponse response)
- throws Throwable {
- //3.1 回调扩展包提供的事件回调处理器
- servletAdapter.handleEvent(request, response, EVENT_DISPATCHER);
- }
- }
其中 EventDispatcher.newBuilder 方法的参数用于签名验证和消息解密使用,默认可以传递为空串;但是如果开发者的应用在 控制台 的【事件订阅】里面开启了加密,则必须传递控制台上提供的值。
需要注意的是注册处理器时,比如使用 onP2MessageReceiveV1 注册接受消息事件回调时,其中的P2为消息协议版本,当前飞书开放平台存在 两种消息协议 ,分别为1.0和2.0。
如下图开发者在注册消息处理器时,需从 事件列表 中查看自己需要的是哪种协议的事件。 如果是1.0的消息协议,则注册处理器时,需要找以onP1xxxx开头的。如果是2.0的消息协议,则注册处理器时,需要找以OnP2xxxx开头的。
更多事件订阅示例:event.java
针对 ISV 开发者,如果想在消息处理器内给对应租户的用户发送消息,则需先从消息事件内获取租户 key,然后使用下面方式调用消息 API 进行消息发送:
- package com.lark.oapi.sample.event;
-
- import com.lark.oapi.core.request.RequestOptions;
- import com.lark.oapi.core.utils.Jsons;
- import com.lark.oapi.event.EventDispatcher;
- import com.lark.oapi.service.im.v1.ImService.P2MessageReceiveV1Handler;
- import com.lark.oapi.service.im.v1.enums.ReceiveIdTypeEnum;
- import com.lark.oapi.service.im.v1.model.CreateMessageReq;
- import com.lark.oapi.service.im.v1.model.CreateMessageReqBody;
- import com.lark.oapi.service.im.v1.model.P2MessageReceiveV1;
- import com.lark.oapi.sdk.servlet.ext.ServletAdapter;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class EventController {
- //1. 注册消息处理器
- private final EventDispatcher EVENT_DISPATCHER = EventDispatcher.newBuilder("", "")
- .onP2MessageReceiveV1(new P2MessageReceiveV1Handler() {
- @Override
- public void handle(P2MessageReceiveV1 event) throws Exception {
- // 处理消息
- System.out.println(Jsons.DEFAULT.toJson(event));
- System.out.println(event.getRequestId());
-
- // 获取租户 key
- String tenantKey = event.getTenantKey();
-
- // 发送请求
- client.im().message().create(CreateMessageReq.newBuilder()
- .receiveIdType(ReceiveIdTypeEnum.OPEN_ID)
- .createMessageReqBody(CreateMessageReqBody.newBuilder()
- .content("text")
- .build())
- .build()
- , RequestOptions.newBuilder()
- .tenantKey(tenantKey)
- .build());
-
- }
- }).build();
-
- //2. 注入 ServletAdapter 实例
- @Autowired
- private ServletAdapter servletAdapter;
-
- //3. 创建路由处理器
- @RequestMapping("/webhook/event")
- public void event(HttpServletRequest request, HttpServletResponse response)
- throws Throwable {
- //3.1 回调扩展包提供的事件回调处理器
- servletAdapter.handleEvent(request, response, EVENT_DISPATCHER);
- }
- }
更多事件订阅示例:event.java
关于卡片行为相关的知识,可点击这里查看
本节我们介绍,如何集成基于 Servlet 技术栈实现的 SpringBoot Web框架。
需在项目 pom 文件中引入下面 maven 坐标
-
oapi-sdk-servlet-ext -
com.larksuite.oapi -
1.0.0-rc2 -
-
-
oapi-sdk -
com.larksuite.oapi -
-
注入 ServletAdapter 实例到 IOC 容器
- import com.lark.oapi.sdk.servlet.ext.ServletAdapter;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.context.annotation.Bean;
-
- @SpringBootApplication
- public class AppStartup {
-
- public static void main(String[] args) {
- SpringApplication.run(AppStartup.class, args);
- }
-
- // 注入扩展实例到 IOC 容器
- @Bean
- public ServletAdapter getServletAdapter() {
- return new ServletAdapter();
- }
- }
编写 Controller 注册卡片行为处理器
- import com.lark.oapi.card.CardActionHandler;
- import com.lark.oapi.card.model.CardAction;
- import com.lark.oapi.core.utils.Jsons;
- import com.lark.oapi.sdk.servlet.ext.ServletAdapter;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class CardActionController {
-
- //1. 注册卡片处理器
- private final CardActionHandler CARD_ACTION_HANDLER = CardActionHandler.newBuilder("v", "e",
- new CardActionHandler.ICardHandler() {
- @Override
- public Object handle(CardAction cardAction) {
- System.out.println(Jsons.DEFAULT.toJson(cardAction));
- System.out.println(cardAction.getRequestId());
- return null;
- }
- }).build();
-
- // 2. 注入 ServletAdapter 示例
- @Autowired
- private ServletAdapter servletAdapter;
-
- //3. 注册服务路由
- @RequestMapping("/webhook/card")
- public void card(HttpServletRequest request, HttpServletResponse response)
- throws Throwable {
- //3.1 回调扩展包卡片行为处理回调
- servletAdapter.handleCardAction(request, response, CARD_ACTION_HANDLER);
- }
- }
如上示例,如果不需要处理器内返回业务结果给飞书服务端,则直接在处理器内返回 null 。
更多卡片行为示例:CardActionController.java
如开发者需要卡片处理器内同步返回用于更新消息卡片的消息体,则可使用下面方法方式进行处理:
- import com.lark.oapi.card.CardActionHandler;
- import com.lark.oapi.card.model.CardAction;
- import com.lark.oapi.card.model.MessageCard;
- import com.lark.oapi.card.model.MessageCardElement;
- import com.lark.oapi.core.utils.Jsons;
- import com.lark.oapi.sdk.servlet.ext.ServletAdapter;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class CardActionController {
-
- //1. 注册卡片处理器
- private final CardActionHandler CARD_ACTION_HANDLER = CardActionHandler.newBuilder("v", "e",
- new CardActionHandler.ICardHandler() {
- @Override
- public Object handle(CardAction cardAction) {
- // 1.1 处理卡片行为
- System.out.println(Jsons.DEFAULT.toJson(cardAction));
- System.out.println(cardAction.getRequestId());
-
- // 1.2 构建响应卡片内容
- MessageCard card = MessageCard.newBuilder()
- .cardLink(cardURL)
- .config(config)
- .header(header)
- .elements(new MessageCardElement[]{div, note, image, cardAction, hr})
- .build();
- return card;
- }
- }).build();
-
- // 2. 注入 ServletAdapter 示例
- @Autowired
- private ServletAdapter servletAdapter;
-
- //3. 注册服务路由
- @RequestMapping("/webhook/card")
- public void card(HttpServletRequest request, HttpServletResponse response)
- throws Throwable {
- //3.1 回调扩展包卡片行为处理回调
- servletAdapter.handleCardAction(request, response, CARD_ACTION_HANDLER);
- }
- }
更多卡片行为示例:CardActionController.java
如开发者需卡片处理器内返回自定义内容,则可以使用下面方式进行处理:
- import com.lark.oapi.card.CardActionHandler;
- import com.lark.oapi.card.model.CardAction;
- import com.lark.oapi.card.model.CustomResponse;
- import com.lark.oapi.core.utils.Jsons;
- import com.lark.oapi.sdk.servlet.ext.ServletAdapter;
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class CardActionController {
-
- //1. 注册卡片处理器
- private final CardActionHandler CARD_ACTION_HANDLER = CardActionHandler.newBuilder("v", "e",
- new CardActionHandler.ICardHandler() {
- @Override
- public Object handle(CardAction cardAction) {
- // 1.1 处理卡片行为
- System.out.println(Jsons.DEFAULT.toJson(cardAction));
- System.out.println(cardAction.getRequestId());
-
- //1.2 返回自定义结果
- Map
map = new HashMap<>(); - map.put("key1", "value1");
- map.put("ke2", "value2");
- CustomResponse customResponse = new CustomResponse();
- customResponse.setStatusCode(0);
- customResponse.setBody(map);
- Map
> headers = new HashMap>(); - headers.put("key1", Arrays.asList("a", "b"));
- headers.put("key2", Arrays.asList("c", "d"));
- customResponse.setHeaders(headers);
- return customResponse;
- }
- }).build();
-
- // 2. 注入 ServletAdapter 示例
- @Autowired
- private ServletAdapter servletAdapter;
-
- //3. 注册服务路由
- @RequestMapping("/webhook/card")
- public void card(HttpServletRequest request, HttpServletResponse response)
- throws Throwable {
- //3.1 回调扩展包卡片行为处理回调
- servletAdapter.handleCardAction(request, response, CARD_ACTION_HANDLER);
- }
- }
更多卡片行为示例:CardActionController.java
针对 ISV 开发者,如果想在卡片行为处理器内给对应租户的用户发送消息,则需先从卡片行为内获取租户 key ,然后使用下面方式调用消息 API 进行消息发送:
- import com.lark.oapi.card.CardActionHandler;
- import com.lark.oapi.card.model.CardAction;
- import com.lark.oapi.card.model.CustomResponse;
- import com.lark.oapi.core.utils.Jsons;
- import com.lark.oapi.sdk.servlet.ext.ServletAdapter;
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class CardActionController {
-
- //1. 注册卡片处理器
- private final CardActionHandler CARD_ACTION_HANDLER = CardActionHandler.newBuilder("v", "e",
- new CardActionHandler.ICardHandler() {
- @Override
- public Object handle(CardAction cardAction) {
- // 1.1 处理卡片行为
- System.out.println(Jsons.DEFAULT.toJson(cardAction));
- System.out.println(cardAction.getRequestId());
-
- // 1.2 获取租户 key
- String tenantKey = cardAction.getTenantKey();
- // 发送请求
- client.im().message().create(CreateMessageReq.newBuilder()
- .receiveIdType(ReceiveIdTypeEnum.OPEN_ID)
- .createMessageReqBody(CreateMessageReqBody.newBuilder()
- .content("text")
- .build())
- .build()
- , RequestOptions.newBuilder()
- .tenantKey(tenantKey)
- .build());
-
- return null;
- }
- }).build();
-
- // 2. 注入 ServletAdapter 示例
- @Autowired
- private ServletAdapter servletAdapter;
-
- //3. 注册服务路由
- @RequestMapping("/webhook/card")
- public void card(HttpServletRequest request, HttpServletResponse response)
- throws Throwable {
- //3.1 回调扩展包卡片行为处理回调
- servletAdapter.handleCardAction(request, response, CARD_ACTION_HANDLER);
- }
- }
更多卡片行为示例:CardActionController.java
https://github.com/larksuite/oapi-sdk-java