如何通过Azure Function HTTP触发器接收Azure AD B2C用户注册/登录事件
Azure AD B2C登录/注册事件触发Azure Function无数据传入的解决步骤
一、先检查Azure AD B2C的事件配置是否正确
你可能只把Function URL配到了应用里,但B2C默认不会主动把登录/注册事件发去外部端点,得通过以下两种方式配置:
- 自定义策略方式:在用户旅程的结束步骤加一个
<TechnicalProfile>,类型选Restful,指向你的Function URL,同时配置要传递的用户数据(比如用户ID、操作类型、着陆页地址这些) - Event Grid集成方式:给B2C租户订阅
UserCreated和UserSignedIn事件,把事件推送到你的Function(注意这种方式的事件格式和自定义策略的Rest调用格式不一样)
二、你的Azure Function代码里的问题
- 静态Run方法用不了构造注入的IPublishEndpoint:Run是static的,而
_publishEndpoint是实例字段,导致你写的PublishMessageToServiceBus方法根本调不了,而且就算能调,你代码里也没实际调用它 - 数据接收逻辑太粗糙:直接转成
dynamic,没处理空请求或者格式错误的情况,也没对应B2C实际发的格式 - 消息发布逻辑没执行:代码里创建了
MessageCustomer,但完全没调用发布方法把消息发出去
三、修复后的代码示例
public class MessageBrokerProducerFunction { private readonly IPublishEndpoint _publishEndpoint; public MessageBrokerProducerFunction(IPublishEndpoint publishEndpoint) { _publishEndpoint = publishEndpoint; } // 去掉static,改成实例方法才能用构造注入的_publishEndpoint [FunctionName("B2CCallbackFunction")] public async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation("C# HTTP trigger function processed a request."); string requestBody; try { requestBody = await new StreamReader(req.Body).ReadToEndAsync(); if (string.IsNullOrWhiteSpace(requestBody)) { log.LogWarning("Received empty request body"); return new BadRequestObjectResult("Request body cannot be empty"); } // 别用dynamic,定义强类型模型对应B2C发的事件格式,避免出错 var b2cEventData = JsonConvert.DeserializeObject<B2cEventData>(requestBody); if (b2cEventData == null) { log.LogError("Failed to parse request body into expected format"); return new BadRequestObjectResult("Invalid request body format"); } // 提取需要的用户旅程信息构建消息 var message = new MessageCustomer( Guid.NewGuid(), b2cEventData.UserId, b2cEventData.OperationType, // 比如是"SignIn"还是"SignUp" b2cEventData.LandingPageUrl, DateTime.UtcNow ); // 调用发布方法,用用户ID作为sessionId await PublishMessageToServiceBus(message, log, b2cEventData.UserId); return new OkObjectResult($"Successfully processed event for user {b2cEventData.UserId}"); } catch (Exception ex) { log.LogError(ex, "Error processing B2C event"); return new StatusCodeResult(StatusCodes.Status500InternalServerError); } } private async Task PublishMessageToServiceBus(object message, ILogger logger, string sessionId) { try { await _publishEndpoint.Publish(message, context => context.SetSessionId(sessionId)); logger.LogInformation($"Message sent successfully for session {sessionId}: {JsonConvert.SerializeObject(message)}"); } catch (Exception ex) { logger.LogError(ex, $"Failed to send message for session {sessionId}, retrying..."); await Task.Delay(TimeSpan.FromSeconds(10)); // 建议加个重试次数限制,别无限循环 await PublishMessageToServiceBus(message, logger, sessionId); } } // 强类型模型,根据B2C实际发送的字段调整 public class B2cEventData { public string UserId { get; set; } public string OperationType { get; set; } public string LandingPageUrl { get; set; } // 可以再加其他需要的字段,比如Email、DisplayName之类的 } }
四、本地测试怎么验证
- 用Postman模拟B2C请求:
- 按照B2C的事件格式写JSON请求体(比如Event Grid的事件包含
eventType、data;自定义策略的Rest调用包含你配置的Claims) - 发POST请求到本地Function的地址(比如
http://localhost:7071/api/B2CCallbackFunction),记得带上Function密钥(如果是Function级别的授权) - 看Function的日志,确认有没有收到数据,有没有成功发消息
- 按照B2C的事件格式写JSON请求体(比如Event Grid的事件包含
- 本地调试:在VS里启动Function调试,打个断点看
req.Body的内容,直接确认有没有数据传进来 - 检查授权级别:如果测试时嫌带密钥麻烦,可以暂时把
AuthorizationLevel.Function改成Anonymous,生产环境改回去
五、要注意的关键点
- 如果用Event Grid,得确保本地Function能被外网访问,测试时可以用ngrok把本地端口暴露出去
- 如果用自定义策略,要在策略里配置
<OutputClaim>把需要的用户数据传给Function,并且Rest技术配置文件的Method要设成POST - 生产环境一定要给
PublishMessageToServiceBus加重试次数限制,不然出错会无限循环 - 排查的时候可以先把
requestBody完整打日志,看看B2C到底发的是什么格式,再调整模型
内容的提问来源于stack exchange,提问作者Laura Díaz
相关产品推荐
相关产品推荐

