Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Procedura obsługi komunikatów to klasa, która odbiera żądanie HTTP i zwraca odpowiedź HTTP. Programy obsługi komunikatów pochodzą z abstrakcyjnej klasy HttpMessageHandler .
Zazwyczaj szereg procedur obsługi komunikatów jest ze sobą łączony. Pierwsza procedura obsługi odbiera żądanie HTTP, wykonuje pewne przetwarzanie i przekazuje żądanie następnej procedurze obsługi. W pewnym momencie odpowiedź jest tworzona i przepływa z powrotem w górę łańcucha. Ten wzorzec jest nazywany procedurą obsługi delegowania .
obsługiwacze wiadomości po stronie serwera
Po stronie serwera potok Web API używa niektórych wbudowanych handlerów komunikatów:
- Serwer HttpServer pobiera żądanie z hosta.
- HttpRoutingDispatcher wysyła żądanie na podstawie trasy.
- HttpControllerDispatcher wysyła żądanie do kontrolera internetowego interfejsu API.
Do pipeline'u można dodawać niestandardowe programy obsługi. Przechwytywacze wiadomości są przydatne w przypadku zagadnień przekrojowych, które działają na poziomie wiadomości HTTP (zamiast akcji kontrolera). Na przykład program obsługi komunikatów może:
- Odczytywanie lub modyfikowanie nagłówków żądań.
- Dodaj nagłówek do odpowiedzi.
- Zweryfikuj żądania, zanim dotrą do kontrolera.
Na tym diagramie przedstawiono dwa niestandardowe moduły obsługi wstawione do potoku.
Uwaga / Notatka
Po stronie klienta klient HttpClient używa również programów obsługi komunikatów. Aby uzyskać więcej informacji, zobacz Programy obsługi komunikatów HttpClient.
Niestandardowe programy obsługi komunikatów
Aby napisać niestandardową procedurę obsługi komunikatów, należy utworzyć metodę System.Net.Http.DelegatingHandler i zastąpić metodę SendAsync . Ta metoda ma następujący podpis:
Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken);
Metoda przyjmuje komunikat HttpRequestMessage jako dane wejściowe i asynchronicznie zwraca komunikat HttpResponseMessage. Typowa implementacja wykonuje następujące czynności:
- Przetwórz komunikat żądania.
- Wywołaj metodę
base.SendAsync, aby wysłać żądanie do procedury obsługi wewnętrznej. - Program obsługi wewnętrznej zwraca komunikat odpowiedzi. (Ten krok jest asynchroniczny).
- Przetwórz odpowiedź i zwróć do wywołującego.
Oto przykład trywialny:
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;
}
}
Uwaga / Notatka
Wywołanie metody base.SendAsync jest asynchroniczne. Jeśli program obsługi wykona jakąkolwiek pracę po tym wywołaniu, użyj słowa kluczowego await , jak pokazano poniżej.
Program obsługi delegowania może również pominąć program obsługi wewnętrznej i bezpośrednio utworzyć odpowiedź:
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;
}
}
Jeśli program obsługi delegowania tworzy odpowiedź bez wywoływania base.SendAsync metody, żądanie pomija resztę potoku. Może to być przydatne w przypadku procedury obsługi, która weryfikuje żądanie (tworzenie odpowiedzi na błąd).
Dodawanie programu obsługi do potoku
Aby dodać program obsługi komunikatów po stronie serwera, dodaj procedurę obsługi do kolekcji HttpConfiguration.MessageHandlers . Jeśli do utworzenia projektu użyto szablonu "ASP.NET MVC 4 Web Application", możesz to zrobić w klasie WebApiConfig :
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new MessageHandler1());
config.MessageHandlers.Add(new MessageHandler2());
// Other code not shown...
}
}
Programy obsługi komunikatów są wywoływane w takiej samej kolejności, jak w kolekcji MessageHandlers . Ponieważ są one zagnieżdżone, komunikat odpowiedzi jest przenoszony w innym kierunku. Oznacza to, że ostatni procedura obsługi jako pierwszy otrzymuje komunikat odpowiedzi.
Zwróć uwagę, że nie trzeba ustawiać wewnętrznych obsługujących; platforma Web API automatycznie łączy programy obsługi komunikatów.
Jeśli samodzielnie hostujesz, utwórz wystąpienie klasy HttpSelfHostConfiguration i dodaj programy obsługi do kolekcji MessageHandlers .
var config = new HttpSelfHostConfiguration("http://localhost");
config.MessageHandlers.Add(new MessageHandler1());
config.MessageHandlers.Add(new MessageHandler2());
Teraz przyjrzyjmy się niektórym przykładom niestandardowych procedur obsługi komunikatów.
Przykład: X-HTTP-Method-Override
X-HTTP-Method-Override jest niestandardowym nagłówkiem HTTP. Jest przeznaczony dla klientów, którzy nie mogą wysyłać określonych typów żądań HTTP, takich jak PUT lub DELETE. Zamiast tego klient wysyła żądanie POST i ustawia nagłówek X-HTTP-Method-Override na żądaną metodę. Przykład:
X-HTTP-Method-Override: PUT
Oto procedura obsługi komunikatów, która dodaje obsługę 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);
}
}
W metodzie SendAsync program obsługi sprawdza, czy komunikat żądania jest żądaniem POST i czy zawiera nagłówek X-HTTP-Method-Override. Jeśli tak, weryfikuje wartość nagłówka, a następnie modyfikuje metodę żądania. Na koniec program obsługi wywołuje base.SendAsync, aby przekazać komunikat do następnego programu obsługi.
Gdy żądanie osiągnie klasę HttpControllerDispatcher , httpControllerDispatcher będzie kierować żądanie na podstawie zaktualizowanej metody żądania.
Przykład: dodawanie niestandardowego nagłówka odpowiedzi
Oto procedura obsługi komunikatów, która dodaje niestandardowy nagłówek do każdego komunikatu odpowiedzi:
// .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;
}
}
Najpierw program obsługi wywołuje base.SendAsync, aby przekazać żądanie do wewnętrznego programu obsługi komunikatów. Program obsługi wewnętrznej zwraca komunikat odpowiedzi, ale wykonuje to asynchronicznie przy użyciu obiektu T< zadania>. Komunikat odpowiedzi nie jest dostępny, dopóki base.SendAsync nie zostanie ukończony asynchronicznie.
W tym przykładzie, aby po zakończeniu SendAsync asynchronicznie wykonywać pracę, użyto słowa kluczowego await. Jeśli korzystasz z .NET Framework 4.0, użyj metody Task<T>.ContinueWith:
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;
}
);
}
}
Przykład: sprawdzanie klucza API
Niektóre usługi internetowe wymagają, aby klienci dołączali klucz interfejsu API do żądania. W poniższym przykładzie pokazano, jak program obsługi komunikatów może sprawdzać żądania prawidłowego klucza interfejsu API:
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);
}
}
Ten uchwyt poszukuje klucza API w ciągu zapytania URI. (W tym przykładzie przyjęto założenie, że klucz jest ciągiem statycznym. Rzeczywista implementacja prawdopodobnie będzie używać bardziej złożonej weryfikacji). Jeśli ciąg zapytania zawiera klucz, procedura obsługi przekazuje żądanie do procedury obsługi wewnętrznej.
Jeśli żądanie nie ma prawidłowego klucza, program obsługi tworzy komunikat odpowiedzi o stanie 403 Zabronione. W takim przypadku program obsługi nie wywołuje base.SendAsync, więc wewnętrzny program obsługi nigdy nie odbiera żądania, podobnie jak kontroler. W związku z tym kontroler może przyjąć, że wszystkie żądania przychodzące mają prawidłowy klucz interfejsu API.
Uwaga / Notatka
Jeśli klucz interfejsu API ma zastosowanie tylko do niektórych akcji kontrolera, rozważ użycie filtru akcji zamiast programu obsługi komunikatów. Filtry akcji są uruchamiane po wykonaniu routingu identyfikatora URI.
Obsługiwacze wiadomości dla każdej trasy
Programy obsługi w kolekcji HttpConfiguration.MessageHandlers są stosowane globalnie.
Alternatywnie można dodać procedurę obsługi komunikatów do określonej trasy podczas definiowania trasy:
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
}
}
W tym przykładzie jeśli identyfikator URI żądania pasuje do "Route2", żądanie jest wysyłane do MessageHandler2. Na poniższym diagramie przedstawiono przepływ dla tych dwóch tras.
Zwróć uwagę, że MessageHandler2 zastępuje domyślną wartość HttpControllerDispatcher. W tym przykładzie MessageHandler2 tworzy odpowiedź, a żądania zgodne z trasą "Route2" nigdy nie przechodzą do kontrolera. Umożliwia to zastąpienie całego mechanizmu kontrolera interfejsu API własnym niestandardowym punktem końcowym.
Alternatywnie, program obsługi wiadomości na danej trasie może delegować do HttpControllerDispatcher, który następnie przekazuje ją do kontrolera.
Poniższy kod pokazuje, jak skonfigurować tę trasę:
// 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
);