T2FGame.Action 1.0.9

dotnet add package T2FGame.Action --version 1.0.9
                    
NuGet\Install-Package T2FGame.Action -Version 1.0.9
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="T2FGame.Action" Version="1.0.9" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="T2FGame.Action" Version="1.0.9" />
                    
Directory.Packages.props
<PackageReference Include="T2FGame.Action" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add T2FGame.Action --version 1.0.9
                    
#r "nuget: T2FGame.Action, 1.0.9"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package T2FGame.Action@1.0.9
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=T2FGame.Action&version=1.0.9
                    
Install as a Cake Addin
#tool nuget:?package=T2FGame.Action&version=1.0.9
                    
Install as a Cake Tool

T2FGame.Action

T2FGame 框架的 Action 处理层,提供类似 ioGame BarSkeleton 的业务处理框架。

功能特性

  • Action 控制器:声明式路由配置,使用 [ActionController] 和 [ActionMethod] 注解
  • Pipeline 处理链:可扩展的请求处理管道,支持认证、授权、验证、缓存、限流等
  • 参数绑定:自动解析 Protobuf 请求参数和 FlowContext 上下文
  • 模块间调用:支持跨模块 Action 调用
  • 消息推送:支持单播、多播、广播消息
  • 调试输出:类似 ioGame 的 DebugInOut 控制台调试信息
  • Result 模式集成:函数式错误处理

安装

<PackageReference Include="T2FGame.Action" />

快速开始

1. 定义 Action 控制器

[ActionController(cmd: 1)]  // 模块命令
public class UserController
{
    private readonly IUserService _userService;

    public UserController(IUserService userService)
    {
        _userService = userService;
    }

    /// <summary>
    /// 用户登录
    /// </summary>
    [ActionMethod(subCmd: 1)]
    public async Task<LoginResponse> Login(LoginRequest request, FlowContext context)
    {
        var result = await _userService.LoginAsync(request.Username, request.Password);

        // 设置认证状态
        UserIdSettingKit.SettingUserId(context, result.UserId);

        return new LoginResponse
        {
            UserId = result.UserId,
            Token = result.Token
        };
    }

    /// <summary>
    /// 获取用户信息(需要认证)
    /// </summary>
    [ActionMethod(subCmd: 2, requireAuth: true)]
    public async Task<UserInfo> GetUserInfo(FlowContext context)
    {
        var user = await _userService.GetByIdAsync(context.UserId);
        return user.ToProto();
    }
}

2. 注册服务

// 方式一:运行时反射扫描(无需 Source Generator)
services.AddT2FGameAction(typeof(UserController).Assembly);

// 方式二:编译时生成(需要引用 T2FGame.CodeGen.Action,零反射)
// 参见 "Source Generator 使用" 章节
services.AddT2FGameActionGenerated();

// 注册 Pipeline(可选配置)
services.AddActionPipeline(options =>
{
    options.UseTraceHandler = true;          // 链路追踪
    options.UseAuthenticationHandler = true; // 认证检查
    options.UseValidationHandler = true;     // 参数验证
    options.UseDebugInOutHandler = true;     // 调试输出
});

Pipeline 处理链

请求流程:前置处理器 → Action 执行 → 后置处理器

内置处理器

处理器 Order 阶段 功能 默认启用
TraceHandler 10 前置 链路追踪,生成 TraceId ✅
TimeoutHandler 15 前置 超时控制 ❌
RateLimitHandler 20 前置 请求限流(滑动窗口) ❌
AuthenticationHandler 30 前置 认证检查 ✅
AuthorizationHandler 40 前置 授权检查(角色/权限) ❌
ValidationHandler 50 前置 参数验证(DataAnnotations) ✅
CacheHandler 60 前置 响应缓存检查 ✅
ActionInvokeHandler 100 执行 执行 Action 方法 ✅
CacheStoreHandler 200 后置 缓存存储 ✅
DebugInOutHandler 205 后置 调试信息输出 ❌
LoggingHandler 210 后置 日志记录 ✅
MetricsHandler 220 后置 指标统计 ❌

自定义处理器

处理器按 Order 属性从小到大依次执行,返回 false 将终止管道:

public class MyCustomHandler : ActionHandlerBase
{
    // Order 值决定执行顺序:
    // 10=Trace, 20=RateLimit, 30=Auth, 50=Validation, 60=Cache, 100=ActionInvoke
    public override int Order => 25; // 在限流之后,认证之前

