| 订阅方式 | 介绍 |
|---|---|
| 使用长连接接收回调 | 该方式是飞书 SDK 内提供的能力,你可以通过集成飞书 SDK 与开放平台建立一条 WebSocket 全双工通道(你的服务器需要能够访问公网)。后续当应用订阅的回调发生时,开放平台会通过该通道向你的服务器发送消息。相较于传统的 Webhook 模式,长连接模式大大降低了接入成本,将原先 1 周左右的开发周期降低到 5 分钟。具体优势如下:测试阶段无需使用内网穿透工具,通过长连接模式在本地开发环境中即可接收回调。SDK 内封装了鉴权逻辑,只在建连时进行鉴权,后续回调推送均为明文数据,无需再处理解密和验签逻辑。只需保证运行环境具备访问公网能力即可,无需提供公网 IP 或域名。无需部署防火墙和配置白名单。 |
| 将回调发送至开发者服务器 | 传统的 Webhook 模式,该方式需要你提供用于接收回调消息的服务器公网地址。后续当应用订阅的回调发生时,开放平台会向服务器的公网地址发送 HTTP POST 请求,请求内包含回调数据。 |

{
"behaviors": [
{ // 声明交互类型是卡片回传交互。
"type": "callback",
"value": {
// 回传交互数据。开放平台 SDK 仅支持对象类型的卡片回传参数。
"key": "value"
}
}
]
}
package com.lark.oapi.sample.ws;
import com.lark.oapi.core.request.EventReq;
import com.lark.oapi.core.utils.Jsons;
import com.lark.oapi.event.CustomEventHandler;
import com.lark.oapi.event.EventDispatcher;
import com.lark.oapi.event.cardcallback.P2CardActionTriggerHandler;
import com.lark.oapi.event.cardcallback.P2URLPreviewGetHandler;
import com.lark.oapi.event.cardcallback.model.*;
import com.lark.oapi.service.im.ImService;
import com.lark.oapi.service.im.v1.model.P2MessageReceiveV1;
import com.lark.oapi.ws.Client;
import java.nio.charset.StandardCharsets;
public class Sample {
private static final EventDispatcher EVENT_HANDLER = EventDispatcher.newBuilder("", "") // 长连接不需要这两个参数,请保持空字符串
// 监听「卡片回传交互 card.action.trigger」
.onP2CardActionTrigger(new P2CardActionTriggerHandler() {
@Override
public P2CardActionTriggerResponse handle(P2CardActionTrigger event) throws Exception {
System.out.printf("[ P2CardActionTrigger access ], data: %s\n", Jsons.DEFAULT.toJson(event.getEvent()));
P2CardActionTriggerResponse resp = new P2CardActionTriggerResponse();
CallBackToast toast = new CallBackToast();
toast.setType("info");
toast.setContent("卡片交互成功 from Java SDk");
resp.setToast(toast);
return resp;
}
})
// 监听「拉取链接预览数据 url.preview.get」
.onP2URLPreviewGet(new P2URLPreviewGetHandler() {
@Override
public P2URLPreviewGetResponse handle(P2URLPreviewGet event) throws Exception {
System.out.printf("[ P2URLPreviewGet access ], data: %s\n", Jsons.DEFAULT.toJson(event.getEvent()));
P2URLPreviewGetResponse resp = new P2URLPreviewGetResponse();
URLPreviewGetInline inline = new URLPreviewGetInline();
inline.setTitle("链接预览测试fromJavaSDK");
resp.setInline(inline);
return resp;
}
})
.build();
public static void main(String[] args) {
Client client = new Client.Builder("", "")
.eventHandler(EVENT_HANDLER)
.build();
client.start();
}
}


