ASP.NET Web API 'de HTTP Ileti IşleyicileriHTTP Message Handlers in ASP.NET Web API

, Mike te sonby Mike Wasson

İleti işleyicisi , http isteği alan ve http yanıtı döndüren bir sınıftır.A message handler is a class that receives an HTTP request and returns an HTTP response. İleti işleyicileri soyut HttpMessageHandler sınıfından türetilir.Message handlers derive from the abstract HttpMessageHandler class.

Genellikle, bir dizi ileti işleyicisi birlikte zincirleme yapılır.Typically, a series of message handlers are chained together. İlk işleyici bir HTTP isteği alır, bazı işlemleri yapar ve isteği bir sonraki işleyiciye verir.The first handler receives an HTTP request, does some processing, and gives the request to the next handler. Bir noktada yanıt oluşturulur ve zinciri yedekler.At some point, the response is created and goes back up the chain. Bu düzene temsilci seçme işleyicisi denir.This pattern is called a delegating handler.

Sunucu tarafı Ileti IşleyicileriServer-Side Message Handlers

Sunucu tarafında, Web API 'SI ardışık düzeni bazı yerleşik ileti işleyicileri kullanır:On the server side, the Web API pipeline uses some built-in message handlers:

  • HttpServer , konaktan gelen isteği alır.HttpServer gets the request from the host.
  • Httproutingdispatcher , isteği yola göre dağıtır.HttpRoutingDispatcher dispatches the request based on the route.
  • Httpcontrollerdispatcher , Isteği BIR Web API denetleyicisine gönderir.HttpControllerDispatcher sends the request to a Web API controller.

Komut hattına özel işleyiciler ekleyebilirsiniz.You can add custom handlers to the pipeline. İleti işleyicileri, HTTP iletileri düzeyinde (denetleyici eylemleri yerine) çalışan çapraz kesme sorunları için uygundur.Message handlers are good for cross-cutting concerns that operate at the level of HTTP messages (rather than controller actions). Örneğin, bir ileti işleyicisi şunları içerebilir:For example, a message handler might:

  • İstek üst bilgilerini okuyun veya değiştirin.Read or modify request headers.
  • Yanıtlara bir yanıt üst bilgisi ekleyin.Add a response header to responses.
  • İstekleri denetleyiciye ulaşmadan önce doğrulayın.Validate requests before they reach the controller.

Bu diyagramda, ardışık düzene yerleştirilmiş iki özel işleyici gösterilmektedir:This diagram shows two custom handlers inserted into the pipeline:

Note

İstemci tarafında HttpClient Ayrıca ileti işleyicilerini kullanır.On the client side, HttpClient also uses message handlers. Daha fazla bilgi için bkz. HttpClient Ileti işleyicileri.For more information, see HttpClient Message Handlers.

Özel Ileti IşleyicileriCustom Message Handlers

Özel bir ileti işleyicisi yazmak için, System .net. http. DelegatingHandler 'ten türetirsiniz ve sendadsync yöntemini geçersiz kılın.To write a custom message handler, derive from System.Net.Http.DelegatingHandler and override the SendAsync method. Bu yöntem aşağıdaki imzaya sahiptir:This method has the following signature:

Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken);

Yöntemi, giriş olarak bir HttpRequestMessage alır ve zaman uyumsuz bir HttpResponseMessagedöndürür.The method takes an HttpRequestMessage as input and asynchronously returns an HttpResponseMessage. Tipik bir uygulama şunları yapar:A typical implementation does the following:

  1. İstek iletisini işleyin.Process the request message.
  2. İsteği iç işleyiciye göndermek için base.SendAsync çağırın.Call base.SendAsync to send the request to the inner handler.
  3. İç işleyici bir yanıt iletisi döndürür.The inner handler returns a response message. (Bu adım zaman uyumsuzdur.)(This step is asynchronous.)
  4. Yanıtı işleyin ve çağırana döndürün.Process the response and return it to the caller.

Önemsiz bir örnek aşağıda verilmiştir:Here is a trivial example:

public class MessageHandler1 : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Debug.WriteLine("Process request");
        // Call the inner handler.
        var response = await base.SendAsync(request, cancellationToken);
        Debug.WriteLine("Process response");
        return response;
    }
}

