如何部署支持多Appid的Azure Functions Bot?复用Stack Overflow代码
适配多AppId Bot到Azure Functions的实现方案
当然可以实现!咱们可以把那篇文章里的多AppId支持逻辑,适配到Azure Functions环境中,用单个HttpTrigger函数作为多个机器人通道的消息端点。下面是具体的步骤和代码示例:
1. 准备必要的NuGet包
首先确保你的Azure Functions项目安装了以下包:
Microsoft.Bot.BuilderMicrosoft.Bot.Builder.Azure.FunctionsMicrosoft.Bot.ConnectorMicrosoft.Extensions.Configuration(用于读取配置)
2. 自定义多AppId认证逻辑
Azure Functions Bot默认的BotFrameworkAuthentication只支持单个AppId和密码,所以我们需要自定义一个认证类,来处理多个AppId的验证:
using Microsoft.Bot.Builder; using Microsoft.Bot.Connector.Authentication; using Microsoft.Extensions.Configuration; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Net.Http; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; public class MultiAppIdBotFrameworkAuthentication : BotFrameworkAuthentication { private readonly IConfiguration _configuration; private readonly Dictionary<string, string> _botCredentials; public MultiAppIdBotFrameworkAuthentication(IConfiguration configuration) { _configuration = configuration; // 从配置中读取所有Bot的AppId和对应的密码 _botCredentials = configuration.GetSection("BotCredentials") .GetChildren() .ToDictionary(kv => kv.Key, kv => kv.Value); } public override Task<AuthenticateRequestResult> AuthenticateRequestAsync(HttpRequest httpRequest, CancellationToken cancellationToken) { // 提取请求中的Authorization头 var authHeader = httpRequest.Headers["Authorization"].FirstOrDefault(); if (string.IsNullOrEmpty(authHeader)) { return Task.FromResult(new AuthenticateRequestResult { IsAuthenticated = false }); } // 从Token中解析出Bot的AppId var token = authHeader.Replace("Bearer ", string.Empty); var jwtHandler = new JwtSecurityTokenHandler(); var jwtToken = jwtHandler.ReadJwtToken(token); var appId = jwtToken.Claims.FirstOrDefault(c => c.Type == "appid")?.Value; if (!string.IsNullOrEmpty(appId) && _botCredentials.TryGetValue(appId, out var password)) { // 使用对应的凭证验证请求合法性 var credentialProvider = new SimpleCredentialProvider(appId, password); var validationParameters = JwtTokenValidation.CreateValidationParameters(credentialProvider); ClaimsIdentity claimsIdentity = null; try { claimsIdentity = jwtHandler.ValidateToken(token, validationParameters, out _); } catch { } return Task.FromResult(new AuthenticateRequestResult { IsAuthenticated = claimsIdentity != null, ClaimsIdentity = claimsIdentity, BotAppId = appId }); } return Task.FromResult(new AuthenticateRequestResult { IsAuthenticated = false }); } // 实现其他必要的接口方法,复用默认逻辑即可 public override BotFrameworkClient CreateBotFrameworkClient() { return new BotFrameworkClient(new HttpClient()); } public override Task<ConnectorClient> CreateConnectorClientAsync(string serviceUrl, CancellationToken cancellationToken) { return base.CreateConnectorClientAsync(serviceUrl, cancellationToken); } public override Task<string> GetOriginatingAudienceAsync(HttpRequest httpRequest, CancellationToken cancellationToken) { return base.GetOriginatingAudienceAsync(httpRequest, cancellationToken); } }
3. 配置多Bot的AppId和密码
在Azure Functions的应用设置(或者本地的local.settings.json)中添加多个Bot的凭证,格式如下:
{ "Values": { "BotCredentials:YourFirstBotAppId": "YourFirstBotPassword", "BotCredentials:YourSecondBotAppId": "YourSecondBotPassword" } }
4. 实现Azure Functions的消息端点
创建一个HttpTrigger函数,使用我们自定义的认证类和适配器,处理来自多个Bot的请求:
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Azure.Functions; using Microsoft.Extensions.Logging; public class MultiBotFunction { private readonly IBot _bot; private readonly MultiAppIdBotFrameworkAuthentication _auth; // 通过依赖注入获取Bot和自定义认证实例 public MultiBotFunction(IBot bot, MultiAppIdBotFrameworkAuthentication auth) { _bot = bot; _auth = auth; } [FunctionName("MultiBotEndpoint")] public async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "api/messages")] HttpRequest req, ILogger log) { log.LogInformation("Received a message from a bot channel."); // 使用自定义认证初始化适配器 var adapter = new CloudAdapter(_auth); // 全局异常处理 adapter.OnTurnError = async (turnContext, exception) => { log.LogError(exception, $"Error processing turn: {exception.Message}"); await turnContext.SendActivityAsync("Sorry, something went wrong while processing your request!"); }; // 处理请求并运行Bot逻辑 await adapter.ProcessAsync(req, req.HttpContext.Response, _bot); return new OkResult(); } }
5. 可选:按AppId区分Bot业务逻辑
如果不同的Bot需要独立的业务逻辑,可以在Bot的OnTurnAsync方法中,通过turnContext.Activity.Recipient.Id(即当前Bot的AppId)来区分处理:
public class MultiBot : ActivityHandler { protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken) { var currentBotAppId = turnContext.Activity.Recipient.Id; switch (currentBotAppId) { case "YourFirstBotAppId": await turnContext.SendActivityAsync(MessageFactory.Text($"👋 Hello from Bot 1! You said: {turnContext.Activity.Text}"), cancellationToken); break; case "YourSecondBotAppId": await turnContext.SendActivityAsync(MessageFactory.Text($"🤖 Greetings from Bot 2! Your message: {turnContext.Activity.Text}"), cancellationToken); break; default: await turnContext.SendActivityAsync(MessageFactory.Text("⚠️ Unknown bot instance!"), cancellationToken); break; } } }
注意事项
- 确保每个机器人通道注册的消息端点都指向这个Azure Functions的URL:
https://<your-function-app-name>.azurewebsites.net/api/messages - 本地调试时,需要用ngrok等工具暴露本地端口,让Bot Framework能回调到你的本地函数
- 生产环境建议将Bot密码存储在Azure Key Vault中,避免明文配置
内容的提问来源于stack exchange,提问作者Renato Muñoz
相关产品推荐
相关产品推荐

