Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Handler pesan adalah kelas yang menerima permintaan HTTP dan mengembalikan respons HTTP. Penangan pesan berasal dari kelas HttpMessageHandler abstrak.
Biasanya, serangkaian penangan pesan dirangkai bersama-sama. Handler pertama menerima permintaan HTTP, melakukan beberapa pemrosesan, dan memberikan permintaan ke handler berikutnya. Pada titik tertentu, respons dibuat dan kembali ke rantai. Pola ini disebut penangan pendelegasian .
Penangan Pesan Server-Side
Di sisi server, alur API Web menggunakan beberapa penangan pesan bawaan:
- HttpServer mendapatkan permintaan dari host.
- HttpRoutingDispatcher mengirimkan permintaan berdasarkan rute.
- HttpControllerDispatcher mengirimkan permintaan ke pengontrol API Web.
Anda dapat menambahkan handler kustom ke alur. Penangan pesan baik untuk masalah lintas pemotongan yang beroperasi pada tingkat pesan HTTP (bukan tindakan pengontrol). Misalnya, handler pesan mungkin:
- Membaca atau mengubah header permintaan.
- Tambahkan header respons ke respons.
- Validasi permintaan sebelum mencapai pengontrol.
Diagram ini memperlihatkan dua handler kustom yang dimasukkan ke dalam alur:
Catatan
Di sisi klien, HttpClient juga menggunakan penangan pesan. Untuk informasi selengkapnya, lihat Penangan Pesan HttpClient.
Penangan Pesan Kustom
Untuk menulis handler pesan kustom, berasal dari System.Net.Http.DelegatingHandler dan ambil alih metode SendAsync . Metode ini memiliki tanda tangan berikut:
Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken);
Metode ini mengambil HttpRequestMessage sebagai input dan secara asinkron mengembalikan HttpResponseMessage. Implementasi umum melakukan hal berikut:
- Memproses pesan permintaan.
- Panggil
base.SendAsync
untuk mengirim permintaan ke handler dalam. - Handler dalam mengembalikan pesan respons. (Langkah ini asinkron.)
- Proses respons dan kembalikan ke pemanggil.
Berikut adalah contoh sepele:
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;
}
}
Catatan
Panggilan ke base.SendAsync
tidak sinkron. Jika handler melakukan pekerjaan apa pun setelah panggilan ini, gunakan kata kunci tunggu, seperti yang ditunjukkan.
Penangan pendelegasian juga dapat melewati handler dalam dan langsung membuat respons:
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;
}
}
Jika penangan pendelegasian membuat respons tanpa memanggil base.SendAsync
, permintaan melewati sisa alur. Ini dapat berguna untuk handler yang memvalidasi permintaan (membuat respons kesalahan).
Menambahkan Handler ke Alur
Untuk menambahkan handler pesan di sisi server, tambahkan handler ke koleksi HttpConfiguration.MessageHandlers . Jika Anda menggunakan templat "ASP.NET MVC 4 Web Application" untuk membuat proyek, Anda dapat melakukan ini di dalam kelas 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...
}
}
Penangan pesan dipanggil dalam urutan yang sama dengan yang muncul di koleksi MessageHandlers . Karena berlapis, pesan respons berjalan ke arah lain. Artinya, handler terakhir adalah yang pertama mendapatkan pesan respons.
Perhatikan bahwa Anda tidak perlu mengatur handler dalam; kerangka kerja API Web secara otomatis menghubungkan penangan pesan.
Jika Anda menghost sendiri, buat instans kelas HttpSelfHostConfiguration dan tambahkan handler ke koleksi MessageHandlers .
var config = new HttpSelfHostConfiguration("http://localhost");
config.MessageHandlers.Add(new MessageHandler1());
config.MessageHandlers.Add(new MessageHandler2());
Sekarang mari kita lihat beberapa contoh penangan pesan kustom.
Contoh: X-HTTP-Method-Override
X-HTTP-Method-Override adalah header HTTP non-standar. Ini dirancang untuk klien yang tidak dapat mengirim jenis permintaan HTTP tertentu, seperti PUT atau DELETE. Sebagai gantinya, klien mengirim permintaan POST dan mengatur header X-HTTP-Method-Override ke metode yang diinginkan. Contohnya:
X-HTTP-Method-Override: PUT
Berikut adalah handler pesan yang menambahkan dukungan untuk 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);
}
}
Dalam metode SendAsync , handler memeriksa apakah pesan permintaan adalah permintaan POST, dan apakah berisi header X-HTTP-Method-Override. Jika demikian, ini memvalidasi nilai header, lalu memodifikasi metode permintaan. Terakhir, handler memanggil base.SendAsync
untuk meneruskan pesan ke handler berikutnya.
Ketika permintaan mencapai kelas HttpControllerDispatcher , HttpControllerDispatcher akan merutekan permintaan berdasarkan metode permintaan yang diperbarui.
Contoh: Menambahkan Header Respons Kustom
Berikut adalah handler pesan yang menambahkan header kustom ke setiap pesan respons:
// .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;
}
}
Pertama, handler memanggil base.SendAsync
untuk meneruskan permintaan ke handler pesan dalam. Handler dalam mengembalikan pesan respons, tetapi melakukannya secara asinkron menggunakan objek Task<T> . Pesan respons tidak tersedia sampai base.SendAsync
selesai secara asinkron.
Contoh ini menggunakan kata kunci tunggu untuk melakukan pekerjaan secara asinkron setelah SendAsync
selesai. Jika Anda menargetkan .NET Framework 4.0, gunakan Tugas<T>. Metode 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;
}
);
}
}
Contoh: Memeriksa Kunci API
Beberapa layanan web mengharuskan klien untuk menyertakan kunci API dalam permintaan mereka. Contoh berikut menunjukkan bagaimana handler pesan dapat memeriksa permintaan untuk kunci API yang valid:
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);
}
}
Handler ini mencari kunci API dalam string kueri URI. (Untuk contoh ini, kami berasumsi bahwa kuncinya adalah string statis. Implementasi nyata mungkin akan menggunakan validasi yang lebih kompleks.) Jika string kueri berisi kunci, handler meneruskan permintaan ke handler dalam.
Jika permintaan tidak memiliki kunci yang valid, handler membuat pesan respons dengan status 403, Terlarang. Dalam hal ini, handler tidak memanggil base.SendAsync
, sehingga handler dalam tidak pernah menerima permintaan, atau pengontrol. Oleh karena itu, pengontrol dapat mengasumsikan bahwa semua permintaan masuk memiliki kunci API yang valid.
Catatan
Jika kunci API hanya berlaku untuk tindakan pengontrol tertentu, pertimbangkan untuk menggunakan filter tindakan alih-alih handler pesan. Filter tindakan berjalan setelah perutean URI dilakukan.
Penangan Pesan Per-Route
Handler di koleksi HttpConfiguration.MessageHandlers berlaku secara global.
Atau, Anda dapat menambahkan handler pesan ke rute tertentu saat menentukan rute:
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
}
}
Dalam contoh ini, jika URI permintaan cocok dengan "Route2", permintaan dikirim ke MessageHandler2
. Diagram berikut menunjukkan alur untuk dua rute ini:
Perhatikan bahwa MessageHandler2
menggantikan HttpControllerDispatcher default. Dalam contoh ini, MessageHandler2
membuat respons, dan permintaan yang cocok dengan "Route2" tidak pernah masuk ke pengontrol. Ini memungkinkan Anda mengganti seluruh mekanisme pengontrol API Web dengan titik akhir kustom Anda sendiri.
Atau, handler pesan per rute dapat mendelegasikan ke HttpControllerDispatcher, yang kemudian dikirim ke pengontrol.
Kode berikut menunjukkan cara mengonfigurasi rute ini:
// 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
);