    public override async ValueTask<bool> HandleAsync(ActionPipelineContext context)
    {
        // 前置逻辑
        if (!CheckSomething(context))
        {
            context.SetError(ErrorCodes.Forbidden, "检查失败");
            return false; // 中断管道
        }

        return true; // 继续执行下一个处理器
    }
}

// 或使用接口
public class AnotherHandler : IActionHandler
{
    public int Order => 35;
    public string Name => "AnotherHandler";
    public bool Enabled => true;

    public async ValueTask<bool> HandleAsync(ActionPipelineContext context)
    {
        // 处理逻辑
        return true;
    }
}

// 注册自定义处理器
services.AddActionHandler<MyCustomHandler>();

参数验证

支持 .NET DataAnnotations 验证规范(类似 Java JSR 380):

public class RegisterRequest : IMessage<RegisterRequest>
{
    [Required(ErrorMessage = "邮箱不能为空")]
    [EmailAddress(ErrorMessage = "不是一个合法的电子邮件地址")]
    public string Email { get; set; }

    [Required]
    [StringLength(20, MinimumLength = 6, ErrorMessage = "密码长度必须在6-20之间")]
    public string Password { get; set; }

    [Range(18, 120, ErrorMessage = "年龄必须在18-120之间")]
    public int Age { get; set; }
}

验证失败时自动返回错误码 202(ValidationError),不执行 Action 方法。

常用验证特性

特性 说明
[Required] 必填
[EmailAddress] 邮箱格式
[Range(min, max)] 数值范围
[StringLength(max, MinimumLength)] 字符串长度
[RegularExpression(pattern)] 正则匹配
[Phone] 电话格式
[Url] URL 格式

调试输出 (DebugInOut)

启用后在控制台输出请求处理详情,便于开发调试:

services.AddActionPipeline(options =>
{
    options.UseDebugInOutHandler = true;
});

services.Configure<DebugInOutOptions>(options =>
{
    options.Enabled = true;
    options.MinTriggerTimeMs = 0;      // 0=打印所有,>0=只打印慢请求
    options.PrintRequestData = true;   // 打印请求参数
    options.PrintResponseData = true;  // 打印响应数据
    options.MaxDataLength = 200;       // 最大数据长度
});

成功请求输出

┏━━━━━ Debug. [(UserController.cs).Login] ━━━━━ [1-1] ━━━━━
┣ userId: 12345
┣ sessionId: abc123
┣ 连接方式: WebSocket
┣ 参数: LoginRequest: { "username": "test" }
┣ 响应: LoginResponse: { "userId": 12345, "token": "xxx" }
┣ 时间: 5 ms (业务方法总耗时)
┗━━━━━ [T2FGame] ━━━━━ [线程:Worker-1] ━━━━━━━ [traceId:abc123def456] ━━━━━

错误请求输出

┏━━━━━ Error. [(UserController.cs).Register] ━━━━━ [1-2] ━━━━━
┣ userId: (未登录)
┣ sessionId: abc123
┣ 连接方式: WebSocket
┣ 参数: RegisterRequest: { "email": "invalid", "age": 10 }
┣ 错误码: 202
┣ 错误信息: 不是一个合法的电子邮件地址
┣ 时间: 0 ms (业务方法总耗时)
┗━━━━━ [T2FGame] ━━━━━ [线程:Worker-1] ━━━━━━━ [traceId:abc123def456] ━━━━━

忽略调试输出

[ActionController(cmd: 1)]
[IgnoreDebugInOut]  // 整个控制器忽略
public class HeartbeatController
{
    [ActionMethod(subCmd: 1)]
    [IgnoreDebugInOut]  // 单个方法忽略
    public void Ping() { }
}

响应缓存

[ActionMethod(subCmd: 3)]
[ActionCache(Duration = 60, Scope = CacheScope.User, IncludeRequestData = true)]
public async Task<LeaderboardResponse> GetLeaderboard(FlowContext context)
{
    // 此响应将被缓存 60 秒
    return await _leaderboardService.GetTopPlayers();
}
参数 说明
Duration 缓存时长(秒)
Scope 缓存范围:Global/User/Session
IncludeRequestData 是否将请求数据作为缓存键的一部分

限流

[ActionMethod(subCmd: 1)]
[RateLimit(Limit = 10, WindowSeconds = 60)]  // 每分钟最多 10 次
public async Task<Response> SensitiveAction(FlowContext context)
{
    // ...
}

消息广播

使用 FlowContext 内置广播方法(推荐)