Note

base.SendAsync çağrısı zaman uyumsuzdur.The call to base.SendAsync is asynchronous. İşleyici Bu çağrıdan sonra herhangi bir çalışmayı yapar, gösterildiği gibi await anahtar sözcüğünü kullanın.If the handler does any work after this call, use the await keyword, as shown.

Ayrıca, bir temsilci işleyicisi iç işleyiciyi atlayabilir ve yanıtı doğrudan oluşturabilir:A delegating handler can also skip the inner handler and directly create the response:

public class MessageHandler2 : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Create the response.
        var response = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StringContent("Hello!")
        };

        // Note: TaskCompletionSource creates a task that does not contain a delegate.
        var tsc = new TaskCompletionSource<HttpResponseMessage>();
        tsc.SetResult(response);   // Also sets the task state to "RanToCompletion"
        return tsc.Task;
    }
}

Bir temsilci işleyicisi base.SendAsyncçağrılmadan yanıt oluşturursa, istek işlem hattının geri kalanını atlar.If a delegating handler creates the response without calling base.SendAsync, the request skips the rest of the pipeline. Bu, isteği doğrulayan bir işleyici için yararlı olabilir (hata yanıtı oluşturma).This can be useful for a handler that validates the request (creating an error response).

İşlem hattına Işleyici eklemeAdding a Handler to the Pipeline

Sunucu tarafında bir ileti işleyicisi eklemek için, işleyiciyi HttpConfiguration. MessageHandlers koleksiyonuna ekleyin.To add a message handler on the server side, add the handler to the HttpConfiguration.MessageHandlers collection. Projeyi oluşturmak için "ASP.NET MVC 4 Web uygulaması" şablonunu kullandıysanız, bunu WebApiConfig sınıfının içinde yapabilirsiniz:If you used the "ASP.NET MVC 4 Web Application" template to create the project, you can do this inside the WebApiConfig class:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MessageHandlers.Add(new MessageHandler1());
        config.MessageHandlers.Add(new MessageHandler2());

        // Other code not shown...
    }
}

İleti işleyicileri Messagehandlers koleksiyonunda göründükleri sırada çağrılır.Message handlers are called in the same order that they appear in MessageHandlers collection. İç içe olduklarından yanıt iletisi diğer yönde hareket eder.Because they are nested, the response message travels in the other direction. Diğer bir deyişle, son işleyici yanıt iletisini ilk kez alır.That is, the last handler is the first to get the response message.

İç işleyicileri ayarlamanız gerekmez; Web API çerçevesi, ileti işleyicilerini otomatik olarak bağlar.Notice that you don't need to set the inner handlers; the Web API framework automatically connects the message handlers.

Kendi kendine barındırıyorsanız, Httpselfhostconfiguration sınıfının bir örneğini oluşturun ve işleyicileri messagehandlers koleksiyonuna ekleyin.If you are self-hosting, create an instance of the HttpSelfHostConfiguration class and add the handlers to the MessageHandlers collection.

var config = new HttpSelfHostConfiguration("http://localhost");
config.MessageHandlers.Add(new MessageHandler1());
config.MessageHandlers.Add(new MessageHandler2());

Şimdi özel ileti işleyicilerinin bazı örneklerine göz atalım.Now let's look at some examples of custom message handlers.

Örnek: X-HTTP-Method-overrideExample: X-HTTP-Method-Override

X-HTTP-Method-override standart olmayan bir HTTP üst bilgisi.X-HTTP-Method-Override is a non-standard HTTP header. PUT veya DELETE gibi belirli HTTP istek türlerini gönderemediği istemciler için tasarlanmıştır.It is designed for clients that cannot send certain HTTP request types, such as PUT or DELETE. Bunun yerine, istemci bir POST isteği gönderir ve X-HTTP-Method-override üst bilgisini istenen metoda ayarlar.Instead, the client sends a POST request and sets the X-HTTP-Method-Override header to the desired method. Örneğin:For example:

X-HTTP-Method-Override: PUT

X-HTTP-Method-override için destek ekleyen bir ileti işleyicisi aşağıda verilmiştir:Here is a message handler that adds support for X-HTTP-Method-Override:

