You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何部署支持多Appid的Azure Functions Bot?复用Stack Overflow代码

适配多AppId Bot到Azure Functions的实现方案

当然可以实现!咱们可以把那篇文章里的多AppId支持逻辑,适配到Azure Functions环境中,用单个HttpTrigger函数作为多个机器人通道的消息端点。下面是具体的步骤和代码示例:

1. 准备必要的NuGet包

首先确保你的Azure Functions项目安装了以下包:

  • Microsoft.Bot.Builder
  • Microsoft.Bot.Builder.Azure.Functions
  • Microsoft.Bot.Connector
  • Microsoft.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:59:25