XiHan Framework 处于预览阶段

查看获取方式
跳转到内容

XiHan.Framework.Protocols.CloudEvents ​

CloudEvents 协议适配器,提供 protocols.cloudevents@1:消息子系统信封的 CloudEvents 1.0 HTTP 协议绑定。入站端点接收 binary、structured、batch 三种内容模式的事件,按类型映射到主题后派发给处理器或发布到消息代理;出站 Webhook 发布者把信封推送到配置的 HTTP 端点。

适用于与外部系统用 CloudEvents 交换事件的应用。

项内容
包 IDXiHan.Framework.Protocols.CloudEvents
环层Ring 5(协议与插件)
能力提供 protocols.cloudevents@1;依赖 kernel.hosting@1、messaging.queue@1
特性入口CloudEventsProtocolFeature
配置节XiHan:Protocols:CloudEvents
依赖XiHan.Framework.Kernel、XiHan.Framework.Hosting.AspNetCore、XiHan.Framework.Messaging.Abstractions、XiHan.Framework.Localization.Abstractions;共享框架 Microsoft.AspNetCore.App
AOT兼容(IsAotCompatible)
稳定性Preview

概述 ​

CloudEventsProtocolFeature 登记选项、本包的消息目录、命名 HttpClient xihan.protocols.cloudevents(过出站地址策略)与 CloudEventsWebhookPublisher。

入站:MapXiHanCloudEvents() 映射 POST /events,按 Content-Type 与 ce-specversion 头识别内容模式,把事件读成 MessageEnvelope,事件类型经 Topics 映射到主题(没有映射时类型本身须是合法主题)。Inbound = Dispatch(缺省)时在请求内经 MessageDispatcher 派发给本实例里主题的处理器(竞争订阅与广播订阅各一次投递,先竞争后广播),处理器的结果就是响应,要让广播订阅到达每个实例用 Publish;Inbound = Publish 时发布到消息代理后立即 202。扩展属性 xihantenant、xihanprincipal、xihancorrelation、xihanlocale 与 traceparent 在派发时进入调用上下文。入站端点经过 UseXiHan(),请求凭据在外层认证。框架不给端点加任何要求,权限要由宿主自己加(MapXiHanCloudEvents().RequireXiHanPermission(...)):扩展属性里的租户与主体是发送方给的,能调端点的人就能选租户(ADR-0121)。

出站:CloudEventsWebhookPublisher 实现 IMessagePublisher,把信封 POST 到 Webhooks:<主题>,模式为 Binary(缺省)或 Structured,批量时用 batch 内容模式一次 POST。它只按具体类型登记,不替换消息代理的发布者。信封按原样发出:它不像 DirectPublisher、OutboxPublisher 那样从当前调用补齐 traceparent、租户、主体、关联与区域扩展,租户 ID、主体 ID 等内部标识不会外推给接收方。这是定下的规则(ADR-0074):接收方是第三方,内部标识推出去会暴露内部结构与身份、让第三方依赖内部 ID,追踪链是否跨出组织边界也由调用方决定;要让接收方拿到的扩展由调用方显式写进信封。HTTP 层同理(ADR-0083):启用追踪时 .NET 的 HttpClient 本会按当前 Activity 写 W3C 的 traceparent、tracestate 与 baggage 头(与信封扩展无关),Webhook 的命名客户端 xihan.protocols.cloudevents 缺省不写这些头;要把追踪链接到接收方,设 XiHan:Protocols:CloudEvents:PropagateTraceContext 为 true,按 .NET 的全局传播器写。这只作用于 Webhook 的命名客户端(主处理器 SocketsHttpHandler 的 ActivityHeadersPropagator),不改进程全局的传播,出站请求的客户端跨度照常记录。调连接参数用 UseSocketsHttpHandler 配置同一个处理器,缺省保留;整个替换主处理器(ConfigurePrimaryHttpMessageHandler)则由替换后的处理器决定是否传播。

何时使用 ​