public class MethodOverrideHandler : DelegatingHandler      
{
    readonly string[] _methods = { "DELETE", "HEAD", "PUT" };
    const string _header = "X-HTTP-Method-Override";

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Check for HTTP POST with the X-HTTP-Method-Override header.
        if (request.Method == HttpMethod.Post && request.Headers.Contains(_header))
        {
            // Check if the header value is in our methods list.
            var method = request.Headers.GetValues(_header).FirstOrDefault();
            if (_methods.Contains(method, StringComparer.InvariantCultureIgnoreCase))
            {
                // Change the request method.
                request.Method = new HttpMethod(method);
            }
        }
        return base.SendAsync(request, cancellationToken);
    }
}

Sendadsync yönteminde, işleyici istek ILETISININ bir post isteği olup olmadığını ve X-http-Method-override üst bilgisini içerip içermediğini denetler.In the SendAsync method, the handler checks whether the request message is a POST request, and whether it contains the X-HTTP-Method-Override header. Bu durumda, üst bilgi değerini doğrular ve sonra istek metodunu değiştirir.If so, it validates the header value, and then modifies the request method. Son olarak, işleyici iletiyi sonraki işleyiciye geçirmek için base.SendAsync çağırır.Finally, the handler calls base.SendAsync to pass the message to the next handler.

İstek httpcontrollerdispatcher sınıfına ulaştığında, httpcontrollerdispatcher isteği güncelleştirilmiş istek yöntemine göre yönlendirir.When the request reaches the HttpControllerDispatcher class, HttpControllerDispatcher will route the request based on the updated request method.

Örnek: özel yanıt üst bilgisi eklemeExample: Adding a Custom Response Header

Her yanıt iletisine özel bir üst bilgi ekleyen bir ileti işleyicisi aşağıda verilmiştir:Here is a message handler that adds a custom header to every response message:

// .Net 4.5
public class CustomHeaderHandler : DelegatingHandler
{
    async protected override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
        response.Headers.Add("X-Custom-Header", "This is my custom header.");
        return response;
    }
}

İlk olarak, işleyici isteği iç ileti işleyicisine geçirmek için base.SendAsync çağırır.First, the handler calls base.SendAsync to pass the request to the inner message handler. İç işleyici bir yanıt iletisi döndürür, ancak bunu zaman uyumsuz bir görev<t> nesnesi kullanarak yapar.The inner handler returns a response message, but it does so asynchronously using a Task<T> object. Yanıt iletisi base.SendAsync zaman uyumsuz olarak tamamlanana kadar kullanılamaz.The response message is not available until base.SendAsync completes asynchronously.

Bu örnek, SendAsync tamamlandıktan sonra zaman uyumsuz olarak çalışmayı gerçekleştirmek için await anahtar sözcüğünü kullanır.This example uses the await keyword to perform work asynchronously after SendAsync completes. .NET Framework 4,0 ' i hedefliyorsanız,<T>görevini kullanın . Yönteme devam edin:If you are targeting .NET Framework 4.0, use the Task<T>.ContinueWith method:

public class CustomHeaderHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return base.SendAsync(request, cancellationToken).ContinueWith(
            (task) =>
            {
                HttpResponseMessage response = task.Result;
                response.Headers.Add("X-Custom-Header", "This is my custom header.");
                return response;
            }
        );
    }
}

Örnek: bir API anahtarı denetleniyorExample: Checking for an API Key

Bazı Web Hizmetleri, istemcilerin istemesi için bir API anahtarı içermesini gerektirir.Some web services require clients to include an API key in their request. Aşağıdaki örnek bir ileti işleyicisinin geçerli bir API anahtarı için istekleri nasıl denetlegösterdiğini gösterir:The following example shows how a message handler can check requests for a valid API key:

public class ApiKeyHandler : DelegatingHandler
{
    public string Key { get; set; }

    public ApiKeyHandler(string key)
    {
        this.Key = key;
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (!ValidateKey(request))
        {
            var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(response);    
            return tsc.Task;
        }
        return base.SendAsync(request, cancellationToken);
    }

    private bool ValidateKey(HttpRequestMessage message)
    {
        var query = message.RequestUri.ParseQueryString();
        string key = query["key"];
        return (key == Key);
    }
}

