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.Cluster.Orleans

A high-performance game server framework

T2FGame.Room

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

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 495 12/11/2025
1.0.8 500 12/11/2025
1.0.7 495 12/11/2025
1.0.6 490 12/11/2025
1.0.5 498 12/10/2025
1.0.4 506 12/10/2025
1.0.3 503 12/9/2025
1.0.2 413 12/8/2025
1.0.1 550 12/1/2025
1.0.0 231 11/28/2025
0.0.1 418 12/8/2025