استخدام الخدمات الخارجية من خدمة APIM Microsoft Azure

ينطبق على: جميع مستويات إدارة واجهة برمجة التطبيقات

تقوم النهج المتوفرة في خدمة تطبيقات Microsoft Azure APIM بنطاق واسع من الأعمال المفيدة استنادًا إلى الطلب الوارد والاستجابة الصادرة ومعلومات التكوين الأساسية. ومع ذلك، فإن القدرة على التفاعل مع الخدمات الخارجية من سياسات الخدمات من APIM تفتح العديد من الفرص.

سبق لك أن رأيت كيفية التفاعل مع خدمة Azure Event Hub لتسجيل الدخول والمراقبة والتحليلات. توضح هذه المقالة النهج التي تسمح لك بالتفاعل مع أي خدمة خارجية تستند إلى http. يمكن استخدام هذه النهج مشغّل الأحداث البعيدة أو لاسترداد المعلومات المستخدمة لمعالجة الطلب الأصلي والاستجابة بطريقة ما.

إرسال طلب باتجاه واحد

ربما يكون أبسط تفاعل خارجي هو أسلوب الطلب الذي يسمح بإخطار خدمة خارجية بنوع من الأحداث المهمة. يمكن استخدام نهج تدفق عنصر التحكمchooseللكشف عن أي نوع من الحالات التي تهتم بها. إذا تم استيفاء الشرط، يمكنك إجراء طلب HTTPخارجي باستخدام طلب الإرسال أحادي الاتجاهالنهج. قد يكون هذا طلب إلى نظام مراسلة مثل Hipchat أو Slack، أو API بريد إلكتروني مثل SendGrid أو MailChimp، أو لحوادث الدعم الحرجة شيء مثل نداء الواجب. تحتوي كافة أنظمة المراسلة هذه على APIhttp بسيطة يمكن استدعاؤها.

التنبيه باستخدام Slack

يوضح المثال التالي كيفية إرسال رسالة إلى غرفة دردشةSlack إذا كان رمز حالة استجابة HTTPأكبر من أو يساوي 500. يشير خطأ نطاق ال 500 إلى وجود مشكلة فيAPI الخلفية التي لا يمكن للعميل API حل نفسها. وعادة ما يتطلب نوعًا من التدخل على جزء APIM.

<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 لديه فكرة خطاطيف إخطار على الويب. عند تكوين إخطار على الويب وارد، يقوم Slack بإنشاء عنوان URLخاص، والذي يسمح لك بإجراء طلب نشر بسيط وتمرير رسالة إلى قناة Slack. يستند نص JavaScript Object Notation الذي تقوم بإنشائه إلى نموذج معرف بواسطة Slack.

Slack إخطار على الويب

هل الإطلاق وإزالته جيداً بما فيه الكفاية ؟

هناك بعض المقايضات عند استخدام نمط الإطلاق والإزالة من الطلب. إذا لسبب ما، إذا فشل الطلب، ثم لن يتم الإبلاغ عن الفشل. وفي هذه الحالة بالخصوص، لا يوجد مبرر لتعقيد وجود نظام ثانوي للإبلاغ عن نظام الفشل وتكلفة الأداء الإضافية لانتظار الرد. بالنسبة للسيناريوهات حيث من الضروري فحص الاستجابة، ثم نهجإرسال طلبهو خيار أفضل.

إرسال الطلب

send-requestتمكن النهج من استخدام خدمة خارجية لتنفيذ وظائف معالجة معقدة وإعادة البيانات إلى خدمة APIM التي يمكن استخدامها لمزيد من معالجة النهج.

تفويض الرموز المرجعية المميزة

دالة رئيسية APIM هي حماية الموارد الخلفية. إذا كان خادم التخويل المستخدم من قبل واجهة برمجة التطبيقات ينشئ رموز JWT المميزة كجزء من تدفق OAuth2 الخاص به، كما يفعل معرف Microsoft Entra، فيمكنك استخدام validate-jwt النهج أو validate-azure-ad-token النهج للتحقق من صحة الرمز المميز. إنشاء بعض خادم التخويل بما يسمى الرموز المميزة المرجعيةلا يمكن التحقق دون إجراء رد اتصال إلى خادم التخويل.