Bu işleyici, URI sorgu dizesinde API anahtarını arar.This handler looks for the API key in the URI query string. (Bu örnekte, anahtarın bir statik dize olduğunu varsaytık.(For this example, we assume that the key is a static string. Gerçek bir uygulama, büyük olasılıkla daha karmaşık doğrulama kullanır.) Sorgu dizesi anahtarı içeriyorsa, işleyici isteği iç işleyiciye geçirir.A real implementation would probably use more complex validation.) If the query string contains the key, the handler passes the request to the inner handler.

İstek geçerli bir anahtara sahip değilse, işleyici durum 403 olan bir yanıt iletisi oluşturur, yasak.If the request does not have a valid key, the handler creates a response message with status 403, Forbidden. Bu durumda, işleyici base.SendAsyncçağırmaz, bu nedenle iç işleyici isteği hiçbir şekilde almaz veya denetleyici yapmaz.In this case, the handler does not call base.SendAsync, so the inner handler never receives the request, nor does the controller. Bu nedenle, denetleyici tüm gelen isteklerin geçerli bir API anahtarı olduğunu varsayabilir.Therefore, the controller can assume that all incoming requests have a valid API key.

Note

API anahtarı yalnızca belirli denetleyici eylemlerine geçerliyse, ileti işleyicisi yerine bir eylem filtresi kullanmayı düşünün.If the API key applies only to certain controller actions, consider using an action filter instead of a message handler. İşlem filtreleri URI yönlendirmesi gerçekleştirildikten sonra çalışır.Action filters run after URI routing is performed.

Yol başına Ileti IşleyicileriPer-Route Message Handlers

HttpConfiguration. messagehandlers koleksiyonundaki işleyiciler küresel olarak uygulanır.Handlers in the HttpConfiguration.MessageHandlers collection apply globally.

Alternatif olarak, yolu tanımlarken belirli bir yola bir ileti işleyicisi ekleyebilirsiniz:Alternatively, you can add a message handler to a specific route when you define the route:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "Route1",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        config.Routes.MapHttpRoute(
            name: "Route2",
            routeTemplate: "api2/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional },
            constraints: null,
            handler: new MessageHandler2()  // per-route message handler
        );

        config.MessageHandlers.Add(new MessageHandler1());  // global message handler
    }
}

Bu örnekte, istek URI 'SI "Route2" ile eşleşiyorsa, istek MessageHandler2gönderilir.In this example, if the request URI matches "Route2", the request is dispatched to MessageHandler2. Aşağıdaki diyagramda bu iki yolun işlem hattı gösterilmektedir:The following diagram shows the pipeline for these two routes:

MessageHandler2 varsayılan Httpcontrollerdispatcher'ın yerini aldığını unutmayın.Notice that MessageHandler2 replaces the default HttpControllerDispatcher. Bu örnekte, MessageHandler2 yanıtı oluşturur ve "Route2" ile eşleşen istekler hiçbir şekilde denetleyiciye gitmez.In this example, MessageHandler2 creates the response, and requests that match "Route2" never go to a controller. Bu, tüm Web API denetleyici mekanizmasını kendi özel uç noktanızla değiştirmenize olanak sağlar.This lets you replace the entire Web API controller mechanism with your own custom endpoint.

Alternatif olarak, yönlendirme başına ileti işleyicisi, daha sonra bir denetleyiciye dağıtırsa Httpcontrollerdispatcheröğesine temsilci seçebilir.Alternatively, a per-route message handler can delegate to HttpControllerDispatcher, which then dispatches to a controller.

Aşağıdaki kod, bu yolun nasıl yapılandırılacağını göstermektedir:The following code shows how to configure this route:

// List of delegating handlers.
DelegatingHandler[] handlers = new DelegatingHandler[] {
    new MessageHandler3()
};

// Create a message handler chain with an end-point.
var routeHandlers = HttpClientFactory.CreatePipeline(
    new HttpControllerDispatcher(config), handlers);

config.Routes.MapHttpRoute(
    name: "Route2",
    routeTemplate: "api2/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional },
    constraints: null,
    handler: routeHandlers
);