BotFrameworkAdapter.ProcessActivityAsync手动调用返回null或冻结问题
解决手动调用ProcessActivityAsync返回null或程序冻结的问题
我之前在手动测试Bot Framework的Turn逻辑时也碰到过类似的问题,结合你的EchoBot测试场景,咱们来梳理下问题原因和解决方案:
核心问题分析
你的代码里主要有几个可能导致异常的点:
- 直接给
ProcessActivityAsync传递null作为ClaimsIdentity参数,这会触发adapter内部身份验证逻辑异常,进而导致处理中断或程序冻结 - 依赖注入初始化时,可能未正确配置adapter的类型或配套服务
- 控制台环境下异步方法的调用方式不当,容易引发死锁
分步解决方案
1. 正确构造ClaimsIdentity
不要传null,而是创建一个符合Bot Framework要求的身份标识。针对本地测试场景,可以构造包含必要声明的ClaimsIdentity:
var claimsIdentity = new ClaimsIdentity(new List<Claim> { new Claim(ClaimTypes.NameIdentifier, activity.From.Id), new Claim(ClaimTypes.Name, activity.From.Name), new Claim("http://schemas.microsoft.com/identity/claims/scope", "bot") }, "Emulator");
2. 确保Adapter类型正确配置
如果依赖注入中注册的IAdapterIntegration实例不符合测试场景,建议直接手动初始化适配的adapter(避免DI配置遗漏):
// 替换DI获取adapter的代码,手动初始化BotFrameworkAdapter var adapter = new BotFrameworkAdapter(new BotFrameworkAdapterSettings());
3. 避免异步死锁
控制台程序的Main方法若不是async类型,直接使用await可能导致死锁。可以改用GetAwaiter().GetResult()等待异步操作完成,或者将Main改为async Task:
// 示例:使用GetAwaiter().GetResult()避免死锁 var resp = adapter.ProcessActivityAsync(claimsIdentity, activity, bot.OnTurnAsync, CancellationToken.None).GetAwaiter().GetResult();
4. 完整修改后的测试代码
using System; using System.Collections.Generic; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Integration.AspNet.Core; using Microsoft.Bot.Schema; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; class Program { static void Main(string[] args) { // 初始化DI容器 var sc = new ServiceCollection(); new Startup().ConfigureServices(sc); var inject = sc.BuildServiceProvider(); // 反序列化测试Activity var activityJson = @"{ ""channelData"": {""clientActivityID"": ""15617619894020.cy8e5xuro9h""}, ""channelId"": ""emulator"", ""conversation"": {""id"": ""79e62041-99f6-11e9-a11f-bbd34246e3a4|livechat""}, ""entities"": [{ ""requiresBotState"": true, ""supportsListening"": true, ""supportsTts"": true, ""type"": ""ClientCapabilities"" }], ""from"": {""id"": ""5e26d43a-f8ec-485b-9688-091010cb8f88"",""name"": ""User"",""role"": ""user""}, ""id"": ""918703e0-99f6-11e9-be13-9b4b2092afdf"", ""localTimestamp"": ""2019-06-28T19:46:29-03:00"", ""locale"": ""en-US"", ""recipient"": {""id"": ""12fdf550-99e3-11e9-a11f-bbd34246e3a4"",""name"": ""Bot"",""role"": ""bot""}, ""serviceUrl"": ""http://localhost:53796"", ""text"": ""Ruda"", ""textFormat"": ""plain"", ""timestamp"": ""2019-06-28T22:46:29.406Z"", ""type"": ""message"" }"; var activity = JsonConvert.DeserializeObject<Activity>(activityJson); // 获取Bot实例 var bot = inject.GetService<IBot>(); // 构造合法的ClaimsIdentity var claimsIdentity = new ClaimsIdentity(new List<Claim> { new Claim(ClaimTypes.NameIdentifier, activity.From.Id), new Claim(ClaimTypes.Name, activity.From.Name), new Claim("http://schemas.microsoft.com/identity/claims/scope", "bot") }, "Emulator"); // 初始化Adapter var adapter = new BotFrameworkAdapter(new BotFrameworkAdapterSettings()); // 添加中间件捕获Bot的回复内容 adapter.Use(new CatchResponseMiddleware()); // 处理Activity(避免死锁) var resp = adapter.ProcessActivityAsync(claimsIdentity, activity, bot.OnTurnAsync, CancellationToken.None).GetAwaiter().GetResult(); Console.WriteLine("处理完成,ProcessActivityAsync返回值:" + (resp ?? "null")); } } // 自定义中间件,用于捕获Bot发送的回复 public class CatchResponseMiddleware : IMiddleware { public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default) { // 监听SendActivity事件 turnContext.OnSendActivity(async (ctx, activities, nextSend) => { foreach (var activity in activities) { Console.WriteLine("Bot回复内容:" + activity.Text); } return await nextSend(); }); await next(cancellationToken); } }
额外说明
- 为什么返回null?
BotFrameworkAdapter.ProcessActivityAsync的设计初衷是处理HTTP请求,它的返回值对应HTTP响应而非Bot的对话回复。Bot的回复是通过TurnContext.SendActivityAsync发送的,所以手动调用时返回null是预期行为。 - 程序冻结的主要原因是异步操作未正确等待,或
null的ClaimsIdentity导致adapter内部异步任务无法正常完成。
内容的提问来源于stack exchange,提问作者Rudá Cunha
相关产品推荐
相关产品推荐

