====== 回调功能 ====== 更新时间:2022-03-17 回调功能,即环信 IM 服务器会在事件发生之前或之后,向你的应用服务器发送请求,你可以根据业务需求来干预事件的后续处理流程(发送前回调),或据此进行必要的数据同步(发送后回调)。 一般发送前回调是对用户发送的消息内容的处理,发送后回调还包括送达回执和已读回执、群组或聊天室操作、好友关系操作和用户状态变化等事件,具体见 [[https://docs-im.easemob.com/ccim/rest/userstatuscallback|用户在线状态回调]]和发送后回调过滤规则设置。 设置消息内容回调的规则可以在环信即时通讯云控制台实现,如需单独设置特定类型不回调,请联系环信商务经理。 从处理角度看,回调分为以下两类: * 发送前回调:回调的主要目的在于让 app 后台可以干预该事件的处理逻辑,环信 IM 服务器会根据回调返回码和返回值确定后续处理流程; * 发送后回调:回调的主要目的在于让 app 后台实现必要的数据同步,环信 IM 服务器忽略回调返回码。 ===== 1、发送前回调 ===== ==== 技术原理 ==== 应用服务器可以通过发送前回调实时监控用户的聊天消息,包括: * 对聊天消息进行实时同步; * 拦截用户的消息请求。可拦截所有类型的消息,包含文本、图片、自定义消息等; {{https://docs-im.easemob.com/_media/playground/im%E6%96%87%E6%A1%A3.jpg?t=1620890800&w=499&h=190&tok=87e2fe}} ==== 回调发生时间 ==== 环信 IM 服务器收到用户发送的上行单聊和群聊消息之后、将该消息下发给目标用户之前。 ==== 前提条件 ==== 以下是使用发送前回调功能需要了解的前提条件,包括在高并发可用性的基础上的使用限制说明,请按照此限制正确使用回调。 * 回调的方向是环信 IM 服务器向应用服务器发起 HTTP/HTTPS POST 请求。 * 若同时设置了消息发送前和发送后两种回调,且消息发送前回调返回拒绝,则消息发送后回调将不会被触发。 * 发送前回调只对客户端发送的消息有效,不包含 REST API 发送的消息。对同一个 app,可以针对不同类型的消息(“TEXT”,“IMAGE”,“VIDEO”,“LOCATION”,“VOICE”和”FILE”)做配置;规则支持选择两种以上消息类型同时回调至一个指定服务器地址,接收到消息后,你可以区分消息的类型以便进行分类处理(详见 [[https://docs-im.easemob.com/ccim/rest/message|消息管理 REST API]])。 * 环信 IM 服务器执行发送前回调后,如果你的应用服务器没有返回 valid 状态或者返回其他错误码,该条消息会根据默认设置(console 后台发送前回调中”调用失败时默认策略”)处理,不会再重试;后续消息依然正常执行回调。 * 消息发送到你的应用服务器后,应用服务器需返回 HTTP 响应码 200 和 valid 属性,根据 valid 状态决定是否进行下发。 ==== 实现步骤 ==== - 在管理后台开通消息回调服务。 - 请在环信即时通讯云控制台配置开通规则配置。 ^配置说明 ^是否必填 ^内容 ^ |规则名称 |必填 | 填写文字,支持中英文,长度限制为 32 字符。 | |会话类型 |必填 | * 单聊;
* 群组;
* 聊天室。 | |消息类型 |必填 |多选:
文本;图片;视频;位置;语音;文件;自定义消息。 | |等待响应时间 |必填 |后台判断超时时间,默认 200,单位为毫秒。 | |调用失败时默认策略 |必填 | 当您的服务器返回结果异常或等待时间内未返回结果时,消息放行或不放行。 | |消息拦截报错时显示 |必填 | 当消息被拦截时,是否通知发送者 SDK 消息发送失败:
* 报错:通知发送者 SDK 消息发送失败,发送者会感知到消息发送失败;
* 不报错:不通知发送者 SDK 消息发送失败,发送者无感知。 | |启用状态 |必填 |回调规则是否马上生效:
* 启用:马上生效;
* 不启用:暂不生效。
(建议首次创建配置为“不启用”,等您的服务器配置好验证信息后再修改为“启用”) | |回调地址 |必填 |回调 URL,环信 IM 对 HTTP 和 HTTPS 的回调地址均支持。| **规则说明:** * 规则可以有多条,每个规则的名称必须是唯一的。 * 环信 IM 服务器发送前回调应用服务器的超时时间默认为 200 ms,支持自定义“超时时间”。如果回调超时无应答,消息默认会正常下发,支持修改消息处理逻辑。 * 目前支持发送前回调的消息类型包括:文本,图片,视频,位置,语音,文件,自定义消息。
  1. 从环信服务器向你的应用服务器发送请求。
请求采用 POST 方式,支持 HTTP/HTTPS,正文部分为 JSON 格式的字符串,字符集为 UTF-8。 请求 ''%%header%%'' 中包括以下字段: ^字段名 ^值 ^ |''%%Content-Type%%''|''%%application/json%%''| === 请求体参数 === ^参数 ^类型 ^ |''%%callId%%'' |''%%callId%%'' 为 ''%%{appkey}_{uuid}%%'',其中 ''%%uuid%%'' 为随机生成,作为每条回调的唯一标识。 | |''%%eventType%%'' |有两种,''%%chat_offline%%'' 离线消息,''%%chat%%'' 上行消息。 | |''%%timestamp%%'' |环信 IM 服务器接收到此消息的时间戳。 | |''%%chat_type%%'' |''%%chat%%'' 单聊回调、''%%groupchat%%'' 群聊回调包含了群组和聊天室的消息回调,默认全选。 | |''%%group_id%%'' |回调消息所在的群组。群聊消息回调才有此参数。 | |''%%from%%'' |消息的发送方。 | |''%%to%%'' |消息的接收方。 | |''%%msg_id%%'' |消息的 ID。 | |''%%payload%%'' |消息内容,与通过 REST API 发送过来的一致,查看[[https://docs-im.easemob.com/im/server/basics/chatrecord#聊天记录数据结构|消息格式文档]]。| |''%%securityVersion%%''|安全校验版本,目前为 1.0.0。忽略此参数,以后会改成 Console 后台做设置。 | |''%%security%%'' |签名,格式如下: MD5(callId+Secret+timestamp)。Secret 见 [[https://console.easemob.com/user/login|环信即时通讯云控制台]]回调规则。 | === 响应体参数 === ^参数 ^类型 ^是否必需 ^描述 ^ |''%%valid%%'' | bool |是|根据开发者自己服务器设定的规则判断消息是否合法。 | |''%%code%%'' |String |否|自定义信息,字符串类型。 | |''%%payload%%'' | Object |否 |如果需要更改消息内容可以回传修改后的内容,不修改则无需传该内容。格式同传入的消息内容,目前仅支持文本的形式,并且消息大小不能超过 1K。 | === 请求示例 === { "callId":"easemob-demo#test_0990a64f-dp01-6c50-8696-cf3b48b20e7e", "eventType":"chat_offline", "timestamp":1600060847294, "chat_type":"groupchat", "group_id":"16934809238921545", "from":"user1", "to":"user2", "msg_id":"8924312242322", "payload":{ // 具体的消息内容 }, "securityVersion":"1.0.0", "security":"2ca02c394bef9e7abc83958bcc3156d3" } === 响应示例 === { "valid": true, "code": "HX:10000", "payload":{ // 具体的消息内容。 // 仅支持文本类型消息。 }, } ===== 2、发送后回调 ===== ==== 技术原理 ==== 发送后回调经常用在 app 后台需要实现必要的数据同步的场景。比如: * 针对客户消息的内容进行自动回复; * 在你的应用服务器端及时保存聊天历史记录或者离线消息。 注:如果您对聊天消息没有时效性需求,可以直接通过免费的[[https://docs-im.easemob.com/im/server/basics/chatrecord|聊天记录拉取 REST API]] 获取聊天记录,无需使用发送后回调。 {{https://docs-im.easemob.com/_media/im/extensions/value/%E6%B6%88%E6%81%AF%E5%9B%9E%E8%B0%83.png?cache=}} ==== 前提条件 ==== 以下是需要了解的发送后回调功能说明,包括在高并发可用性的基础上的使用限制说明,请按照此限制正确使用回调。 * 消息回调属于增值服务,需要开通相应版本后才能使用,具体见 [[https://www.easemob.com/pricing/im|环信即时通讯 IM 价格]]。 * 消息回调规则设置成功即可正常使用。最多支持 4 个回调规则(包含发送前回调和发送后回调),如果需要再增加回调规则,需要联系商务经理开通。 * 发送后回调范围,所有上行消息有效(包含 REST 发送的消息),包含单聊/群聊;如果 app 开通了反垃圾/敏感词过滤,被识别的消息会在服务器被拦截并禁止发送,将不会回调。 * 发送后回调接收延时,是指消息服务器接收到客户端上行消息、再将消息成功回调至客户指定服务器地址的时间间隔。''%%消息接收延时保障是 99.95% 的消息在 30s 内%%''。 * 发送后回调对同一个 app 可以针对不同类型的消息(chat 和 chat_offline)做配置,如果 app 同时需要 chat 和 chat_offline 两种消息,建议区分回调地址。当然,规则也可以把这两种消息同时回调至一个指定服务器地址,在接收到消息后,可以对 eventType 做判断,区分消息的类型。 * **发送后回调重试,当环信服务器执行发出回调后,响应状态码非 200,认为是指定服务器无网络、超过 60 秒或者其他因素导致失败,记录一次失败,然后立即重试,若再次失败,再记录一次失败直至无法重试。30 秒内累计 90 次失败,会对应封禁该 app 回调规则,5 分钟后自动解封。重试失败以及封禁期间的回调不会自动补录,可以下载历史消息文件自行补充。** * 指定服务器收到回调消息后,向消息服务器发出响应内容不能超过 1,000 字符长度,如果连续发送超长的响应内容,会导致回调规则封禁,只能手动解除,需要用户手动重新设置。 * 客户有特殊需求不能丢失回调消息的情况下,请联系环信商务经理开通回调异常缓存功能,并使用 [[https://docs-im.easemob.com/ccim/rest/setupcallback#查询回调储存详情接口描述|查询回调异常缓存]] 和 [[https://docs-im.easemob.com/ccim/rest/setupcallback#补发回调存储信息接口描述|补发回调储存信息]]接口。 ==== 实现步骤 ==== - 发送后回调规则配置; - 从环信服务器向你的应用服务器发送请求; - 在不能丢失回调消息的情况下,请联系环信商务经理开通回调异常缓存功能后,进行回调异常缓存的查询和重新发送。 具体如下: - App Key 可以配置回调规则的数据格式,可以配置多个回调规则(默认最多开通 4 个规则),开通服务后才允许用户配置,前往 [[https://console.easemob.com/|环信即时通讯云控制台]] 配置: ^参数 ^说明 ^ |规则名称 |填写唯一的规则名称(不能用中文)。 | |消息类型 |需要回调的类型,目前有两种消息类型可以回调(chat 为上行消息;chat_offline 为离线消息,可以同时选择) | |回调地址 |环信 IM 服务器会把消息推送到指定的 URL 地址,支持针对不同类型的消息配置不同的 HTTP 和 HTTPS 回调地址。 | |Secret |(可选)默认情况下,环信 IM 服务器发给 URL(也就是您自己服务器的)的数据,签名时使用的密钥由环信 IM 服务器生成,可通过环信即时通讯 IM Console 管理后台查看。若要使用自定义密钥,可以联系环信商务经理。 | === chat 上行消息 === 这个消息跟通过 REST 导出聊天记录查询到的消息一致。比如:一个用户(u1)给另一个用户(u2)发送消息,那么就会产生一条 Chat 消息,与接收方是否在线无关。接收到的消息里面 from 为 u1,to 为 u2。一个用户(u1)给一个群(g1)发送消息,那么会产生一条 Chat 消息,接收到的消息里面 from 为 u1,to 为 g1,且返回值包含 group_id 字段。 === chat_offline 离线消息 === App 可以根据这个消息做 APNS 推送等个性化的推广。例如:给一个用户发送消息,如果用户不在线,那么会产生一条离线消息。如果给一个群发送消息,那么群里有几个用户不在线,就会产生几条离线消息,那么这几条离线消息的 to 为接收消息用户的 ID,并不是群组 ID。 === chat_type 的选择 === * 如果 app 只需要用户的聊天记录,则配置 Chat 类型消息的回调即可。 * 如果 app 只需要针对离线消息做 APNS 等推送,则配置 chat_offline 类型的消息回调即可。 * 如果 app 需同时对用户的聊天记录和离线消息进行处理,则需要配置 chat 和 chat_offline 两种类型的消息回调。
  1. 从环信服务器向你的应用服务器发送请求
请求采用 POST 方式,支持 HTTP/HTTPS,正文部分为 JSON 格式的字符串,字符集为 UTF-8。 回调时,会对发送的正文做 MD5 签名。 环信 IM 使用的 MD5 为 ''%%org.apache.commons.codec.digest.DigestUtils#md5Hex%%''。 app 的响应内容不能超过 1,000 个字符,否则环信服务器会认为是攻击,导致回调失败。 **回调消息的参数说明** ^参数 ^类型 ^ |''%%callId%%'' |callId 为 {appkey}_{uuid} 其中 UUID 为随机生成,作为每条回调的唯一标识。 | |''%%eventType%%'' |“chat” 上行消息、“chat_offline” 离线消息。 | |''%%timestamp%%'' |环信 IM 服务器接收到此消息的 Unix 时间戳,单位为毫秒。 | |''%%chat_type%%'' |“chat” 单聊回调、“groupchat” 群聊回调包含了群组和聊天室的消息回调,默认全选。 | |''%%group_id%%'' |群聊时才有此参数,回调消息所在的群组。 | |''%%from%%'' |消息的发送方。 | |''%%to%%'' |消息的接收方。 | |''%%msg_id%%'' |消息的 ID。 | |''%%payload%%'' |消息内容,与通过 REST API 发送过来的一致,查看[[https://docs-im.easemob.com/im/server/basics/chatrecord#聊天记录数据结构|消息格式文档]]。| |''%%securityVersion%%''|安全校验版本,目前为 1.0.0。忽略此参数,以后会改成控制台做设置。 | |''%%security%%'' |签名,格式如下: MD5(callId+Secret+timestamp)。 Secret 见 Console 后台回调规则。 | **回调成功返回的示例** { "callId":"easemob-demo#test_0990a64f-dp01-6c50-8696-cf3b48b20e7e", "eventType":"chat_offline", "timestamp":1600060847294, "chat_type":"groupchat", "group_id":"16934809238921545", "from":"user1", "to":"user2", "msg_id":"8924312242322", "payload":{ // 具体的消息内容。 }, "securityVersion":"1.0.0", "security":"2ca02c394bef9e7abc83958bcc3156d3" } [[https://docs-im.easemob.com/im/server/basics/chatrecord#聊天记录数据结构|Payload 数据格式说明]] === 应答包字段说明 === 环信 IM 服务器不会验证响应的 response 内容,只要你的服务器给我们返回 HTTP 状态码是 200 即认为将消息回调成功。
  1. 查询回调存储详情和补发回调消息
==== 查询回调储存详情接口描述 ==== 查询 App Key 下由于异常(比如链接超时,响应超时,回调规则封禁等)回调失败时存储的消息和事件类型集合,按每十分钟一个 date key 存储,然后用户可以根据消息集合按需拉取。 === 功能限制说明 === * 异常存储过期时间默认 3 天,若有存储需及时补发。 * 补发重试次数建议控制在 10 次以内。 === 认证方式 === 环信即时通讯 RESTful API 要求 Bearer HTTP 认证。每次发送 HTTP 请求时,都必须在请求头部填入如下 Authorization 字段: Authorization:''%%Bearer ${YourAppToken}%%'' 为提高项目的安全性,使用 token(动态密钥)对即将登录即时通讯系统的用户进行鉴权。即时通讯 RESTful API 仅支持使用 App Token 的鉴权方式,详见[[ccim:authentication|使用 Token 鉴权]]。 === 基本信息 === 方法:''%%GET%%'' 接入点: ''%%/{orgName}/{appName}/callbacks/storage/info%%'' === 路径参数 === ^参数 ^类型 ^是否必需^描述 ^ |''%%orgName%%''|String|是 |你在环信 IM 管理后台注册的组织唯一标识。 | |''%%appName%%''|String|是 |你在环信 IM 管理后台注册的 App 唯一标识 。| === 请求头 === ^参数 ^类型 ^是否必需^描述 ^ |''%%Authorization%%''|String|是 |鉴权 Token,管理员 Token(含)以上权限。| === 响应参数 === ^参数 ^类型 ^描述 ^ |''%%path%%'' |string|请求路径。 | |''%%uri%%'' |string|请求路径的 URI。 | |''%%timestamp%%'' |long |环信 IM 服务器接收到此消息的 Unix 时间戳,单位为毫秒。 | |''%%organization%%'' |string|你在环信 IM 管理后台注册的组织唯一标识。 | |''%%application%%'' |string|你在环信 IM 管理后台注册的 App 唯一标识。 | |''%%action%%'' |string|请求方法。 | |''%%duration%%'' |long |请求耗时,单位为毫秒。 | |''%%applicationName%%''|string|你在环信 IM 管理后台注册的 App 名称。 | |''%%data%%'' |object|响应数据内容。包括以下三个参数:''%%date%%''、''%%size%%'' 和 ''%%retry%%''。| |''%%date%%'' |String|代表本次可以发送的补发的一个十分钟 date key,key 为十分钟的起点。 | |''%%size%%'' |Int |本时段消息数量。 | |''%%retry%%'' |Int |开发者已经重试补发的次数。考虑到补发也可能失败,服务器会继续存储。最开始是 0。 | === 请求示例 === curl -X GET 'http://a1.easemob.com/easemob-demo/easeim/callbacks/storage/info' \ -H 'Authorization: Bearer {{token}}' === 响应示例 === { "path": "/callbacks", "uri": "http://a1.easemob.com/easemob-demo/easeim/callbacks", "timestamp": 1631193031254, "organization": "easemob-demo", "application": "8dfb1641-b6d8-450b-bbe9-d8d45a3be39f", "action": "post", "data": [ { "date": "202109091440", "size": 15, "retry": 0 }, { "date": "202109091450", "size": 103, "retry": 1 } ], "duration": 153, "applicationName": "easeim" } ==== 补发回调存储信息接口描述 ==== 调用接口根据存储集合进行回调补发。 === 基本信息 === 方法:''%%POST%%'' 接入点: ''%%http://{host}/{orgName}/{appName}/callbacks/storage/retry%%'' === 路径参数 === ^参数 ^类型 ^是否必需^描述 ^ |''%%orgName%%''|String|是 |你在环信 IM 管理后台注册的组织唯一标识。 | |''%%appName%%''|String|是 |你在环信 IM 管理后台注册的 App 唯一标识。| === 请求头 === ^参数 ^类型 ^是否必需^描述 ^ |''%%Content-Type%%'' |String|是 |''%%application/json%%'' | |''%%Authorization%%''|String|是 |鉴权 App Token 的值。详见[[ccim:authentication|使用 token 鉴权]]。| === 请求体 === ^参数 ^类型 ^是否必需^描述 ^ |''%%date%%'' |String|是 |可以补发的一个十分钟 date key,key 为十分钟的起点。 | |''%%retry%%'' |Int |否 |开发已重试的次数。考虑到补发也可能失败,服务器会继续存储。最开始是 0。| |''%%targetUrl%%''|String|否 |补发消息的回调地址,如果为空,则使用原回调规则的回调地址。 | === 响应参数 === ^参数 ^类型 ^描述 ^ |''%%path%%'' |String |请求路径。 | |''%%uri%%'' |String |请求路径的 URI。 | |''%%timestamp%%'' |long |环信 IM 服务器接收到此消息的 Unix 时间戳,单位为毫秒。 | |''%%organization%%''|String |你在环信 IM 管理后台注册的组织唯一标识。 | |''%%application%%'' |String |你在环信 IM 管理后台注册的 app 唯一标识。 | |''%%action%%'' |String |请求方法。 | |''%%data%%'' |Bool|- ''%%success%%'':成功;
- ''%%failure%%'':失败。| |''%%duration%%'' |long |请求耗时,单位为毫秒。 | |''%%retry%%'' |Int |开发者已经重试补发的次数。考虑到补发也可能失败,服务器会继续存储。最开始是 0。 | === 请求示例 === curl -X POST 'http://a1.easemob.com/easemob-demo/easeim/callback/storage/retry' \ -H 'Authorization: Bearer {{token}}' \ -H 'Content-Type: application/json' \ --data-raw '{ "date": "202108272230", "retry": 0, "targetUrl": "http://localhost:8000/test" }' === 响应示例 === { "path": "/callbacks", "uri": "http://a1.easemob.com/easemob-demo/easeim/callbacks", "timestamp": 1631194031721, "organization": "easemob-demo", "application": "8dfb1641-b6d8-450b-bbe9-d8d45a3be39f", "action": "post", "data": "success", "duration": 225, "applicationName": "easeim" } === 响应码说明 === ^状态码^描述 ^ |200|请求成功。 | |400|请求参数错误,请根据返回提示检查。| |401|用户权限错误。 | |403|服务未开通或权限不足。 | |429|单位时间内请求过多。 | |500|服务器内部错误。 |