<dependency>
<artifactId>oapi-sdk-servlet-ext</artifactId>
<groupId>com.larksuite.oapi</groupId>
<version>1.0.0-rc3</version>
<exclusions>
<exclusion>
<artifactId>oapi-sdk</artifactId>
<groupId>com.larksuite.oapi</groupId>
</exclusion>
</exclusions>
</dependency>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();
}
}
import com.lark.oapi.core.utils.Jsons;
import com.lark.oapi.event.EventDispatcher;
import com.lark.oapi.event.cardcallback.P2CardActionTriggerHandler;
import com.lark.oapi.event.cardcallback.P2URLPreviewGetHandler;
import com.lark.oapi.event.cardcallback.model.*;
import com.lark.oapi.sdk.servlet.ext.ServletAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@RestController
public class EventController {
//1. 注册消息处理器
private final EventDispatcher EVENT_DISPATCHER = EventDispatcher.newBuilder("", "")
.onP2CardActionTrigger(new P2CardActionTriggerHandler() {
// 监听「卡片回传交互 card.action.trigger」
@Override
public P2CardActionTriggerResponse handle(P2CardActionTrigger event) throws Exception {
System.out.printf("[ P2CardActionTrigger access ], data: %s\n", Jsons.DEFAULT.toJson(event.getEvent()));
P2CardActionTriggerResponse resp = new P2CardActionTriggerResponse();
CallBackToast toast = new CallBackToast();
toast.setType("info");
toast.setContent("卡片交互成功 from Java SDk");
resp.setToast(toast);
return resp;
}
})
// 监听「拉取链接预览数据 url.preview.get」
.onP2URLPreviewGet(new P2URLPreviewGetHandler() {
@Override
public P2URLPreviewGetResponse handle(P2URLPreviewGet event) throws Exception {
System.out.printf("[ P2URLPreviewGet access ], data: %s\n", Jsons.DEFAULT.toJson(event.getEvent()));
P2URLPreviewGetResponse resp = new P2URLPreviewGetResponse();
URLPreviewGetInline inline = new URLPreviewGetInline();
inline.setTitle("链接预览测试fromJavaSDK");
resp.setInline(inline);
return resp;
}
})
.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);
}
}
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);
}
}
import com.lark.oapi.core.utils.Jsons;
import com.lark.oapi.event.EventDispatcher;
import com.lark.oapi.event.cardcallback.P2CardActionTriggerHandler;
import com.lark.oapi.event.cardcallback.P2URLPreviewGetHandler;
import com.lark.oapi.event.cardcallback.model.*;
import com.lark.oapi.sdk.servlet.ext.ServletAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class EventController {
//1. 注册消息处理器
private final EventDispatcher EVENT_DISPATCHER = EventDispatcher.newBuilder("", "")
.onP2CardActionTrigger(new P2CardActionTriggerHandler() {
// 监听「卡片回传交互 card.action.trigger」
@Override
public P2CardActionTriggerResponse handle(P2CardActionTrigger event) throws Exception {
System.out.printf("[ P2CardActionTrigger access ], data: %s\n", Jsons.DEFAULT.toJson(event.getEvent()));
P2CardActionTriggerResponse resp = new P2CardActionTriggerResponse();
CallBackToast toast = new CallBackToast();
toast.setType("info");
toast.setContent("卡片交互成功 from Java SDk");
CallBackCard card = new CallBackCard();
Map<String, Object> cardData = new HashMap<>();
// Config map
Map<String, Object> configMap = new HashMap<>();
configMap.put("enable_forward", true);
// Text map inside elements
Map<String, String> textMap = new HashMap<>();
textMap.put("content", "This is the plain text");
textMap.put("tag", "plain_text");
// Element map
Map<String, Object> elementMap = new HashMap<>();
elementMap.put("tag", "div");
elementMap.put("text", textMap);
// Elements list
List<Map<String, Object>> elementsList = new ArrayList<>();
elementsList.add(elementMap);
// Title map inside header
Map<String, String> titleMap = new HashMap<>();
titleMap.put("content", "This is the title");
titleMap.put("tag", "plain_text");
// Header map
Map<String, Object> headerMap = new HashMap<>();
headerMap.put("template", "blue");
headerMap.put("title", titleMap);
// Putting everything into the main map
cardData.put("config", configMap);
cardData.put("elements", elementsList);
cardData.put("header", headerMap);
card.setType("raw");
card.setData(cardData); // 这里的data必须是一个对象,不是序列化后的字符串
resp.setToast(toast);
resp.setCard(card);
return resp;
}
})
// 监听「拉取链接预览数据 url.preview.get」
.onP2URLPreviewGet(new P2URLPreviewGetHandler() {
@Override
public P2URLPreviewGetResponse handle(P2URLPreviewGet event) throws Exception {
System.out.printf("[ P2URLPreviewGet access ], data: %s\n", Jsons.DEFAULT.toJson(event.getEvent()));
P2URLPreviewGetResponse resp = new P2URLPreviewGetResponse();
URLPreviewGetInline inline = new URLPreviewGetInline();
inline.setTitle("链接预览测试fromJavaSDK");
resp.setInline(inline);
return resp;
}
})
.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);
}
}
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);
}
}