【问题标题】:Root Prefix to WebApi v2 Attribute RoutingWebApi v2 属性路由的根前缀
【发布时间】:2014-03-14 02:06:53
【问题描述】:

我目前正在开发一个可扩展的 WebApi,其中各个动态加载的模块可以注册自己的路由。我的原始版本是使用基于https://github.com/darrelmiller/ApiRouter 的技术使用 ASP.NET WebApi v1 构建的,但是我想使用 ASP.NET WebApi v2 提供的基于属性的路由

假设我有一个带有以下控制器的模块

[RoutePrefix("foo")]
public class FooController : ApiController {

    [Route("{id}")]
    public object GetFoo(int id) {
        return id;
    }
}

如果我只调用config.MapHttpAttributeRoutes(),那么该控制器将绑定到http://localhost/foo/2。我想做的是以编程方式更改控制器的路由前缀。所以它应该有望绑定到类似http://localhost/modules/FooModule/foo/2

有没有办法使用提供的工具来完成这项工作,还是我必须继续使用我原来的方法?

【问题讨论】:

    标签: asp.net-web-api asp.net-web-api-routing


    【解决方案1】:

    如果我没记错的话,您基本上是在寻找一个钩子,您可以在其中检查由 web api 的属性路由探测机制生成的结束路由前缀或路由模板,并在它们被添加到路由表之前对其进行修改。通过这种方式,您可以插入具有属性控制器的第 3 方模块并修改其路由模板,以防止它们与其他模块或您自己的应用程序的路由混淆……对吗?

    Web API 提供 IRoutePrefixRoutePrefixAttribute (实现 IRoutePrefix)等类型,以支持创建自定义 RoutePrefixes,您可以在其中更改路由前缀。但从您正在寻找的场景来看,我认为这没有多大帮助。

    不幸的是,我无法推荐 Web API 中的好方法来执行此操作。这种场景在设计时就出现了,但由于时间限制,我们无法提供必要的钩子。

    更新
    根据您的评论,以下是我创建自定义路由属性并修改模板的示例,方法是在将模块名称添加到路由模板之前将其添加到路由表。 正如我在评论中提到的,如果您拥有所有模块,则可以使用此方法,因为您可以使用此自定义路由属性。如果您期望 3rd 方模块,那么您必须与他们达成协议,以便他们也使用相同的属性。但这很容易出错,例如在他们不使用此自定义属性而是使用默认 RouteAttribute 的情况下......

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
    public class MyRouteAttribute : Attribute, IDirectRouteFactory
    {
        private readonly string _template;
    
        public MyRouteAttribute(string template)
        {
            _template = template;
        }
    
        public string Template
        {
            get { return _template; }
        }
    
        public string Name { get; set; }
    
        public int Order { get; set; }
    
        public virtual IDictionary<string, object> Defaults
        {
            get { return null; }
        }
    
        public virtual IDictionary<string, object> Constraints
        {
            get { return null; }
        }
    
        public virtual IDictionary<string, object> DataTokens
        {
            get { return null; }
        }
    
        public RouteEntry CreateRoute(DirectRouteFactoryContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
    
            IDirectRouteBuilder builder = context.CreateBuilder(Template);
    
            //You would receive the final template..that is RoutePrefix + the route template
            string finalTemplate = builder.Template;
    
            //Modify this template to have your ModuleName
            builder.Template = "modulename/" + finalTemplate;
    
            builder.Name = Name;
            builder.Order = Order;
    
            IDictionary<string, object> builderDefaults = builder.Defaults;
    
            if (builderDefaults == null)
            {
                builder.Defaults = Defaults;
            }
            else
            {
                IDictionary<string, object> defaults = Defaults;
    
                if (defaults != null)
                {
                    foreach (KeyValuePair<string, object> defaultItem in defaults)
                    {
                        builderDefaults[defaultItem.Key] = defaultItem.Value;
                    }
                }
            }
    
            IDictionary<string, object> builderConstraints = builder.Constraints;
    
            if (builderConstraints == null)
            {
                builder.Constraints = Constraints;
            }
            else
            {
                IDictionary<string, object> constraints = Constraints;
    
                if (constraints != null)
                {
                    foreach (KeyValuePair<string, object> constraint in constraints)
                    {
                        builderConstraints[constraint.Key] = constraint.Value;
                    }
                }
            }
    
            IDictionary<string, object> builderDataTokens = builder.DataTokens;
    
            if (builderDataTokens == null)
            {
                builder.DataTokens = DataTokens;
            }
            else
            {
                IDictionary<string, object> dataTokens = DataTokens;
    
                if (dataTokens != null)
                {
                    foreach (KeyValuePair<string, object> dataToken in dataTokens)
                    {
                        builderDataTokens[dataToken.Key] = dataToken.Value;
                    }
                }
            }
    
            return builder.Build();
        }
    }
    

    【讨论】:

    • 这就是我要找的。 IRoutePrefix 是从 DI 容器中检索的,还是仅用于 RoutePrefix 属性?我还注意到接口 IDirectRouteFactory,这有什么用吗?它的目的是什么?
    • No IRoutePrefix 不是从 DI 容器中检索到的,它对属性很有用(因为这是基于属性的路由)...当然,IDirectRouteFactory 或实现此接口的抽象类 RouteFactoryAttribute让您有机会定义自定义属性,您可以使用这些属性修改添加到路由表的最终路由模板......但如果您期望来自第 3 方的控制器,那么您必须与他们达成协议才能使用一个共同的属性...长话短说,如果所有模块都是您开发的,那么您可以使用IDirectRouteFactory方法
    • 这些模块都将在内部开发,所以理论上我可以使用自定义属性,但就像你提到的那样,它非常脆弱且易于使用 RouteAttribute 而不是自定义属性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-31
    • 2019-11-04
    • 2016-12-17
    • 1970-01-01
    • 2014-09-26
    • 2023-03-27
    • 1970-01-01
    相关资源
    最近更新 更多