الاستبطان القياسي

في الماضي، لم تكن هناك طريقة قياسية للتحقق من رمز مرجعي مميز مع خادم التخويل. ومع ذلك تم نشرRFC 7662القياسية المقترحة مؤخرا بواسطة IETF الذي يعرف كيف يمكن التحقق من صحة رمز مميز خادم مورد.

يستخرج الرمز المميز

الخطوة الأولى هي لاستخراج الرمز المميز من عنوان التخويل. يجب تنسيق قيمة العنوان مع Bearer نظام التخويل، مسافة واحدة ثم الرمز التخويل المميز وفقًا لـ RFC 6750. لسوء الحظ، هناك حالات تم فيها حذف مخطط التخويل. لحساب هذا عند تحليل APIM تقسيم قيمة العنوان على مسافة ثم تحديد السلسلة الأخيرة من الصفيف إرجاع سلاسل. يوفر هذا حلا لعناوين التخويل منسقة منفذة بطريقة سيئة.

<set-variable name="token" value="@(context.Request.Headers.GetValueOrDefault("Authorization","scheme param").Split(' ').Last())" />

إجراء التحقق من صحة الطلب

بمجرد أن تحصل APIM على الرمز المميز للتخويل، APIM يمكن إجراء الطلب للتحقق من صحة الرمز المميز. تواصل على RFC7662 هذه عملية الاستبطان ويتطلبPOSTنموذج HTML إلى مورد الاستبطان. يجب أن يحتوي نموذج HTML على الأقل على زوج مفتاح/قيمة مع المفتاح token. يجب أيضًا مصادقة هذا الطلب إلى خادم التخويل، للتأكد من أن العملاء الضارين لا يمكن أن تذهب الصيد بشباك الجر للرموز المميزة صالحة.

<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>

فحص الاستجابة

response-variable-nameيتم استخدام السمة لمنح الوصول إلى الاستجابة التي تم إرجاعها. يمكن استخدام خاصية الاسم المعرف في هذه الخاصية كمفتاح في context.Variables القاموس للوصول إلى IResponse العنصر.

من عنصر الاستجابة، يمكنك استرداد النص الأساسي و RFC 7622 يخبر APIM أن الاستجابة يجب أن يكون كائن JSON ويجب أن تحتوي على خاصية على الأقل تسمىactiveقيمة منطقية. عندماactive يكون صحيحا ثم يعتبر الرمز المميز سارياً.

بدلًا من ذلك، إذا لم يتضمن خادم التخويل الحقل "النشط" للإشارة إلى ما إذا كان الرمز المميز سارياً، استخدم أداة مثل ساعي البريد لتحديد الخصائص التي تم تعيينها في رمز مميز سارٍ. على سبيل المثال، إذا كانت استجابة رمز مميز ساري يحتوي على خاصية تسمى "expires_in"، تحقق ما إذا كان اسم الخاصية هذا موجودا في استجابة خادم التخويل بهذه الطريقة:

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

تقرير الإبلاغ

يمكنك استخدام<choose>نهج للكشف عن إذا كان الرمز المميز غير ساري وإذا كان الأمر كذلك، قم بإرجاع استجابة 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>

وفقاً لـ RFC 6750الذي يصف كيفية bearer استخدام الرموز المميزة، تقوم APIM أيضا بإرجاع WWW-Authenticate العنوان مع استجابة 401. تهدف WWW-Authenticate إلى إرشاد العميل حول كيفية إنشاء طلب المصادقة بشكل صحيح. ونظرًا للتنوع الواسع في النهج الممكنة في إطار عمل OAuth2، من الصعب إيصال جميع المعلومات المطلوبة. لحسن الحظ هناك جهود جارية لمساعدة العملاء على اكتشاف كيفية تفويض الطلبات بشكل صحيح إلى خادم مورد.

