Bagikan melalui


Menggunakan layanan eksternal dari layanan Azure API Management

BERLAKU UNTUK: Semua tingkatan manajemen API

Kebijakan yang tersedia di 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 peristiwa jarak jauh atau untuk mengambil informasi yang digunakan untuk memanipulasi permintaan dan respons asli dalam beberapa cara.

Send-One-Way-Request

Mungkin interaksi eksternal yang paling sederhana adalah gaya permintaan fire-and-forget yang memungkinkan layanan eksternal diberi tahu tentang beberapa jenis peristiwa penting. Kebijakan choose alur kontrol dapat digunakan untuk mendeteksi segala jenis kondisi yang Anda minati. Jika kondisi terpenuhi, Anda dapat membuat permintaan HTTP eksternal menggunakan kebijakan send-one-way-request. Ini bisa menjadi permintaan ke sistem olahpesan seperti Hipchat atau Slack, atau API email seperti SendGrid atau MailChimp, atau untuk insiden dukungan penting seperti PagerDuty. Semua sistem olahpesan 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 sendiri oleh klien API. Biasanya membutuhkan 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 konsep inbound webhook. Saat mengonfigurasi web hook masuk, Slack menghasilkan URL khusus, yang memungkinkan Anda melakukan permintaan POST sederhana dan meneruskan pesan ke saluran Slack. Isi JSON yang Anda buat didasarkan pada format yang ditentukan oleh Slack.

Slack Web Hook

Apakah api dan lupa cukup baik?

Ada kompromi tertentu saat menggunakan gaya permintaan yang tidak memerlukan tindak lanjut. Jika karena alasan tertentu, permintaan gagal, maka kegagalan tidak akan dilaporkan. Dalam situasi khusus ini, kompleksitas memiliki sistem pelaporan kegagalan sekunder dan biaya performa tambahan untuk menunggu respons tidaklah diperlukan. Untuk skenario di mana penting untuk memeriksa respons, maka kebijakan permintaan kirim adalah opsi yang lebih baik.

Send-Request

Kebijakan ini send-request 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 API Management adalah melindungi sumber daya backend. Jika server otorisasi yang digunakan oleh API Anda membuat 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 yang distandarisasi

Di masa lalu, belum ada cara standar untuk memverifikasi token referensi dengan server otorisasi. Namun RFC 7662 standar yang baru-baru ini diusulkan diterbitkan oleh IETF yang menentukan bagaimana server sumber daya dapat memverifikasi validitas token.

Menarik token

Langkah pertama adalah mengekstrak token dari header Otorisasi. Nilai header harus diformat dengan Bearer skema otorisasi, 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 mengirimkan formulir HTML ke sumber daya introspeksi. Formulir HTML setidaknya harus berisi pasangan kunci/nilai dengan kunci token. Permintaan ke server otorisasi ini juga harus diautentikasi, untuk memastikan bahwa klien berbahaya 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 digunakan untuk memberikan akses respons yang dikembalikan. Nama yang ditentukan dalam properti ini dapat digunakan sebagai kunci ke dalam context.Variables kamus untuk mengakses IResponse objek.

Dari objek respons, Anda dapat mengambil isi dan RFC 7622 memberi tahu API Management bahwa respons harus berupa 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 di respons server otorisasi dengan cara ini:

<when condition="@(((IResponse)context.Variables["tokenstate"]).Body.As<JObject>().Property("expires_in") == null)">

Kegagalan pelaporan

Anda dapat menggunakan <choose> kebijakan untuk mendeteksi apakah token tidak valid dan jika demikian, kembalikan 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 bearer token harus digunakan, API Management juga mengembalikan WWW-Authenticate header dengan respons 401. WWW-Authenticate dimaksudkan untuk menginstruksikan klien tentang cara membuat permintaan yang diotorisasi dengan benar. Karena berbagai pendekatan yang mungkin dilakukan dengan kerangka kerja OAuth2, sulit untuk mengomunikasikan semua informasi yang diperlukan. Untungnya ada upaya yang sedang 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 send-request kebijakan 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 dapat digunakan untuk meningkatkan permintaan utama ke sistem backend, seperti yang Anda lihat dalam contoh sebelumnya, atau dapat digunakan sebagai pengganti lengkap untuk panggilan backend. Menggunakan teknik ini Anda dapat dengan mudah membuat sumber daya komposit yang dikumpulkan dari beberapa sistem yang berbeda.

Membangun dasbor

Terkadang Anda ingin bisa mengekspos informasi yang ada di beberapa sistem backend, misalnya, untuk mengoperasikan dasbor. KPI berasal dari semua back-end yang berbeda, tetapi Anda lebih suka tidak memberikan akses langsung kepada mereka dan akan lebih baik jika semua informasi dapat diambil dalam satu permintaan. Mungkin beberapa informasi data backend perlu diolah dan dibersihkan terlebih dahulu! Kemampuan untuk menyimpan sumber daya komposit tersebut akan berguna untuk mengurangi beban backend karena Anda tahu pengguna memiliki kebiasaan memencet tombol F5 berulang kali untuk melihat apakah metrik yang kurang memadai mungkin berubah.

Memalsukan sumber daya

Langkah pertama untuk membangun sumber daya dasbor adalah mengonfigurasi operasi baru di portal Microsoft Azure. Ini adalah operasi sementara yang digunakan untuk mengonfigurasi kebijakan komposisi untuk membangun sumber daya yang dinamis.

Pengoperasian dashboard

Membuat permintaan

Setelah operasi dibuat, Anda dapat mengonfigurasi kebijakan khusus untuk operasi tersebut.

Cuplikan layar yang memperlihatkan halaman Cakupan kebijakan.

Langkah pertama adalah mengekstrak parameter kueri apa pun dari permintaan masuk, sehingga Anda dapat meneruskannya ke backend. Dalam contoh ini, dasbor menampilkan informasi berdasarkan periode waktu dan karenanya memiliki fromDate parameter dan toDate . Anda dapat menggunakan set-variable kebijakan 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-masing 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 respons pengembalian . Elemen set-body dapat menggunakan ekspresi untuk membangun yang baru JObject 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 meningkatkan 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 dunia kemungkinan.