ใน Message Handler ชุดของ Message Handlers จะถูกเชื่อมต่อกันเป็น Chain Handler แรกจะรับ HTTP Request ทำ Processing แล้วส่ง Request ไปยัง Handler ถัดไป ในบางจุด Response จะถูกสร้างขึ้นและกลับขึ้นไปใน Chain รูปแบบนี้เรียกว่า Delegating Handler
นอกจาก Built-in Server-side Message Handlers แล้ว เรายังสามารถสร้าง Custom Server-Side HTTP Message Handlers ได้ ใน ASP.NET Web API เราใช้ DelegatingHandler ต้องสร้าง Class สืบทอดจาก System.Net.Http.DelegatingHandler คลาส Custom นั้นควร Override เมธอด SendAsync
Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
เมธอดนี้รับ HttpRequestMessage เป็น Input และคืนค่า HttpResponseMessage แบบ Asynchronous การ Implement แบบปกติจะทำดังนี้:
- Process Request Message
- Call
base.SendAsyncเพื่อส่ง Request ไปยัง Inner Handler - Inner Handler คืนค่า Response Message (Async)
- Process Response และ Return ให้ Caller
ตัวอย่าง: Logging Handler (Pass-through)
public class CustomMessageHandler : DelegatingHandler {
protected async override Task SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken) {
Debug.WriteLine("CustomMessageHandler processing the request");
// เรียก Inner Handler (สำคัญ: ต้อง await)
var response = await base.SendAsync(request, cancellationToken);
Debug.WriteLine("CustomMessageHandler processing the response");
return response;
}
}
ตัวอย่าง: Short-circuit Handler (Skip Inner Handler)
public class CustomMessageHandler : DelegatingHandler {
protected override Task SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken) {
// สร้าง Response เอง ไม่เรียก Inner Handler
var response = new HttpResponseMessage(HttpStatusCode.OK) {
Content = new StringContent("Skipping the inner handler")
};
// TaskCompletionSource สร้าง Task ที่ไม่มี Delegate
var taskCompletion = new TaskCompletionSource();
taskCompletion.SetResult(response);
return taskCompletion.Task;
}
}
ตัวอย่าง: Rate Limiting Handler
public class RateLimitingHandler : DelegatingHandler {
private static readonly ConcurrentDictionary _counters = new();
protected override async Task SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken) {
var clientIp = GetClientIp(request);
var counter = _counters.GetOrAdd(clientIp, _ => new RequestCounter());
if (counter.Increment() > 100) { // 100 requests per window
var response = new HttpResponseMessage(HttpStatusCode.TooManyRequests) {
Content = new StringContent("Rate limit exceeded"),
ReasonPhrase = "Too Many Requests"
};
response.Headers.RetryAfter = new System.Net.Http.Headers.RetryConditionHeaderValue(TimeSpan.FromMinutes(1));
return response;
}
return await base.SendAsync(request, cancellationToken);
}
private string GetClientIp(HttpRequestMessage request) {
// Implementation to extract client IP
return request.GetOwinContext()?.Request.RemoteIpAddress ?? "unknown";
}
private class RequestCounter {
private int _count = 0;
private DateTime _windowStart = DateTime.UtcNow;
public int Increment() {
var now = DateTime.UtcNow;
if (now - _windowStart > TimeSpan.FromMinutes(1)) {
_count = 0;
_windowStart = now;
}
return Interlocked.Increment(ref _count);
}
}
}
ลำดับการ Execute ของ Message Handlers
Handlers ถูก Execute ในลำดับที่ Add เข้าไปใน MessageHandlers Collection:
// Registration Order (Outer to Inner)
config.MessageHandlers.Add(new Handler1()); // Outermost - First to receive Request
config.MessageHandlers.Add(new Handler2());
config.MessageHandlers.Add(new Handler3()); // Innermost - Last before Controller
// Request Flow: Handler1 → Handler2 → Handler3 → Controller
// Response Flow: Controller → Handler3 → Handler2 → Handler1
Built-in Server-side Handlers
- HttpServer - รับ Request จาก Host
- HttpRoutingDispatcher - Dispatch ตาม Route
- HttpControllerDispatcher - ส่ง Request ไปยัง Web API Controller
Use Cases สำหรับ DelegatingHandler
- Logging และ Monitoring
- Authentication/Authorization (API Key, JWT Validation)
- Rate Limiting / Throttling
- Request/Response Transformation
- Caching
- Error Handling / Exception Logging
- Correlation ID Propagation