Menggunakan layanan eksternal dari layanan Azure API Management
BERLAKU UNTUK: Semua tingkatAN API Management
Kebijakan yang tersedia dalam layanan Azure API Management dapat melakukan berbagai pekerjaan yang berguna berdasarkan murni pada permintaan masuk, respons keluar, dan informasi konfigurasi dasar. Namun, dapat berinteraksi dengan layanan eksternal dari kebijakan API Management membuka lebih banyak peluang.
Anda sebelumnya telah melihat cara berinteraksi dengan layanan Azure Event Hubs untuk pengelogan, pemantauan, dan analitik. Artikel ini menunjukkan kebijakan yang memungkinkan Anda berinteraksi dengan layanan berbasis HTTP eksternal apa pun. Kebijakan ini dapat digunakan untuk memicu kejadian jarak jauh atau untuk mengambil informasi yang digunakan untuk memanipulasi permintaan dan respons asli dalam beberapa cara.
Kirim-Permintaan-Satu-Arah
Mungkin interaksi eksternal yang paling sederhana adalah gaya permintaan lepaskan-dan-lupakan yang memungkinkan layanan eksternal untuk diberitahu tentang beberapa jenis peristiwa penting. Kebijakan alur kontrol choose
dapat digunakan untuk mendeteksi segala jenis kondisi yang Anda minati. Jika kondisinya terpenuhi, Anda dapat membuat permintaan HTTP eksternal menggunakan kebijakan kirim-permintaan-satu-arah. Ini bisa berupa permintaan ke sistem perpesanan seperti Hipchat atau Slack, atau API email seperti SendGrid atau MailChimp, atau untuk insiden dukungan kritis seperti PagerDuty. Semua sistem perpesanan ini memiliki API HTTP sederhana yang dapat dipanggil.
Pemberitahuan dengan Slack
Contoh berikut menunjukkan cara mengirim pesan ke ruang obrolan Slack jika kode status respons HTTP lebih besar dari atau sama dengan 500. Kesalahan rentang 500 menunjukkan masalah dengan API backend yang tidak dapat diselesaikan oleh klien API sendiri. Biasanya memerlukan semacam intervensi pada bagian API Management.
<choose>
<when condition="@(context.Response.StatusCode >= 500)">
<send-one-way-request mode="new">
<set-url>https://hooks.slack.com/services/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</set-url>
<set-method>POST</set-method>
<set-body>@{
return new JObject(
new JProperty("username","APIM Alert"),
new JProperty("icon_emoji", ":ghost:"),
new JProperty("text", String.Format("{0} {1}\nHost: {2}\n{3} {4}\n User: {5}",
context.Request.Method,
context.Request.Url.Path + context.Request.Url.QueryString,
context.Request.Url.Host,
context.Response.StatusCode,
context.Response.StatusReason,
context.User.Email
))
).ToString();
}</set-body>
</send-one-way-request>
</when>
</choose>
Slack memiliki gagasan tentang webhook masuk. Saat mengonfigurasi webhook masuk, Slack menghasilkan URL khusus, yang memungkinkan Anda untuk melakukan permintaan POST sederhana dan meneruskan pesan ke saluran Slack. Bodi JSON yang Anda buat didasarkan pada format yang ditentukan oleh Slack.
Apakah lepaskan dan lupakan cukup baik?
Ada pengorbanan tertentu saat menggunakan gaya permintaan lepaskan-dan-lupakan. Jika karena alasan tertentu, permintaan gagal, maka kegagalan tidak akan dilaporkan. Dalam situasi khusus ini, kompleksitas dari memiliki sistem pelaporan kegagalan sekunder dan biaya kinerja tambahan untuk menunggu respons tidak dijamin. Untuk skenario di mana penting untuk memeriksa respons, maka kebijakan kirim-permintaan adalah pilihan yang lebih baik.
Kirim-Permintaan
Kebijakan send-request
ini memungkinkan penggunaan layanan eksternal untuk melakukan fungsi pemrosesan yang kompleks dan mengembalikan data ke layanan manajemen API yang dapat digunakan untuk pemrosesan kebijakan lebih lanjut.
Mengotorisasi token referensi
Fungsi utama dari API Management adalah melindungi sumber daya backend. Jika server otorisasi yang digunakan oleh API Anda membuat token JWT sebagai bagian dari alur OAuth2-nya, seperti yang dilakukan Microsoft Entra ID , maka Anda dapat menggunakan validate-jwt
kebijakan atau validate-azure-ad-token
kebijakan untuk memverifikasi validitas token. Beberapa server otorisasi membuat apa yang disebut token referensi yang tidak dapat diverifikasi tanpa melakukan panggilan balik ke server otorisasi.
Introspeksi terstandardisasi
Di masa lalu, belum ada cara terstandardisasi untuk memverifikasi token referensi dengan server otorisasi. Namun RFC 7662 standar yang baru-baru ini diusulkan telah diterbitkan oleh IETF yang menentukan bagaimana server sumber daya dapat memverifikasi validitas token.
Mengekstrak token
Langkah pertama adalah mengekstrak token dari header Otorisasi. Nilai header harus diformat dengan skema otorisasi Bearer
, spasi tunggal, lalu token otorisasi sesuai RFC 6750. Sayangnya ada kasus di mana skema otorisasi dihilangkan. Untuk memperhitungkan hal ini saat mengurai, API Management membagi nilai header pada spasi dan memilih string terakhir dari array string yang dikembalikan. Ini menyediakan solusi untuk header otorisasi yang diformat dengan buruk.
<set-variable name="token" value="@(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />
Membuat permintaan validasi
Setelah API Management memiliki token otorisasi, API Management dapat membuat permintaan untuk memvalidasi token. RFC 7662 menyebut proses ini introspeksi dan mengharuskan Anda POST
formulir HTML ke sumber daya introspeksi. Formulir HTML setidaknya harus berisi pasangan kunci/nilai dengan token
kunci. Permintaan ini ke server otorisasi juga harus diautentikasi, untuk memastikan bahwa klien jahat tidak dapat menelusuri token yang valid.
<send-request mode="new" response-variable-name="tokenstate" timeout="20" ignore-error="true">
<set-url>https://microsoft-apiappec990ad4c76641c6aea22f566efc5a4e.azurewebsites.net/introspection</set-url>
<set-method>POST</set-method>
<set-header name="Authorization" exists-action="override">
<value>basic dXNlcm5hbWU6cGFzc3dvcmQ=</value>
</set-header>
<set-header name="Content-Type" exists-action="override">
<value>application/x-www-form-urlencoded</value>
</set-header>
<set-body>@($"token={(string)context.Variables["token"]}")</set-body>
</send-request>
Memeriksa respons
Atribut response-variable-name
ini digunakan untuk memberikan akses respons yang dikembalikan. Nama yang didefinisikan dalam properti ini dapat digunakan sebagai kunci ke dalam kamus context.Variables
untuk mengakses objek IResponse
.
Dari objek respons, Anda dapat mengambil bodi dan RFC 7622 memberi tahu API Management bahwa respons harus merupakan objek JSON dan harus berisi setidaknya properti yang disebut active
yang merupakan nilai boolean. Ketika active
benar maka token dianggap valid.
Atau, jika server otorisasi tidak menyertakan bidang "aktif" untuk menunjukkan apakah token valid, gunakan alat klien HTTP seperti curl
untuk menentukan properti apa yang diatur dalam token yang valid. Misalnya, jika respons token yang valid berisi properti yang disebut "expires_in", periksa apakah nama properti ini ada dalam respons server otorisasi dengan cara ini:
<when condition="@(((IResponse)context.Variables["tokenstate"]).Body.As<JObject>().Property("expires_in") == null)">
Kegagalan pelaporan
Anda dapat menggunakan kebijakan <choose>
untuk mendeteksi apakah token tidak valid dan jika demikian, mengembalikan respons 401.
<choose>
<when condition="@((bool)((IResponse)context.Variables["tokenstate"]).Body.As<JObject>()["active"] == false)">
<return-response response-variable-name="existing response variable">
<set-status code="401" reason="Unauthorized" />
<set-header name="WWW-Authenticate" exists-action="override">
<value>Bearer error="invalid_token"</value>
</set-header>
</return-response>
</when>
</choose>
Sesuai RFC 6750 yang menjelaskan bagaimana token bearer
harus digunakan, API Management juga mengembalikan header WWW-Authenticate
dengan respons 401. Laporan WWW-Authenticate dimaksudkan untuk menginstruksikan klien tentang cara membangun permintaan yang diotorisasi dengan benar. Karena berbagai pendekatan yang mungkin dilakukan dengan kerangka kerja OAuth2, sulit untuk mengkomunikasikan semua informasi yang diperlukan. Untungnya ada upaya yang dilakukan untuk membantu klien menemukan cara mengotorisasi permintaan dengan benar ke server sumber daya.
Solusi akhir
Pada akhirnya, Anda mendapatkan kebijakan berikut:
<inbound>
<!-- Extract Token from Authorization header parameter -->
<set-variable name="token" value="@(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />
<!-- Send request to Token Server to validate token (see RFC 7662) -->
<send-request mode="new" response-variable-name="tokenstate" timeout="20" ignore-error="true">
<set-url>https://microsoft-apiappec990ad4c76641c6aea22f566efc5a4e.azurewebsites.net/introspection</set-url>
<set-method>POST</set-method>
<set-header name="Authorization" exists-action="override">
<value>basic dXNlcm5hbWU6cGFzc3dvcmQ=</value>
</set-header>
<set-header name="Content-Type" exists-action="override">
<value>application/x-www-form-urlencoded</value>
</set-header>
<set-body>@($"token={(string)context.Variables["token"]}")</set-body>
</send-request>
<choose>
<!-- Check active property in response -->
<when condition="@((bool)((IResponse)context.Variables["tokenstate"]).Body.As<JObject>()["active"] == false)">
<!-- Return 401 Unauthorized with http-problem payload -->
<return-response response-variable-name="existing response variable">
<set-status code="401" reason="Unauthorized" />
<set-header name="WWW-Authenticate" exists-action="override">
<value>Bearer error="invalid_token"</value>
</set-header>
</return-response>
</when>
</choose>
<base />
</inbound>
Ini hanya salah satu dari banyak contoh bagaimana kebijakan send-request
dapat digunakan untuk mengintegrasikan layanan eksternal yang berguna ke dalam proses permintaan dan respons yang mengalir melalui layanan API Management.
Komposisi Respons
Kebijakan send-request
ini dapat digunakan untuk meningkatkan permintaan utama ke sistem backend, seperti yang Anda lihat dalam contoh sebelumnya, atau dapat digunakan sebagai penggantian lengkap untuk panggilan backend. Dengan menggunakan teknik ini Anda dapat dengan mudah membuat sumber daya komposit yang dikumpulkan dari beberapa sistem yang berbeda.
Membuat dasbor
Terkadang Anda ingin dapat mengekspos informasi yang ada di beberapa sistem backend, misalnya, untuk menggerakkan dasbor. KPI berasal dari semua backend yang berbeda, tetapi Anda lebih suka tidak memberikan akses langsung kepada mereka dan alangkah baiknya jika semua informasi dapat diambil dalam satu permintaan. Mungkin beberapa informasi backend perlu diiris dan dikotakkan dan sedikit sanitasi terlebih dahulu! Mampunya menyimpan sumber daya komposit itu akan berguna untuk mengurangi beban backend seperti yang Anda tahu pengguna memiliki kebiasaan mengetuk ngetuk tombol F5 untuk melihat apakah metrik mereka yang berkinerja buruk dapat berubah.
Memalsukan sumber daya
Langkah pertama untuk membangun sumber daya dasbor adalah mengonfigurasi operasi baru di portal Microsoft Azure. Ini adalah operasi placeholder yang digunakan untuk mengonfigurasi kebijakan komposisi untuk membangun sumber daya dinamis.
Membuat permintaan
Setelah operasi dibuat, Anda dapat mengonfigurasi kebijakan khusus untuk operasi tersebut.
Langkah pertama adalah mengekstrak parameter kueri apa pun dari permintaan masuk, sehingga Anda bisa meneruskannya ke backend. Dalam contoh ini, dasbor menampilkan informasi berdasarkan periode waktu dan karenanya memiliki parameter fromDate
dan toDate
. Anda dapat menggunakan kebijakan set-variable
untuk mengekstrak informasi dari URL permintaan.
<set-variable name="fromDate" value="@(context.Request.Url.Query["fromDate"].Last())">
<set-variable name="toDate" value="@(context.Request.Url.Query["toDate"].Last())">
Setelah Anda memiliki informasi ini, Anda dapat membuat permintaan ke semua sistem backend. Setiap permintaan membuat URL baru dengan informasi parameter dan memanggil server masing-masingnya dan menyimpan respons dalam variabel konteks.
<send-request mode="new" response-variable-name="revenuedata" timeout="20" ignore-error="true">
<set-url>@($"https://accounting.acme.com/salesdata?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")</set-url>
<set-method>GET</set-method>
</send-request>
<send-request mode="new" response-variable-name="materialdata" timeout="20" ignore-error="true">
<set-url>@($"https://inventory.acme.com/materiallevels?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")</set-url>
<set-method>GET</set-method>
</send-request>
<send-request mode="new" response-variable-name="throughputdata" timeout="20" ignore-error="true">
<set-url>@($"https://production.acme.com/throughput?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")</set-url>
<set-method>GET</set-method>
</send-request>
<send-request mode="new" response-variable-name="accidentdata" timeout="20" ignore-error="true">
<set-url>@($"https://production.acme.com/accidentdata?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")</set-url>
<set-method>GET</set-method>
</send-request>
API Management akan mengirim permintaan ini secara berurutan.
Merespons
Untuk membangun respons komposit, Anda dapat menggunakan kebijakan pengembalian-respons. Elemen set-body
dapat menggunakan ekspresi untuk membuat JObject
baru dengan semua representasi komponen yang disematkan sebagai properti.
<return-response response-variable-name="existing response variable">
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>
@(new JObject(new JProperty("revenuedata",((IResponse)context.Variables["revenuedata"]).Body.As<JObject>()),
new JProperty("materialdata",((IResponse)context.Variables["materialdata"]).Body.As<JObject>()),
new JProperty("throughputdata",((IResponse)context.Variables["throughputdata"]).Body.As<JObject>()),
new JProperty("accidentdata",((IResponse)context.Variables["accidentdata"]).Body.As<JObject>())
).ToString())
</set-body>
</return-response>
Kebijakan lengkap terlihat sebagai berikut:
<policies>
<inbound>
<set-variable name="fromDate" value="@(context.Request.Url.Query["fromDate"].Last())">
<set-variable name="toDate" value="@(context.Request.Url.Query["toDate"].Last())">
<send-request mode="new" response-variable-name="revenuedata" timeout="20" ignore-error="true">
<set-url>@($"https://accounting.acme.com/salesdata?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
<set-method>GET</set-method>
</send-request>
<send-request mode="new" response-variable-name="materialdata" timeout="20" ignore-error="true">
<set-url>@($"https://inventory.acme.com/materiallevels?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
<set-method>GET</set-method>
</send-request>
<send-request mode="new" response-variable-name="throughputdata" timeout="20" ignore-error="true">
<set-url>@($"https://production.acme.com/throughput?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
<set-method>GET</set-method>
</send-request>
<send-request mode="new" response-variable-name="accidentdata" timeout="20" ignore-error="true">
<set-url>@($"https://production.acme.com/accidentdata?from={(string)context.Variables["fromDate"]}&to={(string)context.Variables["fromDate"]}")"</set-url>
<set-method>GET</set-method>
</send-request>
<return-response response-variable-name="existing response variable">
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>
@(new JObject(new JProperty("revenuedata",((IResponse)context.Variables["revenuedata"]).Body.As<JObject>()),
new JProperty("materialdata",((IResponse)context.Variables["materialdata"]).Body.As<JObject>()),
new JProperty("throughputdata",((IResponse)context.Variables["throughputdata"]).Body.As<JObject>()),
new JProperty("accidentdata",((IResponse)context.Variables["accidentdata"]).Body.As<JObject>())
).ToString())
</set-body>
</return-response>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
</policies>
Ringkasan
Layanan Azure API Management menyediakan kebijakan fleksibel yang dapat diterapkan secara selektif pada lalu lintas HTTP dan memungkinkan komposisi layanan backend. Apakah Anda ingin menyempurnakan gateway API Anda dengan fungsi pemberitahuan, verifikasi, kemampuan validasi, atau membuat sumber daya komposit baru berdasarkan beberapa layanan backend, send-request
dan kebijakan terkait membuka banyak sekali kemungkinan.