[ActionController(cmd: 1)]
public class ChatController
{
    [ActionMethod(subCmd: 1)]
    public async Task SendMessage(ChatMessage request, FlowContext context)
    {
        var cmdInfo = new CmdInfo(1, 2);
        var notify = new ChatNotify { Message = request.Content };

        // 广播给所有人
        await context.Broadcast(cmdInfo, notify);

        // 广播给指定用户
        await context.Broadcast(cmdInfo, notify, targetUserId);

        // 广播给当前用户
        await context.BroadcastMe(cmdInfo, notify);

        // 房间广播
        await context.BroadcastToRoom(cmdInfo, notify, roomId);

        // 房间广播(排除自己)
        await context.BroadcastToRoomExceptMe(cmdInfo, notify, roomId);
    }
}

使用 RangeBroadcaster 精细控制

// 使用 RangeBroadcaster 进行精细控制
await RangeBroadcaster.Of(context)
    .SetMessage(cmdInfo, notify)
    .AddUserIds(1001, 1002, 1003)  // 添加目标用户
    .ExcludeUserId(context.UserId) // 排除发送者
    .ExecuteAsync();

// 房间广播并排除用户
await RangeBroadcaster.Of(context)
    .SetMessage(cmdInfo, notify)
    .BroadcastToRoom("room_123")
    .ExcludeUserIds(1001, 1002)
    .ExecuteAsync();

模块间调用

[ActionController(cmd: 2)]
public class BattleController
{
    [ActionMethod(subCmd: 1)]
    public async Task<BattleStartResponse> StartBattle(
        BattleStartRequest request,
        FlowContext context)
    {
        // 调用用户模块获取玩家信息
        var userInfo = await context.ModuleInvoker.InvokeAsync<UserInfo>(
            CmdKit.GetMergeCmd(1, 2),  // User 模块的 GetInfo
            context);

        var battle = await CreateBattle(userInfo, request);
        return new BattleStartResponse { BattleId = battle.Id };
    }
}

异常处理

内置异常

异常类型 错误码 说明
BusinessException 自定义 业务异常
ValidationException 202 验证失败
UnauthorizedAccessException 100 未授权
TimeoutException 501 超时
OperationCanceledException 502 已取消

抛出业务异常

[ActionMethod(subCmd: 1)]
public async Task<Response> DoSomething(Request request, FlowContext context)
{
    var item = await _repository.FindAsync(request.Id);
    if (item == null)
    {
        throw new BusinessException(ErrorCodes.NotFound, "物品不存在");
    }
    // ...
}

自定义异常处理器

public class MyExceptionHandler : ISingleExceptionHandler
{
    public int Order => 5;

    public bool CanHandle(Exception exception) => exception is MyCustomException;

    public ResponseMessage Handle(Exception exception, ActionPipelineContext context)
    {
        var ex = (MyCustomException)exception;
        return ResponseMessage.Failure(context.ActionInfo.CmdMerge, ex.Code, ex.Message);
    }
}

// 注册
services.AddExceptionHandler<MyExceptionHandler>();

Result 模式集成

[ActionMethod(subCmd: 1)]
public async Task<ResponseMessage> UpdateUserInfo(
    UpdateUserRequest request,
    FlowContext context)
{
    // 业务层返回 Result<T>
    Result<UserInfo> result = await _userService.UpdateAsync(context.UserId, request);

    // 转换为 ResponseMessage
    return result.ToResponseMessage(context.CmdMerge);
}

// 带转换函数
[ActionMethod(subCmd: 2)]
public async Task<ResponseMessage> GetProfile(FlowContext context)
{
    Result<User> result = await _userService.GetByIdAsync(context.UserId);

    return result.ToResponseMessage(
        context.CmdMerge,
        user => user.ToProfileProto());
}

错误码定义

public static class ErrorCodes
{
    // 成功
    public const int Success = 0;

    // 通用错误 (1-99)
    public const int Unknown = 1;
    public const int InternalError = 2;

    // 认证/授权错误 (100-199)
    public const int Unauthorized = 100;
    public const int Forbidden = 101;
    public const int TokenExpired = 102;

    // 请求错误 (200-299)
    public const int InvalidRequest = 200;
    public const int InvalidArgument = 201;
    public const int ValidationError = 202;
    public const int MissingParameter = 203;

    // 资源错误 (300-399)
    public const int NotFound = 300;
    public const int AlreadyExists = 301;

    // 状态错误 (400-499)
    public const int InvalidOperation = 400;
    public const int InvalidState = 401;

    // 限流/超时 (500-599)
    public const int RateLimited = 500;
    public const int Timeout = 501;
    public const int Cancelled = 502;

    // 路由错误 (1000+)
    public const int RouteNotFound = 1001;
    public const int ActionNotFound = 1003;
}

目录结构

