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

能否在非WebAPI项目中复刻ASP.NET RouteAttribute的功能?

能否在非WebAPI项目中复刻ASP.NET RouteAttribute的功能?

当然可以!我之前在控制台项目里捣鼓过完全类似的功能,核心就是照着ASP.NET路由的思路,拆成几个小模块来实现:自定义标记属性、路由模板解析、命令匹配执行这几步,说起来不难,咱们一步步来。

首先,先定义一个和ASP.NET的[Route]几乎一样的自定义属性,用来标记你的方法:

// 自定义Route属性,标记在方法上,支持绑定多个路由
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
public class RouteAttribute : Attribute
{
    public string Template { get; }

    public RouteAttribute(string template)
    {
        Template = template ?? throw new ArgumentNullException(nameof(template));
    }
}

接下来是核心的路由模板解析,要能识别{param}这种占位符,把模板拆成固定段和参数段:

// 简单的路由模板解析器,处理{param}格式的占位符
public class RouteTemplateParser
{
    public static (string[] Segments, Dictionary<string, int> ParameterPositions) Parse(string template)
    {
        var segments = template.Split('/')
                               .Select(s => s.Trim())
                               .Where(s => !string.IsNullOrEmpty(s))
                               .ToArray();
        var paramPositions = new Dictionary<string, int>();

        for (int i = 0; i < segments.Length; i++)
        {
            var segment = segments[i];
            if (segment.StartsWith("{") && segment.EndsWith("}"))
            {
                var paramName = segment.Substring(1, segment.Length - 2);
                paramPositions[paramName] = i;
                // 把占位符替换成统一标记,方便后续匹配对比
                segments[i] = "{param}";
            }
        }

        return (segments, paramPositions);
    }
}

然后是路由注册和匹配的核心类,负责扫描程序集里的标记方法,然后在输入命令的时候匹配并执行:

// 路由核心类,负责注册路由、匹配并执行对应的方法
public class Router
{
    private readonly List<RouteRegistration> _registeredRoutes = new List<RouteRegistration>();

    // 扫描指定程序集,注册所有标记了RouteAttribute的方法
    public void RegisterRoutesFromAssembly(Assembly assembly)
    {
        var targetMethods = assembly.GetTypes()
                                    .SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
                                    .Where(m => m.GetCustomAttributes<RouteAttribute>().Any());

        foreach (var method in targetMethods)
        {
            foreach (var routeAttr in method.GetCustomAttributes<RouteAttribute>())
            {
                var (segments, paramPositions) = RouteTemplateParser.Parse(routeAttr.Template);
                _registeredRoutes.Add(new RouteRegistration
                {
                    TargetMethod = method,
                    RouteSegments = segments,
                    ParameterPositions = paramPositions
                });
            }
        }
    }

    // 匹配输入的路由命令,执行对应的方法
    public object? MatchAndExecute(string inputCommand, object? instance = null)
    {
        var inputSegments = inputCommand.Split('/')
                                        .Select(s => s.Trim())
                                        .Where(s => !string.IsNullOrEmpty(s))
                                        .ToArray();

        foreach (var route in _registeredRoutes)
        {
            // 先判断段数是否一致,不一致直接跳过当前路由
            if (route.RouteSegments.Length != inputSegments.Length)
                continue;

            var paramValues = new Dictionary<string, string>();
            bool isMatch = true;

            for (int i = 0; i < route.RouteSegments.Length; i++)
            {
                var routeSeg = route.RouteSegments[i];
                var inputSeg = inputSegments[i];

                if (routeSeg == "{param}")
                {
                    // 提取参数名和对应用户输入的值
                    var paramName = route.ParameterPositions.First(p => p.Value == i).Key;
                    paramValues[paramName] = inputSeg;
                }
                else if (routeSeg != inputSeg)
                {
                    // 固定段不匹配,直接结束当前路由的判断
                    isMatch = false;
                    break;
                }
            }

            if (isMatch)
            {
                // 准备方法的参数值,做简单的类型转换
                var methodParams = route.TargetMethod.GetParameters();
                var invokeParams = new object[methodParams.Length];

                for (int i = 0; i < methodParams.Length; i++)
                {
                    var param = methodParams[i];
                    if (paramValues.TryGetValue(param.Name, out var value))
                    {
                        invokeParams[i] = Convert.ChangeType(value, param.ParameterType);
                    }
                    else
                    {
                        // 处理可选参数或默认值,这里做简化处理
                        invokeParams[i] = param.DefaultValue ?? Activator.CreateInstance(param.ParameterType);
                    }
                }

                // 执行目标方法并返回结果
                return route.TargetMethod.Invoke(instance, invokeParams);
            }
        }

        return null; // 没有匹配到任何对应的路由
    }

    // 内部类,存储路由与方法的映射关系
    private class RouteRegistration
    {
        public MethodInfo TargetMethod { get; set; } = null!;
        public string[] RouteSegments { get; set; } = null!;
        public Dictionary<string, int> ParameterPositions { get; set; } = null!;
    }
}

最后是在控制台项目里的实际用法,非常直观:

// 定义你的命令类,用Route属性标记需要绑定路由的方法
public class MyConsoleCommands
{
    [Route("testFunc/{param}")]
    public void TestFunction(string param)
    {
        Console.WriteLine($"执行TestFunction,传入的参数是:{param}");
    }

    [Route("greet/{username}/{age}")]
    public string GreetUser(string username, int age)
    {
        return $"嗨,{username}!你今年{age}岁啦~";
    }
}

class Program
{
    static void Main(string[] args)
    {
        var router = new Router();
        // 注册当前程序集里的所有路由
        router.RegisterRoutesFromAssembly(Assembly.GetExecutingAssembly());

        Console.WriteLine("请输入路由命令(比如 testFunc/hello 或者 greet/张三/25):");
        string userInput = Console.ReadLine()!;

        var executeResult = router.MatchAndExecute(userInput, new MyConsoleCommands());
        if (executeResult != null)
        {
            Console.WriteLine($"执行结果:{executeResult}");
        }
        else
        {
            Console.WriteLine("抱歉,没有匹配到对应的路由哦~");
        }
    }
}

当然啦,上面的代码是基础版,你还可以根据自己的需求扩展:

  • 支持路由约束:比如{param:int},在解析模板的时候提取约束类型,匹配时验证参数是否符合要求
  • 支持可选参数:比如[Route("test/{param?}")],解析的时候标记可选段,匹配时允许该段为空
  • 支持通配符:比如[Route("files/*")],处理任意后续的路径段
  • 添加路由优先级:如果多个路由匹配同一个命令,可以给路由加优先级属性,按优先级执行

我当时就是从这个基础版本开始,慢慢迭代成更贴合自己需求的路由系统,完全能达到和ASP.NET [Route]差不多的体验~

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 12:44:55