حل نهائي

وفي النهاية، يمكنك الحصول على النهج التالي:

<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>

هذا هو واحد فقط من العديد من الأمثلة على كيفيةsend-requestاستخدام النهج لدمج الخدمات الخارجية المفيدة في عملية الطلبات والاستجابات المتدفقة من خلال خدمة APIM.

تكوين استجابة

send-requestيمكن استخدام النهج لتحسين طلب أساسي لنظام الخلفية كما رأيت في المثال السابق أو يمكن استخدامه كبديل كامل لمكالمة الخلفية. تستخدم هذه التقنية يمكنك بسهولة إنشاء الموارد المركبة التي يتم تجميعها من أنظمة مختلفة متعددة.

بنية لوحة المعلومات

في بعض الأوقات تريد أن تكون قادرا على كشف المعلومات الموجودة في أنظمة خلفية متعددة، على سبيل المثال، لدفع لوحة معلومات. تأتي KPI من جميع الأطراف الخلفية المختلفة، ولكنك تفضل عدم توفير الوصول المباشر إليها وسيكون من الجميل إذا كان من الممكن استرداد جميع المعلومات في طلب واحد. ربما تحتاج بعض معلومات الخلفية إلى إجراء بعض البحث والفحص والتفصيل وتقليل جانب التعقيد أولًا! تكون قادرة على ذاكرة التخزين المؤقت لهذا المورد المركب سيكون من المفيد للحد من الحمل الخلفي كما تعلمون المستخدمين لديهم عادة من يدق المفتاح F5 من أجل معرفة ما إذا كانت مقاييسها الأداء الضعيف قد تتغير.

تزوير مورد

الخطوة الأولى لإنشاء مورد لوحة المعلومات هي تكوين عملية جديدة في مدخل Microsoft Azure. هذه عملية عنصر نائب تستخدم لتكوين نهج تكوين لبناء المورد الحيوي.

عملية لوحة معلومات

تقديم طلبات

في حالة إنشاء العملية، يمكنك تكوين نهج خصيصًا لتلك العملية.

لقطة الشاشة تعرض شاشة نطاق النهج.

تتمثل الخطوة الأولى في استخراج أي معلمة استعلام من الطلب الوارد، بحيث يمكنك إعادة توجيهها إلى الخلفية. في هذا المثال، لوحة المعلومات تعرض معلومات استنادا إلى فترة من الوقت ولذلك يحتوي علىfromDateوtoDate والمعلمة. يمكنك استخدام النهجset-variableالاستخراج المعلومات من عنوان URL للطلب.

<set-variable name="fromDate" value="@(context.Request.Url.Query["fromDate"].Last())">
<set-variable name="toDate" value="@(context.Request.Url.Query["toDate"].Last())">

حال الحصول على هذه المعلومات، يمكنك تقديم طلبات إلى جميع الأنظمة الخلفية. كل طلب بإنشاء URL جديد مع معلومات المعلمة ثم استدعاء الخادم الخاص به ويخزن الاستجابة في سياق متغير.

<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 هذه الطلبات بشكل تسلسلي.

استجابه

لإنشاء استجابة مركبة، يمكنك استخدامالاستجابة للإرجاع نهج. set-bodyيمكن للعنصر استخدام تعبير لإنشاء JObject جديد مع كافة تمثيلات المكون المضمنة كخصائص.

<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>

حسبما تبدو السياسة الكاملة كما يلي:

<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>

الملخص

توفر خدمة APIM Microsoft Azure نهج مرنة يمكن تطبيقها بشكل انتقائي على حركة مرور HTTP وتمكن تكوين خدمات الواجهة الخلفية. سواء كنت ترغب في تحسين بوابة API الخاصة بك مع وظائف التنبيه أوالتحقق أو قدرات التحقق من الصحة أو إنشاء موارد مركبة جديدة استنادا إلى خدمات خلفية متعددة ، فإنsend-requestالنهج ذات الصلة تفتح عالمًا من الاحتمالات.