外部系统(SaaS 的 Webhook、事件网关、其他语言的服务)以 CloudEvents 推事件进来,或应用要把消息以 CloudEvents 推给外部 HTTP 端点时用它。服务内部的消息传递直接用消息子系统,见消息与事件。

安装与启用 ​

bash
dotnet add package XiHan.Framework.Protocols.CloudEvents --prerelease

当前是预览版,安装要带 --prerelease;从源码或本地包源引用见快速上手。Profiles.Full 已包含本包。

csharp
using XiHan.Framework.Hosting.AspNetCore;
using XiHan.Framework.Profiles.Standard;
using XiHan.Framework.Protocols.CloudEvents;
using XiHan.Framework.Protocols.CloudEvents.Inbound;

var builder = WebApplication.CreateBuilder(args);
// Standard 组合里的内存消息实现提供 messaging.queue@1
builder.AddXiHan(xihan => xihan.UseStandardProfile().UseFeature<CloudEventsProtocolFeature>());

var app = builder.Build();
app.UseXiHan();
app.MapXiHanCloudEvents();   // POST /events
app.Run();

配置 ​

键类型缺省说明
XiHan:Protocols:CloudEvents:InboundDispatch / PublishDispatch入站事件派发给处理器,或发布到消息代理;不区分大小写
XiHan:Protocols:CloudEvents:OutboundBinary / StructuredBinary出站单条事件的内容模式;不接受 Batch
XiHan:Protocols:CloudEvents:PropagateTraceContextboolfalse出站 Webhook 是否在 HTTP 头里写追踪上下文(traceparent、tracestate、baggage);只作用于 Webhook 的命名客户端
XiHan:Protocols:CloudEvents:Topics:<事件类型>string—事件类型到主题的映射,值须满足主题文法
XiHan:Protocols:CloudEvents:Webhooks:<主题>string—出站 Webhook 地址,绝对的 http / https 地址
json
{
  "XiHan": {
    "Protocols": {
      "CloudEvents": {
        "Inbound": "Dispatch",
        "Outbound": "Binary",
        "Topics": { "orders.placed": "orders" },
        "Webhooks": { "orders": "https://hooks.example.com/orders" }
      }
    }
  }
}

值不合法时读取配置就抛出 ArgumentException。

主要类型 ​

类型说明
CloudEventsProtocolFeature特性入口
CloudEventsProtocolOptionsInbound、Outbound、Topics、Webhooks;TopicFor(type);FromConfiguration
Inbound.CloudEventsEndpointRouteBuilderExtensionsMapXiHanCloudEvents(pattern),缺省 /events
Inbound.CloudEventsHttpBindingHTTP 绑定:ModeOf、ReadAsync、Write、WriteBatch、EncodeHeaderValue / DecodeHeaderValue;常量 StructuredContentType、BatchContentType、HeaderPrefix = ce-
Inbound.CloudEventsContentModeBinary、Structured、Batch
Inbound.CloudEventsInboundModeDispatch、Publish
Outbound.CloudEventsWebhookPublisher出站发布者:PublishAsync、PublishManyAsync;常量 HttpClientName = xihan.protocols.cloudevents
CloudEventsErrors本包的错误码

使用示例 ​

外部系统以 binary 模式推一个事件(samples/SimpleApp,主题 orders 上有 OrderPlacedHandler,端点要求 events.deliver,只有服务账号 relay 持有):

bash
curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:5000/events \
  -H "Authorization: ApiKey dev-relay-key" \
  -H "ce-specversion: 1.0" -H "ce-id: evt-1" -H "ce-source: /external" -H "ce-type: orders.placed" \
  -H "ce-xihantenant: acme" -H "Content-Type: application/json" -d '{"id":"evt-1"}'

出站:在消息处理器里把主题上的消息转发给 Webhook,失败时消息按消息子系统的规则重试:

csharp
using XiHan.Framework.Messaging.Abstractions.Handlers;
using XiHan.Framework.Primitives;
using XiHan.Framework.Protocols.CloudEvents.Outbound;

