asp.net-web-api – WebApi v2属性路由的根前缀

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

所以说我有一个带有以下Controller的模块

[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这样的东西

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

最佳答案 如果我没有错,你基本上正在寻找一个钩子,你可以检查web api的属性路由探测机制生成的end route-prefix或route-template,并在它们被添加到路由表之前修改它们.通过这种方式,您可以插入具有归属控制器的第三方模块并修改其路由模板,以防止它们与其他模块或您自己的应用程序路由混乱……是吗?

Web API提供类似IRoutePrefix和RoutePrefixAttribute(实现IRoutePrefix)的类型,以支持创建自定义RoutePrefix,您可以在其中更改路由前缀.但是从您正在寻找的情景中我认为这不会有太大帮助.

不幸的是,我不能在Web API中推荐一个好方法来做到这一点.在设计时会出现这种情况,但由于时间限制,我们无法提供必要的挂钩.

更新:
根据您的评论,下面是一个示例,其中我创建自定义路由属性并通过在路由模板添加到路由表之前将模块名称添加到路由模板来修改模板.
正如我的评论中所提到的,如果您拥有所有模块,则可以使用此方法,因为您可以使用此自定义路由属性.如果您期望第三方模块,那么您必须与他们达成协议,以便他们也使用相同的属性.但这很容易出错,比如他们不使用这个自定义属性而是使用默认的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();
    }
}
点赞