مشاركة عبر


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

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

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

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

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

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

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

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

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

لقطة شاشة تظهر خطاف ويب Slack.

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

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

إرسال الطلب

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

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

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

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

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

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

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

<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 يكون صحيحا، يعتبر الرمز المميز صالحا.

بدلا من ذلك، إذا لم يتضمن خادم التفويض الحقل "active" للإشارة إلى ما إذا كان الرمز المميز صالحا أم لا، فاستخدم أداة عميل HTTP مثل curl تحديد الخصائص التي تم تعيينها في رمز مميز صالح. على سبيل المثال، إذا كانت استجابة الرمز المميز الصالحة تحتوي على خاصية تسمى "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 استخدام الرموز المميزة، تقوم إدارة واجهة برمجة التطبيقات أيضا بإرجاع 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 لدمج الخدمات الخارجية المفيدة في عملية الطلبات والاستجابات المتدفقة عبر خدمة إدارة واجهة برمجة التطبيقات.

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

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

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

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

تزوير مورد

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

لقطة شاشة توضح عملية لوحة معلومات جديدة يتم تكوينها في مدخل 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>

ترسل إدارة واجهة برمجة التطبيقات هذه الطلبات بالتتابع.

استجابه

لإنشاء استجابة مركبة، يمكنك استخدامالاستجابة للإرجاع نهج. 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النهج ذات الصلة تفتح عالمًا من الاحتمالات.