public sealed class OrderWebhookForwarder(CloudEventsWebhookPublisher webhooks) : IMessageHandler
{
    public ValueTask<Result> HandleAsync(MessageDelivery delivery, InvocationContext context, CancellationToken cancellationToken)
        => webhooks.PublishAsync(delivery.Topic, delivery.Envelope, cancellationToken);
}

内容模式、batch 语义与派发模式的完整说明见 gRPC、MCP 与 CloudEvents。

行为与限制 ​

  • 派发模式下成功是 202、无正文,处理器失败按错误 KIND 写 Problem Details,发送方据此重试。
  • batch 先解析全部事件的主题,任一没有映射整批 400、一个都不处理;已处理的事件不回滚,处理器按 id + source 去重。
  • 出站多条事件用一个 batch 请求,接收方须支持 application/cloudevents-batch+json,一个响应对整批生效。
  • 入站内容模式:Content-Type: application/cloudevents+json(structured)、application/cloudevents-batch+json(batch,事件数组),否则有 ce-specversion 头即 binary(属性在 ce-* 头,Content-Type 是数据内容类型,请求体是数据,头值按规范百分号解码);都不是返回 400 PROTOCOLS_VALIDATION_008。Inbound = Publish 时发布到消息代理后立即 202,重试与死信由代理负责;派发只发生在接收请求的实例。batch 在派发模式按顺序处理、第一个失败即停止并作为响应,发布模式按主题分组一次批量发布、第一个失败的结果作为响应。
  • 过出站地址策略(RFC-0008、ADR-0117):Webhook 地址是运维配的,但接收方是第三方,它的 3xx 能把请求引到内网,所以命名客户端的连接与重定向都检查。接收方在内网时写进 XiHan:Security:Outbound:AllowedHosts;被拒绝按连接失败报告 PROTOCOLS_UNAVAILABLE_002,reason 是 OutboundAddressDeniedException 的类型全名。
  • HTTP 追踪头缺省不传播(ADR-0083):启用追踪时 .NET 的 HttpClient 本会按当前 Activity 写 traceparent、tracestate 与 baggage 头(不是信封扩展),这会把内部追踪 ID 与 Baggage 推给第三方,所以命名客户端的主处理器缺省是关掉传播器的 SocketsHttpHandler(ActivityHeadersPropagator = null)。PropagateTraceContext = true 显式打开,按 .NET 的全局传播器写,只影响这个客户端,出站请求的客户端跨度照常记录;调连接参数用 UseSocketsHttpHandler 配置同一个处理器(设置保留),用 ConfigurePrimaryHttpMessageHandler 整个替换主处理器时由替换后的处理器决定是否传播。
  • 入站端点经过 UseXiHan(),请求凭据在外层认证;框架不给端点加任何要求,权限要由宿主自己加(ADR-0121):MapXiHanCloudEvents().RequireXiHanPermission(...),只授给平台的服务账号或服务角色。扩展属性 xihantenant、xihanprincipal 是发送方给的,派发时不和请求的身份核对,能调端点的人就能选租户、写审计主体,权限也不按租户收窄;没有装配 security.authorization@1 时要求只进入上下文、不被判定,端点等于敞开。
  • 发布者自己不重试;超时与重试经命名 HttpClient xihan.protocols.cloudevents 配置,或放在消息处理器 / 发件箱里获得至少一次语义。
错误码情形
PROTOCOLS_VALIDATION_008请求不是 CloudEvent(400)
MESSAGING_VALIDATION_002信封不合法(400)
PROTOCOLS_VALIDATION_009事件类型没有映射到主题,类型本身也不是合法主题(400)
PROTOCOLS_NOTFOUND_003派发模式下主题在本实例两种订阅方式都没有处理器(404)
PROTOCOLS_NOTFOUND_004主题没有配置出站 Webhook
PROTOCOLS_UNAVAILABLE_001Webhook 返回非 2xx,status 在错误细节里
PROTOCOLS_UNAVAILABLE_002Webhook 无法连接

相关 ​

Released under The MIT License