Halo, Habr! Saya hadir untuk Anda terjemahan artikel " Membuat API Gateway sederhana di ASP.NET Core ".
10 menit untuk membaca
Dalam artikel saya sebelumnya, JWT Authentication for Microservices di .NET , saya melihat proses pembuatan microservice untuk otentikasi pengguna. Ini dapat digunakan untuk memverifikasi identitas pengguna sebelum melakukan tindakan apa pun pada komponen lain dari sistem.

Komponen vital lainnya agar produk dapat berfungsi adalah gateway API - sistem antara aplikasi dan backend, yang, pertama, merutekan permintaan yang masuk ke layanan mikro yang sesuai, dan kedua, memberikan otorisasi kepada pengguna.
Ada banyak kerangka kerja yang dapat digunakan untuk membuat gateway API, misalnya, Ocelot di .NET core atau Netflix Zuul di Jawa. Namun, dalam artikel ini saya akan menjelaskan proses pembuatan gateway API sederhana dari awal di .NET Core.
Pembuatan proyek
Pertama, buat aplikasi baru dengan memilih ASP.NET Core Web Application di jendela pembuatan proyek dan Kosongkan sebagai templat.


Proyek ini akan berisi kelas Startup dan Program . Bagi kami, bagian terpenting adalah metode Konfigurasi kelas Startup . Di sini kita dapat memproses permintaan HTTP yang masuk dan menanggapinya. Mungkin kode berikut akan berada di metode Konfigurasi :
app.Run(async (context) => { await context.Response.WriteAsync("Hello, World!"); });
Ejaan router
Karena dalam metode Konfigurasi kami akan memproses permintaan, kami akan menulis logika yang diperlukan:
Router router = new Router("routes.json"); app.Run(async (context) => { var content = await router.RouteRequest(context.Request); await context.Response.WriteAsync(await content.Content.ReadAsStringAsync()); });
Pertama kita membuat objek bertipe Router . Tugasnya termasuk menyimpan rute yang ada, memvalidasi dan mengirim permintaan sesuai dengan rute. Untuk membuat kode lebih bersih, kami akan memuat rute dari file JSON.
Hasilnya adalah logika berikut: setelah permintaan tiba di gateway, itu akan dialihkan ke router, yang, pada gilirannya, akan mengirimkannya ke microservice yang sesuai.
Sebelum menulis kelas Router , buat file routes.json . Dalam file ini, kami menunjukkan daftar rute, yang masing-masing akan berisi alamat eksternal (titik akhir) dan alamat tujuan (tujuan). Selain itu, kami akan menambahkan tanda yang menandakan perlunya mengotorisasi pengguna sebelum mengarahkan ulang.
Seperti apa bentuk file ini:
{ "routes": [ { "endpoint": "/movies", "destination": { "uri": "http://localhost:8090/movies/", "requiresAuthentication": "true" } }, { "endpoint": "/songs", "destination": { "uri": "http://localhost:8091/songs/", "requiresAuthentication": "false" } } ], "authenticationService": { "uri": "http://localhost:8080/api/auth/" } }
Buat Kelas Tujuan
Kita sekarang tahu bahwa setiap Rute harus memiliki endpoint
dan destination
, dan masing-masing Tujuan harus memiliki uri
dan requiresAuthentication
bidang Otentikasi.
Sekarang mari kita menulis kelas Tujuan , mengingatnya. Saya akan menambahkan dua bidang, dua konstruktor dan konstruktor pribadi tanpa parameter untuk deserialisasi JSON.
public class Destination { public string Uri { get; set; } public bool RequiresAuthentication { get; set; } public Destination(string uri, bool requiresAuthentication) { Uri = path; RequiresAuthentication = requiresAuthentication; } public Destination(string uri) :this(uri, false) { } private Destination() { Uri = "/"; RequiresAuthentication = false; } }
Juga, akan benar untuk menulis metode SendRequest
di kelas ini. Ini akan menunjukkan bahwa setiap objek dari kelas Tujuan akan bertanggung jawab untuk mengirim permintaan. Metode ini akan mengambil objek tipe HttpRequest
, yang menggambarkan permintaan masuk, mengeluarkan semua informasi yang diperlukan dari sana, dan mengirimkan permintaan ke URI target. Untuk melakukan ini, kita menulis metode bantu CreateDestinationUri
, yang akan menghubungkan garis-garis dengan alamat dan parameter dari garis alamat (string kueri) dari klien.
private string CreateDestinationUri(HttpRequest request) { string requestPath = request.Path.ToString(); string queryString = request.QueryString.ToString(); string endpoint = ""; string[] endpointSplit = requestPath.Substring(1).Split('/'); if (endpointSplit.Length > 1) endpoint = endpointSplit[1]; return Uri + endpoint + queryString; }
Sekarang kita dapat menulis metode SendRequest
yang akan mengirim permintaan untuk layanan mikro dan menerima respons kembali.
public async Task<HttpResponseMessage> SendRequest(HttpRequest request) { string requestContent; using (Stream receiveStream = request.Body) { using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8)) { requestContent = readStream.ReadToEnd(); } } HttpClient client = new HttpClient(); HttpRequestMessage newRequest = new HttpRequestMessage(new HttpMethod(request.Method), CreateDestinationUri(request)); HttpResponseMessage response = await client.SendAsync(newRequest); return response; }
Buat parser JSON.
Sebelum menulis kelas Router , kita perlu membuat logika untuk membatalkan deserialisasi file JSON dengan rute. Saya akan membuat kelas pembantu untuk ini, di mana akan ada dua metode: satu untuk membuat objek dari file JSON, dan yang lainnya untuk deserialization.
public class JsonLoader { public static T LoadFromFile<T>(string filePath) { using (StreamReader reader = new StreamReader(filePath)) { string json = reader.ReadToEnd(); T result = JsonConvert.DeserializeObject<T>(json); return result; } } public static T Deserialize<T>(object jsonObject) { return JsonConvert.DeserializeObject<T>(Convert.ToString(jsonObject)); } }
Router kelas.
Hal terakhir yang akan kita lakukan sebelum menulis Router adalah mendeskripsikan model rute:
public class Route { public string Endpoint { get; set; } public Destination Destination { get; set; } }
Sekarang mari kita menulis kelas Router dengan menambahkan bidang dan konstruktor di sana.
public class Router { public List<Route> Routes { get; set; } public Destination AuthenticationService { get; set; } public Router(string routeConfigFilePath) { dynamic router = JsonLoader.LoadFromFile<dynamic>(routeConfigFilePath); Routes = JsonLoader.Deserialize<List<Route>>( Convert.ToString(router.routes) ); AuthenticationService = JsonLoader.Deserialize<Destination>( Convert.ToString(router.authenticationService) ); } }
Saya menggunakan tipe dinamis untuk membaca dari JSON dan menulis properti objek untuk itu.
Sekarang semuanya siap untuk menggambarkan fungsi utama gateway API: perutean dan otorisasi pengguna, yang akan terjadi dalam metode RouteRequest
. Kita perlu membongkar bagian dasar dari alamat eksternal (base endpoint) dari objek permintaan. Misalnya, untuk alamat /movies/add
basisnya adalah /movies/
. Setelah itu, kita perlu memeriksa apakah ada deskripsi rute ini. Jika demikian, beri otorisasi kepada pengguna dan kirimkan permintaan, jika tidak, kami akan mengembalikan kesalahan. Saya juga membuat kelas ConstructErrorMessage untuk kenyamanan.
Untuk otorisasi, saya lebih suka cara berikut: kami mengekstrak token dari header permintaan dan mengirimkannya sebagai parameter permintaan. Opsi lain dimungkinkan: biarkan token di header, maka layanan microser yang dimaksudkan harus sudah mengekstraknya.
public async Task<HttpResponseMessage> RouteRequest(HttpRequest request) { string path = request.Path.ToString(); string basePath = '/' + path.Split('/')[1]; Destination destination; try { destination = Routes.First(r => r.Endpoint.Equals(basePath)).Destination; } catch { return ConstructErrorMessage("The path could not be found."); } if (destination.RequiresAuthentication) { string token = request.Headers["token"]; request.Query.Append(new KeyValuePair<string, StringValues>("token", new StringValues(token))); HttpResponseMessage authResponse = await AuthenticationService.SendRequest(request); if (!authResponse.IsSuccessStatusCode) return ConstructErrorMessage("Authentication failed."); } return await destination.SendRequest(request); } private HttpResponseMessage ConstructErrorMessage(string error) { HttpResponseMessage errorMessage = new HttpResponseMessage { StatusCode = HttpStatusCode.NotFound, Content = new StringContent(error) }; return errorMessage; }
Kesimpulan
Membuat gateway API dasar tidak memerlukan banyak upaya, tetapi tidak menyediakan fungsionalitas yang tepat. Jika Anda membutuhkan penyeimbang beban, Anda bisa melihat kerangka kerja atau platform yang ada yang menawarkan pustaka untuk permintaan perutean.
Semua kode dalam artikel ini tersedia di repositori GitHub.