T2FGame.Action/
├── Attributes/
│   ├── ActionControllerAttribute.cs    # 控制器注解
│   ├── ActionMethodAttribute.cs        # 方法注解
│   ├── ActionCacheAttribute.cs         # 缓存注解
│   ├── RateLimitAttribute.cs           # 限流注解
│   └── IgnoreDebugInOutAttribute.cs    # 忽略调试输出注解
├── Context/
│   ├── FlowContext.cs                  # 请求上下文基类
│   ├── ResponseMessage.cs              # 响应消息
│   └── IModuleInvoker.cs               # 模块调用接口
├── Extensions/
│   └── ResultExtensions.cs             # Result 转换扩展
├── Invoker/
│   └── ModuleInvoker.cs                # 模块调用实现
├── Pipeline/
│   ├── ActionPipeline.cs               # 处理管道
│   ├── ActionPipelineContext.cs        # 管道上下文
│   ├── IActionHandler.cs               # 处理器接口
│   ├── IExceptionHandler.cs            # 异常处理器
│   ├── PipelineServiceExtensions.cs    # 服务注册
│   └── Handlers/
│       ├── TraceHandler.cs             # 链路追踪
│       ├── TimeoutHandler.cs           # 超时控制
│       ├── RateLimitHandler.cs         # 限流处理
│       ├── AuthenticationHandler.cs    # 认证处理
│       ├── AuthorizationHandler.cs     # 授权处理
│       ├── ValidationHandler.cs        # 参数验证
│       ├── CacheHandler.cs             # 缓存处理
│       ├── ActionInvokeHandler.cs      # Action 执行
│       ├── DebugInOutHandler.cs        # 调试输出
│       └── LoggingHandler.cs           # 日志/指标
├── Broadcast/
│   ├── IBroadcastService.cs            # 广播服务接口
│   ├── IBroadcastOrderService.cs       # 顺序广播服务接口
│   ├── IBroadcastRegistry.cs           # 广播注册表接口
│   ├── ISessionManager.cs              # 会话管理接口
│   ├── IRoomManager.cs                 # 房间管理接口
│   ├── RangeBroadcaster.cs             # 范围广播工具
│   ├── BroadcastLogger.cs              # 广播日志
│   └── BroadcastMessageAttribute.cs    # 广播消息注解
├── Registry/
│   ├── ActionMethodRegistry.cs         # Action 注册表
│   └── ActionMethodInfo.cs             # Action 元信息
├── Utils/
│   └── UserIdSettingKit.cs             # 用户 ID 设置工具
└── DependencyInjection.cs              # 依赖注入扩展

Source Generator 使用

使用 T2FGame.CodeGen.Action Source Generator 可以消除运行时反射,提升性能:

添加引用

<ItemGroup>
  
  <PackageReference Include="T2FGame.Action" />

  
  <PackageReference Include="T2FGame.CodeGen.Action"
                    OutputItemType="Analyzer"
                    ReferenceOutputAssembly="false" />
</ItemGroup>

使用生成的注册方法

// Source Generator 会在编译时生成 AddT2FGameActionGenerated 扩展方法
services.AddT2FGameActionGenerated();
services.AddActionPipeline();

生成的代码

编译后会在项目中生成以下文件:

  • GeneratedActionInvoker.g.cs - 零反射的方法调用器
  • GeneratedActionRegistry.g.cs - 编译时生成的 Action 注册表
  • GeneratedDependencyInjectionExtensions.g.cs - AddT2FGameActionGenerated() 扩展方法

详细信息请参阅 T2FGame.CodeGen.Action README。

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on T2FGame.Action:

Package Downloads
T2FGame

T2FGame Framework - A high-performance distributed game server framework inspired by ioGame. This meta-package includes all T2FGame components.

T2FGame.Network.Socket

T2FGame Framework - High-performance Socket server based on SuperSocket (TCP/UDP/WebSocket)

T2FGame.Room

T2FGame Framework - Game room framework for multiplayer games, inspired by ioGame's RoomKit

T2FGame.Cluster.Orleans

A high-performance game server framework

T2FGame.Cache.Redis

T2FGame Framework - Redis distributed cache implementation for high-performance caching

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.9 584 12/11/2025
1.0.8 559 12/11/2025
1.0.7 549 12/11/2025
1.0.6 544 12/11/2025
1.0.5 554 12/10/2025
1.0.4 557 12/10/2025
1.0.3 565 12/9/2025
1.0.2 469 12/8/2025
1.0.1 601 12/1/2025
1.0.0 287 11/28/2025
0.0.1 478 12/8/2025