Обработчики маршрутизации и сообщений: запрос на обработку запроса

У меня возникла проблема с порядком выполнения конвейера запросов веб-API ASP.NET.

В соответствии с документацией ASP.NET Web API (доступно здесь), глобальные обработчики сообщений должны быть выполнены до механизма маршрутизации.

Request pipeline

На этом изображении MessageHandler1 является глобальным обработчиком сообщений, тогда как MessageHandler2 относится к Маршруту 2.


Я создал очень простой пример, чтобы показать, что, похоже, проблема в порядке выполнения... или я действительно пропускаю что-то важное.

У меня есть этот контроллер

public class FooController : ApiController {
    [HttpPut]
    public string PutMe() {
        return Request.Method.Method;
    }
}

Он принимает только запросы PUT.

Приложение настроено как таковое:

protected void Application_Start() {
    var configuration = GlobalConfiguration.Configuration;

    configuration.MessageHandlers.Add( new SimpleMethodOverrideHandler() );
    configuration.Configuration.Routes.MapHttpRoute(
        name: "Foo",
        routeTemplate: "api/foo",
        defaults: new { controller = "foo", action = "putme" },
        constraints: new { put = new HttpPutOnlyConstraint() }
    );
}

SimpleMethodOverrideHandler - очень простой DelegatingHandler, который просто изменил метод запроса в соответствии с параметром "method" в строке запроса.

public class SimpleMethodOverrideHandler : DelegatingHandler {
    protected override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken ) {
        var method = request.RequestUri.ParseQueryString()["method"];
        if( !string.IsNullOrEmpty( method ) ) {
            request.Method = new HttpMethod( method );
        }
        return base.SendAsync( request, cancellationToken );
    }
}

В принципе, запрос /api/foo?method=put в моем браузере запускает метод FooController PutMe.
В самом деле, как видно ранее, обработчик сообщений обрабатывает запросы до того, как он будет передан в HttpRoutingDispatched.

Наконец, вот как определяется константа HttpPutOnlyConstraint:

public class HttpPutOnlyConstraint : IHttpRouteConstraint {
    public bool Match( HttpRequestMessage request,
                       IHttpRoute route,
                       string parameterName,
                       IDictionary<string, object> values,
                       HttpRouteDirection routeDirection ) {
        return request.Method == HttpMethod.Put;
    }
}

Хорошо, проблема в том, что когда я запрашиваю /api/foo?method=put в своем браузере, программа сначала вводит метод HttpPutOnlyConstraint Match, что неверно.

Если мы ссылаемся на ранее связанный образ, обработчик сообщения должен быть выполнен первым, к сожалению, это не так.

Итак, конечно, Match возвращает false, и никакой контроллер/действие не найдено для запроса, выполняется 404.

Если я удалю ограничение из определения маршрута, программа войдет в SimpleMethodOverrideHandler, метод запроса будет успешно изменен и сможет сопоставить и выполнить мой метод контроллера.

Я что-то делаю неправильно? Есть ли секретный параметр конфигурации, чтобы знать, чтобы делать такие вещи?: -)

Если кому-то нужен весь проект, он доступен здесь [zip файл 7KB].

Спасибо.

Ответ 1

Вы запутываете механизм маршрутизации с помощью конвейера Web API. HttpRoutingDispatcher не является концепцией двигателя маршрутизации. Ограничения маршрута будут обработаны сначала, потому что вашему базовому узлу необходимо построить таблицу маршрутов и сопоставить маршрут для вашего запроса.

HttpRoutingDispatcher - это просто другая реализация HttpMessageHandler, и все, что она делает, это проверка IHttpRoute маршрута, который был сопоставлен, и выбирает, к какому обработчику сообщения нужно позвонить дальше. Если нет никакого обработчика маршрута, он делегирует обработку HttpControllerDispatcher.