Пользовательский IHttpControllerSelector ASP.NET Web API для одного маршрута

Мне нужен кастом IHttpControllerSelector который должен применяться только к конкретному маршруту. Все остальные маршруты веб-API должны использовать реализацию по умолчанию IHttpControllerSelector,

Во время исследования я обнаружил следующий код, предназначенный для замены IHttpControllerSelector при запуске приложения, но он полностью заменяет селектор контроллера по умолчанию, в результате чего все маршруты в приложении используют мой пользовательский селектор контроллера:

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

Есть ли способ настроить IHttpControllerSelector для одного маршрута?

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});  
Другие вопросы по тегам