Message handler là class nhận HTTP request và trả về HTTP response. Message handlers derive từ abstract HttpMessageHandler class. Message handlers cho phép process, edit, hoặc decline incoming request trước khi nó đến HttpControllerDispatcher.
Message handlers execute sớm hơn nhiều trong request processing pipeline, do đó chúng là nơi tuyệt vời để implement cross-cutting concerns trong Web API. Message Handlers là chuỗi các classes (có thể system-defined hoặc custom) nằm cạnh process HTTP request/response qua pipeline.
Server-Side Message Handlers
Trên server side, Web API pipeline dùng built-in message handlers:
- HttpServer - Nhận request từ host.
- HttpRoutingDispatcher - Dispatch request dựa trên route.
- HttpControllerDispatcher - Gửi request đến Web API controller.
Ta có thể thêm custom handlers vào pipeline. Message handlers tốt cho cross-cutting concerns operate ở HTTP message level (thay vì controller actions). Ví dụ message handler có thể:
- Read hoặc modify request headers.
- Add response header vào responses.
- Validate requests trước khi đến controller.
Client-Side HTTP Message Handlers
Trên client side, class HttpClient dùng message handler xử lý requests. Default handler là HttpClientHandler, gửi request qua network và nhận response từ server. Ta có thể insert custom message handlers vào client pipeline.
Pipeline Architecture
┌─────────────────────────────────────────────────────────────┐
│ HTTP REQUEST │
└─────────────────────────┬───────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ HttpServer (Top-level handler) │
│ - Receive request from host (IIS, Self-host, OWIN) │
└─────────────────────────┬───────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ Custom DelegatingHandlers (Added to config.MessageHandlers)│
│ 1. LoggingHandler → Pre/Post process │
│ 2. AuthenticationHandler → Validate token │
│ 3. CorsHandler → Add CORS headers │
│ 4. CompressionHandler → Compress response │
└─────────────────────────┬───────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ HttpRoutingDispatcher │
│ - Match route, select controller │
└─────────────────────────┬───────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ HttpControllerDispatcher │
│ - Create controller instance │
│ - Execute action, filters │
│ - Return response │
└─────────────────────────┬───────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ HTTP RESPONSE (Reverse order) │
└─────────────────────────────────────────────────────────────┘
HttpMessageHandler Base Class
public abstract class HttpMessageHandler {
protected internal abstract Task SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken);
protected virtual void Dispose(bool disposing) { ... }
public void Dispose() { ... }
}
DelegatingHandler (Chaining)
public class DelegatingHandler : HttpMessageHandler {
protected HttpMessageHandler InnerHandler { get; set; }
protected override async Task SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken) {
// Pre-processing
var response = await InnerHandler.SendAsync(request, cancellationToken);
// Post-processing
return response;
}
}
Custom Handler Examples
1. Request/Response Logging
public class LoggingHandler : DelegatingHandler {
protected override async Task SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken) {
var startTime = DateTime.UtcNow;
var requestBody = await request.Content.ReadAsStringAsync();
Debug.WriteLine($"REQUEST: {request.Method} {request.RequestUri}");
Debug.WriteLine($"Headers: {request.Headers}");
Debug.WriteLine($"Body: {requestBody}");
var response = await base.SendAsync(request, cancellationToken);
var duration = DateTime.UtcNow - startTime;
var responseBody = response.Content != null
? await response.Content.ReadAsStringAsync()
: "";
Debug.WriteLine($"RESPONSE: {response.StatusCode} ({duration.TotalMs}ms)");
Debug.WriteLine($"Body: {responseBody}");
return response;
}
}
2. API Key Validation (Short-circuit)
public class ApiKeyHandler : DelegatingHandler {
private const string ApiKeyHeader = "X-API-Key";
private readonly string _validApiKey;
public ApiKeyHandler(string validApiKey) {
_validApiKey = validApiKey;
}
protected override async Task SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken) {
if (!request.Headers.TryGetValues(ApiKeyHeader, out var values)
|| values.FirstOrDefault() != _validApiKey) {
var response = request.CreateResponse(HttpStatusCode.Unauthorized);
response.Content = new StringContent("Invalid API Key");
return response; // Short-circuit - không gọi base.SendAsync
}
return await base.SendAsync(request, cancellationToken);
}
}
3. Custom Header Injection
public class CustomHeaderHandler : DelegatingHandler {
protected override async Task SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken) {
var response = await base.SendAsync(request, cancellationToken);
// Thêm custom headers vào response
response.Headers.Add("X-Powered-By", "ASP.NET Web API");
response.Headers.Add("X-Version", "1.0.0");
response.Headers.Add("X-Response-Time", DateTime.UtcNow.ToString("O"));
return response;
}
}
Client-Side Handlers (HttpClient)
var handler = new HttpClientHandler();
var client = new HttpClient(new LoggingHandler(handler) {
InnerHandler = new RetryHandler(3, TimeSpan.FromSeconds(1)) {
InnerHandler = new AuthHandler("token") {
InnerHandler = handler
}
}
});
// Hoặc dùng extension (System.Net.Http.Extensions)
var client = HttpClientFactory.Create(
new LoggingHandler(),
new RetryHandler(3),
new AuthHandler("token"),
new HttpClientHandler()
);
Registration Order Quan Trọng
config.MessageHandlers.Add(new LoggingHandler()); // 1. Outer-most (first request, last response) config.MessageHandlers.Add(new AuthenticationHandler()); // 2. config.MessageHandlers.Add(new CorsHandler()); // 3. config.MessageHandlers.Add(new CompressionHandler()); // 4. Inner-most (last request, first response)
Quy tắc: Request flow: 1→2→3→4→Controller. Response flow: Controller→4→3→2→1.