【问题标题】:ASP.NET Web API custom IHttpControllerSelector for a single route用于单个路由的 ASP.NET Web API 自定义 IHttpControllerSelector
【发布时间】:2014-11-12 22:02:45
【问题描述】:

我需要一个自定义的IHttpControllerSelector,它应该只应用于特定的路线。所有其他 web api 路由应使用IHttpControllerSelector 的默认实现。

在研究时,我发现以下代码旨在在应用程序启动时替换 IHttpControllerSelector,但它完全替换了默认控制器选择器,这导致应用程序中的所有路由都使用我的自定义控制器选择器:

config.Services.Replace(typeof(IHttpControllerSelector), 
                                                    new CustomControllerSelector(config));

有没有办法为单个路由配置IHttpControllerSelector

【问题讨论】:

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


    【解决方案1】:

    您可以将每个路由消息处理程序分配给需要使用不同控制器选择逻辑的路由。该处理程序将标记HttpRequestMessage 并带有一个标志,表明该请求需要被区别对待。

    然后简单地让 CustomControllerSelector 继承自 DefaultHttpControllerSelector 并检查该标志:

    • 如果已设置,请继续使用您的自定义逻辑
    • 如果未设置,返回基地 (DefaultHttpControllerSelector)

    代码如下:

    1) 消息处理程序,设置标志

    public class RouteSpecificHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            request.Properties["UseCustomSelector"] = true;
            return base.SendAsync(request, cancellationToken);
        }
    }
    

    2) 仅将每个路由消息处理程序分配给特定路由(不要为其他路由运行)

            config.Routes.MapHttpRoute(
                name: "MyRoute",
                routeTemplate: "api/dummy/{id}",
                defaults: new {controller = "Dummy", id = RouteParameter.Optional},
                constraints: null,
                handler: new RouteSpecificHandler { InnerHandler = new HttpControllerDispatcher(config) }
                );
    

    3) 尊重标志的自定义选择器:

    public class CustomSelector : DefaultHttpControllerSelector
    {
        public CustomSelector(HttpConfiguration configuration) : base(configuration)
        {
        }
    
        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            if (request.Properties.ContainsKey("UseCustomSelector") &&
                request.Properties["UseCustomSelector"] as bool? == true)
            {
                //your logic goes here
            }
    
            return base.SelectController(request);
        }
    }
    

    4) 注册选择器:

    config.Services.Replace(typeof(IHttpControllerSelector), new CustomSelector(config));
    

    编辑

    如果您不希望从 DefaultHttpControllerSelector 继承 - 然后直接实现 IHttpControllerSelector,而不是调用 base.SelectController(request) 将旧选择器保存为类中的字段/属性

    public class CustomSelector : IHttpControllerSelector
    {
        private HttpConfiguration _config;
    
        public IHttpControllerSelector PreviousSelector {get; set;}
    
        public CustomSelector(HttpConfiguration configuration)
        {
             _config = configuration;
        }
    
        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            if (request.Properties.ContainsKey("UseCustomSelector") &&
                request.Properties["UseCustomSelector"] as bool? == true)
            {
                //your logic goes here
            }
    
            return PreviousSelector.SelectController(request);
        }
    }
    

    那就改注册吧:

      var previousSelector = config.Services.GetService(typeof(IHttpControllerSelector)) as IHttpControllerSelector;
     config.Services.Replace(typeof(IHttpControllerSelector), new CustomSelector(config) { PreviousSelector = previousSelector});  
    

    【讨论】:

    • 感谢您详细解释的答案。这看起来很有希望,但是如果我不知道之前注册的IHttpControllerSelector 是不是DefaultHttpControllerSelector 怎么办?我正在开发 CMS 的扩展,其中其他扩展也可能会覆盖 IHttpControllerSelector。假设我的扩展是第二个覆盖它,第一个扩展的 IHttpControllerSelector 将不再使用。
    • 查看编辑以获取答案。提示 - 您应该在原始问题中包含此信息。
    • 太好了,再次感谢您!当我写我的问题时,我认为这些信息不相关,但是我应该包括它。
    • 看看 .net core 是如何完成的会很有趣。
    • 很好的答案@FilipW,有什么方法可以在 Asp.Net Core 中实现这种方法? IHttpControllerSelector 在那里不可用。
    猜你喜欢
    • 1970-01-01
    • 2012-12-29
    • 2020-07-24
    • 1970-01-01
    • 2015-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-23
    相关资源
    最近更新 更多