תנאים עם משתני תהליך

הדף הזה רלוונטי ל-Apigee ול-Apigee Hybrid.

לעיון במסמכי התיעוד של Apigee Edge

משפטי תנאי הם מבנה בקרה נפוץ בכל שפות התכנות. בדומה לשפת תכנות, הגדרת שרת proxy ל-API תומכת במשפטי תנאי לתהליכים, למדיניות, לשלבים ולכללי ניתוב. הגדרתם התנהגות דינמית ל-API באמצעות הגדרת משפטי תנאי. ההתנהגות הדינמית הזו מאפשרת לכם לבצע פעולות כמו המרת XML ל-JSON רק למכשירים ניידים, או ניתוב לכתובת URL של קצה עורפי על סמך סוג התוכן או פועל ה-HTTP של הודעת הבקשה.

במאמר הזה מוסבר איך להשתמש בתנאים כדי להחיל באופן דינמי תכונות של ניהול API בזמן ריצה, בלי לכתוב קוד.

הגדרת משפטי תנאי

התנהגות מותנית מיושמת בשרתי proxy של API באמצעות שילוב של תנאים ומשתנים. משפט מותנה נוצר באמצעות רכיב Condition. התנאי הבא הוא תנאי ריק:

<Condition></Condition>

כדי ליצור הצהרה מותנית, מוסיפים אופרטור מותנה ומשתנה באמצעות התחביר הבא:

<Condition>VARIABLE_NAME OPERATOR "VALUE"</Condition>

לדוגמה:

<Condition>request.verb = "GET"</Condition>

האופרטורים המותנים הנתמכים כוללים את = (שווה ל), != (לא שווה ל) ו-> (גדול מ). כדי שהקוד יהיה קריא יותר, אפשר גם לכתוב את התנאים כטקסט: equals,‏ notequals,‏ greaterthan.

כשעובדים עם נתיבי URI, אפשר להשתמש ב-~/ או ב-MatchesPath. אפשר גם להתאים ביטויים רגולריים של JavaRegex באמצעות האופרטור ~~.

התנאים משמשים להגדרת זרימות מותנות של שרתי proxy ל-API למשאבי API בקצה העורפי, כפי שמתואר במאמר יצירת זרימות מותנות למשאבי API בקצה העורפי. רשימה מלאה של תנאים מופיעה במאמר חומר עזר בנושא תנאים.

משתנים

התנאים פועלים על ידי הערכת הערכים של משתנים. משתנה הוא מאפיין של טרנזקציית HTTP שמופעלת על ידי proxy ל-API, או מאפיין של תצורת proxy ל-API. בכל פעם ש-proxy ל-API מקבל בקשה מאפליקציה, Apigee מאכלס רשימה ארוכה של משתנים שמשויכים לדברים כמו זמן המערכת, פרטי הרשת של האפליקציה, כותרות HTTP בהודעות, תצורת proxy ל-API, הרצות של מדיניות וכן הלאה. כך נוצר הקשר עשיר שבו אפשר להשתמש כדי להגדיר משפטי תנאי.

משתנים תמיד משתמשים בסימון נקודות. לדוגמה, כותרות HTTP בהודעת הבקשה זמינות כמשתנים שנקראים request.header.HEADER_NAME. לכן, כדי להעריך את Content-type header, אפשר להשתמש במשתנה request.header.Content-type. לדוגמה, request.header.Content-type = "application/json" מציין שסוג התוכן של הבקשה צריך להיות JSON.

נניח שאתם צריכים ליצור משפט תנאי שיגרום לאכיפת מדיניות רק כשבקשת הודעה היא GET. כדי ליצור תנאי שבודק את פועל ה-HTTP של בקשה, יוצרים את המשפט המותנה שלמטה. המשתנה בתנאי הזה הוא request.verb. הערך של המשתנה הוא GET. האופרטור הוא =.

<Condition>request.verb = "GET"</Condition>

אפשר גם להשתמש ב:

<Condition>request.verb equals "GET"</Condition>

‫Apigee משתמש בהצהרה כזו כדי להעריך תנאים. הדוגמה שלמעלה מחזירה True אם פועל ה-HTTP שמשויך לבקשה הוא GET. אם פועל ה-HTTP שמשויך לבקשה הוא POST, הביטוי יחזיר את הערך false.

כדי להפעיל התנהגות דינמית, אפשר לצרף תנאים לזרימות, לשלבים ולכללי ניתוב.

כשמצרפים תנאי לתהליך, יוצרים תהליך מותנה. תהליכים מותנים מופעלים רק אם התנאי מתקיים. אפשר לצרף לתהליך מותנה כמה מדיניות שרוצים. תהליך מותנה מאפשר ליצור כללי עיבוד מיוחדים מאוד להודעות בקשה או תגובה שעומדות בקריטריונים מסוימים.

לדוגמה, כדי ליצור Flow שמופעל רק כשפועל הבקשה הוא GET:

<Flows>
  <Flow name="ExecuteForGETs">
  <Condition>request.verb="GET"</Condition>
  </Flow>
</Flows>

כדי ליצור Flow אחד לבקשות של GET ו-Flow אחר לבקשות של GET: POST

<Flows>
  <Flow name="ExecuteForGETs">
    <Condition>request.verb="GET"</Condition>
  </Flow>
  <Flow name="ExecuteForPOSTs">
    <Condition>request.verb="POST"</Condition>
  </Flow>
</Flows>

כפי שמוצג בדוגמה שלמטה, אפשר להחיל את התנאי על שלב המדיניות עצמו. התנאי הבא גורם לאכיפה של המדיניות VerifyAPIKey רק אם הודעת הבקשה היא POST.

<PreFlow name="PreFlow">
    <Request>
        <Step>
            <Condition>request.verb equals "POST"</Condition>
            <Name>VerifyApiKey</Name>
        </Step>
    </Request>
</PreFlow>

אחרי שמגדירים זרימות מותנות כאלה, אפשר לצרף אליהן מדיניות, וכך לאפשר ל-API Proxy לאכוף קבוצה אחת של מדיניות עבור בקשות GET וקבוצה אחרת של מדיניות עבור בקשות POST.

למידע מקיף נוסף, אפשר לעיין במקורות המידע הבאים:

דוגמה 1

בדוגמה הבאה מוצג רצף פעולות מותנה יחיד בשם Convert-for-devices, שהוגדר ברצף הפעולות של התגובה של ProxyEndpoint. מוסיפים את התנאי כרכיב לישות שאליה התנאי חל. בדוגמה הזו, התנאי הוא רכיב של רצף הפעולות. לכן, רצף הפעולות יופעל בכל פעם שההצהרה תניב את הערך true.

<Flows>
  <Flow name="Convert-for-devices">
  <Condition>(request.header.User-Agent = "Mozilla")</Condition>
    <Response>
      <Step><Name>ConvertToJSON</Name></Step>
    </Response>
  </Flow>
</Flows>

לכל בקשה שמתקבלת מאפליקציה, Apigee שומר את הערכים של כל כותרות ה-HTTP שמופיעות כמשתנים. אם הבקשה מכילה כותרת HTTP בשם User-Agent, הכותרת הזו והערך שלה נשמרים כמשתנה בשם request.header.User-Agent.

בהינתן ההגדרה של ProxyEndpoint שלמעלה, מערכת Apigee בודקת את הערך של המשתנה request.header.User-Agent כדי לראות אם התנאי מחזיר את הערך true.

אם התנאי מקבל את הערך true, כלומר הערך של המשתנה request.header.User-Agent שווה ל-Mozilla, אז מתבצעת ההפעלה של Flow מותנה, והמדיניות XMLtoJSON שנקראת ConvertToJSON נאכפת. אם לא, הפעולה של Flow לא מתבצעת, ותגובת ה-XML מוחזרת ללא שינוי (בפורמט XML) לאפליקציה ששלחה את הבקשה.

דוגמה 2

נשתמש בדוגמה ספציפית שבה צריך להמיר הודעות תגובה מ-XML ל-JSON – אבל רק למכשירים ניידים. קודם יוצרים את המדיניות שתמיר את התגובה בפורמט XML מ-Weather API לפורמט JSON:

<XMLToJSON name="ConvertToJSON">
  <Options>
  </Options>
  <OutputVariable>response</OutputVariable>
  <Source>response</Source>
</XMLToJSON>

הגדרת המדיניות שלמעלה מציינת ל-proxy ל-API לקחת את הודעת התגובה, לבצע המרה מ-XML ל-JSON עם הגדרות ברירת מחדל, ואז לכתוב את התוצאה בהודעת התגובה החדשה. (אם ממירים הודעת בקשה מ-XML ל-JSON, פשוט מגדירים את שני הערכים האלה כ-request).

מכיוון שאתם רוצים להמיר תשובות מ-XML ל-JSON, אתם צריכים להגדיר זרימת תגובות מותנית כדי לבצע את ההמרה. לדוגמה, כדי להמיר את כל התגובות מ-XML ל-JSON לפני שהן מוחזרות לאפליקציית הלקוח, צריך להגדיר את רכיב response Flow הבא של ProxyEndpoint.

<Flows>
  <Flow name="Convert-for-devices">
    <Response>
      <Step><Name>ConvertToJSON</Name></Step>
    </Response>
  </Flow>
</Flows>

כשמפעילים את ה-API באמצעות הבקשה הרגילה, התשובה מפורמטת ב-JSON.

עם זאת, המטרה היא להמיר דוחות מזג אוויר ל-JSON רק אם הלקוח ששולח את הבקשה הוא מכשיר נייד. כדי להפעיל התנהגות דינמית כזו, צריך להוסיף הצהרה מותנית ל-Flow.

בדיקת התהליך המותנה

בדוגמה הזו של בקשה, הכותרת User-Agent של HTTP מוגדרת ל-Mozilla, ולכן הביטוי המותנה מוערך כ-true והזרימה המותנית Convert-for-devices מופעלת.

curl -H "User-Agent:Mozilla" http://example.com/weather/forecastrss?w=12797282

או, כדי להדפיס את הפלט בצורה יפה במקומות שבהם Python זמין:

curl -H "User-Agent:Mozilla" http://example.com/weather/forecastrss?w=12797282 | python -mjson.tool

דוגמה לתשובה:

. . .

"yweather_forecast": [
   {
      "code": "11",
      "date": "12 Dec 2012",
      "day": "Wed",
      "high": "55",
      "low": "36",
      "text": "Showers"
    },
    {
      "code": "32",
      "date": "13 Dec 2012",
      "day": "Thu",
      "high": "56",
      "low": "38",
      "text": "Sunny"
    }
  ]
}

. . .

בקשה שנשלחת בלי הכותרת User-Agent, או עם ערך שונה מ-Mozilla, תגרום לתגובה בפורמט XML.

$ curl http://example.com/weather/forecastrss?w=12797282

מוחזרת תגובת ה-XML ללא שינוי.

דוגמה לתשובה:

<yweather:forecast day="Wed" date="12 Dec 2012" low="36" high="55" text="Showers" code="11" /> <yweather:forecast day="Thu" date="13 Dec 2012" low="38" high="56" text="Sunny" code="32" />

התאמת תבניות

בקטע הזה מוסבר איך להשתמש בהתאמת תבניות עם תנאים בתהליך עבודה של Apigee.

אופרטורים

בקטע הזה מוסבר איך להשתמש באופרטורים הבאים להתאמת תבניות בהצהרות מותנות:

התאמות

קודם נבחן את האופרטור של התנאי Matches או ~. שני האופרטורים האלה זהים – הגרסה באנגלית, Matches, נחשבת לאפשרות קריאה יותר.

סיכום: האופרטור Matches מאפשר שתי אפשרויות. המחרוזת צריכה להיות זהה למחרוזת שמוגדרת, או להתאים לתווים כלליים לחיפוש עם *. כצפוי, התו הכללי לחיפוש מתאים לאפס תווים או יותר. בואו נראה איך זה עובד.

.

קוד ה-XML הבא מציג תנאי Step. הוא מפעיל את המדיניות SomePolicy כשהתנאי מחזיר את הערך true. בדוגמה הזו, אנחנו בודקים את המשתנה proxy.pathsuffix, משתנה מובנה ב-Apigee שמאחסן את הסיומת של הנתיב של הבקשה. עם זאת, אפשר לבדוק את הערך של כל משתנה flow שמכיל מחרוזת. לכן, במקרה הזה, אם נתיב הבסיס של הבקשה הנכנסת הוא /animals, והבקשה היא /animals/cat, אז הסיומת של הנתיב היא המחרוזת המילולית /cat.

<PreFlow name="PreFlow">
  <Request>
    <Step>
      <Condition>(proxy.pathsuffix Matches "/cat")</Condition>
      <Name>SomePolicy</Name>
    </Step>
  </Request>
  <Response/>
</PreFlow>

שאלה: איזה סיומת של נתיב proxy תגרום להרצת SomePolicy? יש רק אפשרות אחת.

קריאה ל-API:

GET http://example.com/matchtest/cat

האם המדיניות מופעלת? כן, כי הסיומת של נתיב ה-Proxy זהה בדיוק ל-/cat. היא לא תפעל אם הסיומת היא /bat או /dog או / או כל סיומת אחרת.

עכשיו נתייחס למשפט התנאי הזה שבו אנחנו משתמשים בתו הכללי לחיפוש *:

<Condition>(proxy.pathsuffix Matches "/*at")</Condition>

קריאה ל-API:

GET http://example.com/matchtest/cat

המדיניות מופעלת? כן, כי התו הכללי מתאים לכל תו, ו-"/cat הוא התאמה.

קריאה ל-API:

GET http://example.com/matchtest/bat

האם המדיניות מופעלת? כן, כי התו הכללי לחיפוש מתאים לכל תו, ולכן "/bat" הוא התאמה.

קריאה ל-API:

GET http://example.com/matchtest/owl

המדיניות מופעלת? ברור שלא – למרות שהתו הכללי לחיפוש תואם ל-o, האותיות wl לא תואמות.

עכשיו נעביר את התו הכללי לסוף הסיומת:

<Condition>(proxy.pathsuffix Matches "/cat*")</Condition>

קריאה ל-API:

GET http://example.com/matchtest/cat

האם המדיניות מופעלת? כן, כי התו הכללי מתאים לאפס תווים או יותר מכל סוג.

קריאה ל-API:

GET http://example.com/matchtest/bat

המדיניות מופעלת? לא, /bat לא תואם.

קריאה ל-API:

GET http://example.com/matchtest/cat123

המדיניות מופעלת? כן, התו הכללי מתאים לאפס תווים או יותר מכל התווים, ולכן 123 יוצר התאמה.

קריאה ל-API:

GET http://example.com/matchtest/cat/bird/mouse

האם המדיניות מופעלת? כן, כי התו הכללי מתאים לאפס תווים או יותר מכל סוג, ולכן /bird/mouse מייצר התאמה. שימו לב איך ביטוי כזה יכול להכניס אתכם לצרות כי הוא מתאים לכל מה שאחרי התווים המילוליים.

שאלה: האם האופרטור Matches תלוי באותיות רישיות?

כן. נניח שיש לכם תנאי כזה:

<Condition>(proxy.pathsuffix Matches "/*At")</Condition>

קריאה ל-API:

GET http://example.com/matchtest/cat

האם המדיניות מופעלת? לא, התו הכללי מתאים לכל אות (ללא קשר לרישיות), אבל האות הקטנה a לא מתאימה ל-A.

קריאה ל-API:

GET http://example.com/matchtest/bAt

המדיניות מופעלת? כן, המקרה תואם.

שאלה: איך משתמשים באופרטור Matches כדי לבטל את המשמעות של תווים?

כדי לבטל את המשמעות של תווים שמורים, משתמשים בתו האחוז %. לדוגמה:

<Condition>(proxy.pathsuffix Matches "/c%*at")</Condition>

קריאה ל-API:

GET http://example.com/matchtest/cat

האם המדיניות מופעלת? לא, האופרטור Matches מחפש את המחרוזת המילולית c*at.

קריאה ל-API:

GET http://example.com/matchtest/c*at

שאלה:האם המדיניות מופעלת?

כן, הנתיב הזה, למרות שהוא קצת לא שגרתי, מתאים.

JavaRegex

כפי שאפשר לראות, האופרטור Matches מתאים למצבים פשוטים. אבל אפשר להשתמש באופרטור אחר, JavaRegex או ~~. שני האופרטורים האלה זהים, אבל JavaRegex נחשב לקריא יותר. הוא נקרא JavaRegex כי הוא מאפשר התאמה של דפוסי ביטויים רגולריים, ו-Apigee פועל לפי אותם כללים כמו המחלקות בחבילה java.util.regex בשפת Java. אופן הפעולה של האופרטור JavaRegex שונה מאוד מאופן הפעולה של האופרטור Matches, ולכן חשוב לא להתבלבל ביניהם.

סיכום: האופרטור JavaRegex מאפשר להשתמש בתחביר של ביטויים רגולריים במשפטי התניה.

בדוגמה הבאה מוצג תנאי Step. היא מפעילה את המדיניות SomePolicy אם התנאי מקבל את הערך true. בדוגמה הזו, אנחנו בודקים את המשתנה proxy.pathsuffix, משתנה מובנה ב-Apigee שמאחסן את הסיומת של נתיב הבקשה. אם נתיב הבסיס של הבקשה הנכנסת הוא /animals והבקשה היא /animals/cat, אז סיומת הנתיב היא המחרוזת המילולית /cat.

    <PreFlow name="PreFlow">
        <Request>
            <Step>
                <Condition>(proxy.pathsuffix JavaRegex "/cat")</Condition>
                <Name>SomePolicy</Name>
            </Step>
        </Request>
        <Response/>
    </PreFlow>

שאלה: איזה סיומת של נתיב פרוקסי תגרום להפעלת SomePolicy? בדיוק כמו באופרטור Matches, יש רק אפשרות אחת במקרה הזה.

קריאה ל-API:

GET http://example.com/matchtest/cat

האם המדיניות מופעלת? כן, כי הסיומת של נתיב ה-Proxy זהה בדיוק ל-/cat. היא לא תפעל אם הסיומת היא /bat או /dog או כל דבר אחר.

עכשיו ניצור ביטוי רגולרי באמצעות הכמותן *. הכמות הזו מתאימה לאפס או יותר מהתווים הקודמים.

<Condition>(proxy.pathsuffix JavaRegex "/c*t")</Condition>

קריאה ל-API:

GET http://example.com/matchtest/cat

האם המדיניות מופעלת? לא! הכמות * מתאימה לאפס או יותר מהתו הקודם, שהוא c.

קריאה ל-API:

GET http://example.com/matchtest/ccccct

האם המדיניות מופעלת? כן, כי התו הכללי מתאים לאפס או יותר מהתו הקודם.

בשלב הבא, אנחנו משתמשים בכמות ?, שתואמת לתו הקודם פעם אחת, או לא תואמת לו בכלל.

<Condition>(proxy.pathsuffix JavaRegex "/ca?t")</Condition>

קריאה ל-API:

GET http://example.com/matchtest/cat

האם המדיניות מופעלת? כן. הכמותן ? מתאים לאפס מופעים או למופע אחד של התו הקודם, שהוא a.

קריאה ל-API:

GET http://example.com/matchtest/ct

האם המדיניות מופעלת? כן. הכמת ? מתאים למופע אחד או לאף מופע של התו הקודם. במקרה הזה, אין תו a, ולכן התנאי שווה ל-true.

קריאה ל-API:

GET http://example.com/matchtest/caat

האם המדיניות מופעלת? לא. הכמות ? תואמת לאחד מהתו הקודם, שהוא a.

לאחר מכן, משתמשים בסגנון קיבוץ של ביטוי רגולרי [abc]. הוא תואם לתווים a או b או c.

<Condition>(proxy.pathsuffix JavaRegex "/[cbr]at")</Condition>

קריאה ל-API:

GET http://example.com/matchtest/cat

האם המדיניות מופעלת? כן. אנחנו משתמשים כאן בביטויים רגולריים, והביטוי [cbr] תואם ל-c, b או r. גם השיחות האלה נחשבות להתאמות:

GET http://example.com/matchtest/bat

GET http://example.com/matchtest/rat

אבל אין התאמה:

GET http://example.com/matchtest/mat

שאלה: האם האופרטור JavaRegex תלוי באותיות רישיות?

כן. נניח שיש לכם תנאי כזה:

<Condition>(proxy.pathsuffix JavaRegex "/ca?t")</Condition>

קריאה ל-API:

GET http://example.com/matchtest/cat

המדיניות מופעלת? כן, הביטוי הרגולרי תואם לאפס או לתו אחד מהתו הקודם, שהוא a.

קריאה ל-API:

GET http://example.com/matchtest/cAt

שאלה: האם המדיניות מופעלת?

לא, כי האות הגדולה A לא זהה לאות הקטנה a.

MatchesPath

אפשר לציין את האופרטור MatchesPath גם כך ~/. הוא דומה קצת לאופרטורים Matches (~) ו-JavaRegex (~~). אבל MatchesPath שונה לחלוטין.

חשוב לזכור שהאופרטור הזה מתייחס לנתיב כסדרה של חלקים. לכן, אם הנתיב הוא /animals/cats/wild, אפשר לחשוב על הנתיב ככולל את החלקים /animals, /cats ו-/wild.

האופרטור MatchesPath מאפשר להשתמש בשני סימונים של תווים כלליים לחיפוש: כוכבית אחת (*) ושתי כוכביות (**). כוכבית אחת מתאימה לרכיב נתיב אחד. שתי כוכביות מתאימות לרכיב נתיב אחד או יותר.

נבחן דוגמה. בדוגמה הזו, אנחנו בודקים את המשתנה proxy.pathsuffix, משתנה מובנה ב-Apigee שמאחסן את הסיומת של הנתיב של הבקשה. עם זאת, אפשר לבדוק את הערך של כל משתנה של זרימת נתונים שמכיל מחרוזת.

    <PreFlow name="PreFlow">
        <Request>
            <Step>
                <Condition>(proxy.pathsuffix MatchesPath "/animals/*")</Condition>
                <Name>SomePolicy</Name>
            </Step>
        </Request>
        <Response/>
    </PreFlow>

שאלה: איזה סיומת של נתיב proxy תגרום להרצת SomePolicy?

קריאה ל-API:

GET http://example.com/matchtest/animals

שאלה: האם המדיניות מופעלת?

לא, כי התנאי דורש עוד רכיב נתיב אחרי /animals, כמו שצוין על ידי /*.

קריאה ל-API:

GET http://example.com/matchtest/animals/

האם המדיניות מופעלת? כן, לנתיב יש עוד רכיב נתיב (החלק אחרי /animals/), אבל הוא ריק.

קריאה ל-API:

GET http://example.com/matchtest/animals/cats

המדיניות מופעלת? כן, כי הנתיב כולל בבירור אלמנט (/cats) שמופיע אחרי /animals

קריאה ל-API:

GET http://example.com/matchtest/animals/cats/wild

שאלה: האם המדיניות מופעלת?

לא, כי הכוכבית הבודדת מתאימה רק לרכיב נתיב אחד, וב-API הזה יש יותר מרכיב אחד אחרי /animals.

עכשיו נשתמש בכוכבית כפולה:

    <PreFlow name="PreFlow">
        <Request>
            <Step>
                <Condition>(proxy.pathsuffix MatchesPath "/animals/**")</Condition>
                <Name>SomePolicy</Name>
            </Step>
        </Request>
        <Response/>
    </PreFlow>

שאלה: איזה סיומת של נתיב proxy תגרום להרצת SomePolicy?

קריאה ל-API:

GET http://example.com/matchtest/animals

האם המדיניות מופעלת? לא, כי בתנאי צריך לציין לפחות רכיב אחד של נתיב שמוגדר על ידי /**.

קריאה ל-API:

GET http://example.com/matchtest/animals/

האם המדיניות מופעלת?

כן, לנתיב יש עוד רכיב נתיב (החלק אחרי /animals/), אבל הוא ריק.

קריאה ל-API:

GET http://example.com/matchtest/animals/cats

האם המדיניות מופעלת?

כן, כי הנתיב מכיל לפחות רכיב אחד שמופיע אחרי /animals

קריאה ל-API:

GET http://example.com/matchtest/animals/cats/wild

האם המדיניות מופעלת?

כן, כי הנתיב מכיל יותר מרכיב אחד שמופיע אחרי /animals

ערבוב של כוכביות

אפשר להשתמש בשילובים של כוכבית אחת (*) וכוכבית כפולה (**) כדי לחדד עוד יותר את התאמת הנתיבים.

    <PreFlow name="PreFlow">
        <Request>
            <Step>
                <Condition>(proxy.pathsuffix MatchesPath "/animals/*/wild/**")</Condition>
                <Name>SomePolicy</Name>
            </Step>
        </Request>
        <Response/>
    </PreFlow>

קריאה ל-API:

כל הקריאות הבאות ל-API יניבו התאמה:

GET http://example.com/matchtest/animals/cats/wild/

וגם

GET http://example.com/matchtest/animals/dogs/wild/austrailian

and

GET http://example.com/matchtest/animals/birds/wild/american/finches

משאבי API

שירותי RESTful הם אוספים של משאבי API. משאב API הוא קטע של נתיב URI שמזהה ישות מסוימת שמפתחים יכולים לגשת אליה באמצעות קריאה ל-API שלכם. לדוגמה, אם השירות שלכם מספק דוחות ותחזיות של מזג האוויר, יכול להיות שבשירות הקצה העורפי שלכם מוגדרים שני משאבי API:

  • http://mygreatweatherforecast.com/reports
  • http://mygreatweatherforecast.com/forecasts

כשיוצרים proxy ל-API (כפי שמוסבר במאמר פיתוח פתרונות API proxy ראשון), יוצרים לפחות כתובת URL בסיסית של כינוי שממופה לשירות לקצה העורפי. לדוגמה:

כתובת URL בסיסית של ה-Backend כתובת URL חדשה או שוות ערך של proxy ל-API
http://mygreatweatherforecast.com http://example.com/mygreatweatherforecast

בשלב הזה אפשר לבצע קריאות ל-API לשרת העורפי באמצעות כתובת ה-URL הבסיסית. אבל כשמשתמשים בכתובת ה-proxy ל-API, הדברים מתחילים להיות מעניינים.

בנוסף לניתוח נתונים של API שאפייג'י מתחיל לאסוף כשמשתמשים ב-proxy ל-API, פרוקסי מאפשרים גם להגדיר זרימות מותנות שממופות למשאבים בשרת העורפי. בעצם, אם מתקבלת קריאה של GET למשאב /reports, אפייג'י צריך לבצע פעולה מסוימת.

התמונה הבאה מציגה את ההבדל בהתנהגות בין שתי כתובות URL שבסופו של דבר ניגשות לאותו קצה עורפי. אחת מהן היא כתובת ה-URL של המשאב ללא פרוקסי, והשנייה היא proxy ל-API של Apigee עם זרימה מותנית לאותו משאב בקצה העורפי. בהמשך נסביר על זרימות מותנות בפירוט רב יותר.

במקרה של כתובת URL של proxy ל-API של Apigee עם זרימה מותנית, התגובה ממירה XML ל-JSON ומאספת נתונים לצורך ניתוח.

איך שרתי proxy ל-API ממופים למשאבים ספציפיים בקצה העורפי

באמצעות כתובת URL של proxy ל-API שממופה לכתובת ה-URL הבסיסית של שירות לקצה העורפי (כשיוצרים את ה-proxy), אפשר להוסיף תהליכים מותנים למשאבים ספציפיים, כמו המשאבים /reports ו-/forecasts שצוינו קודם.

נניח שרוצים ש-Apigee יבצע פעולה מסוימת כשמתקבלות שיחות למשאבים /reports או /forecasts. בשלב הזה אתם לא אומרים ל-Apigee מה לעשות, אלא רק שהוא צריך להאזין לקריאות למשאבים האלה. אפשר לעשות זאת באמצעות תנאים. ב-proxy ל-API של Apigee, אפשר ליצור זרימות מותנות עבור /reports ו-/forecasts. לצורך המחשה, בדוגמה הבאה של XML של שרת proxy ל-API מוצג איך התנאים האלה יכולים להיראות.

<Flows>
    <Flow name="reports">
        <Description/>
        <Request/>
        <Response/>
        <Condition>(proxy.pathsuffix MatchesPath "/reports") and (request.verb = "GET")</Condition>
    </Flow>
    <Flow name="forecasts">
        <Description/>
        <Request/>
        <Response/>
        <Condition>(proxy.pathsuffix MatchesPath "/forecasts") and (request.verb = "GET")</Condition>
    </Flow>
</Flows>

התנאים האלה קובעים: כשמתקבלת בקשה של GET עם /reports ו-/forecasts בכתובת ה-URL, מערכת Apigee תבצע את הפעולה שתגדירו (מפתח ה-API) באמצעות המדיניות שמשויכת לזרימות האלה.

עכשיו נראה דוגמה להוראה ל-Apigee מה לעשות כשמתקיים תנאי מסוים. ב-XML של ה-API Proxy הבא, כשבקשת GET נשלחת אל https://example.com/mygreatweatherforecast/reports, ‏ Apigee מפעיל את מדיניות XML-to-JSON-1 בתגובה.

<Flows>
  <Flow name="reports">
    <Description/>
    <Request/>
    <Response>
      <Step>
        <Name>XML-to-JSON-1</Name>
      </Step>
    </Response>
  <Condition>(proxy.pathsuffix MatchesPath "/reports") and (request.verb = "GET")</Condition>
</Flow>

בנוסף לזרימות מותנות אופציונליות, כל proxy ל-API כולל גם שתי זרימות ברירת מחדל: <PreFlow> שמופעלת לפני הזרימות המותנות, ו-<PostFlow> שמופעלת אחרי הזרימות המותנות. הזרימות האלה שימושיות להפעלת מדיניות כשמתבצעת קריאה כלשהי ל-proxy ל-API. לדוגמה, אם רוצים לאמת את מפתח ה-API של אפליקציה בכל קריאה, בלי קשר למשאב העורפי שאליו מתבצעת הגישה, אפשר להוסיף מדיניות לאימות מפתח API ל-<PreFlow>. מידע נוסף על זרימות זמין במאמר הגדרת זרימות.

יצירת תהליכים מותנים למשאבי קצה עורפי

הגדרת זרימות מותנות למשאבי קצה עורפי ב-proxy ל-API היא אופציונלית לחלוטין. עם זאת, הזרימות המותנות האלה מאפשרות לכם להחיל ניהול ומעקב ברמת פירוט גבוהה.

תוכלו:

  • החלת ניהול באופן שמשקף את הסמנטיקה של מודל ה-API
  • החלת מדיניות והתנהגות מבוססת-סקריפט על נתיבי משאבים (URI) ספציפיים
  • איסוף מדדים מפורטים עבור שירותי Analytics

לדוגמה, נניח שאתם צריכים להחיל סוגים שונים של לוגיקה על משאבי /developers עד /apps בשרת העורפי.

כדי לעשות זאת, מוסיפים שני זרמי תנאים ב-proxy ל-API: ‏ /developers ו-/apps.

כדי להוסיף זרימה מותנית:

  1. בוחרים בכרטיסייה Develop (פיתוח) בכלי לעריכת שרת proxy.
  2. בחלונית הימנית, בוחרים באפשרות Proxy endpoints > default (נקודות קצה של שרת Proxy > ברירת מחדל).

    בחלונית הימנית, בוחרים באפשרות Proxy endpoints (נקודות קצה של שרת Proxy) > default (ברירת מחדל).

  3. לוחצים על הלחצן + מעל החלונית תגובה.

    לחצן להוספת זרימה מותנית

  4. בתיבת הדו-שיח הוספת זרימה מותנית, מזינים את ההגדרות הבאות:
    • שם התהליך: Developers
    • סוג התנאי: Path
    • נתיב: /developers

    תיבת דו-שיח להוספת זרימה מותנית.

    התנאי יופעל (והכללים יבוצעו) אם שיחה תישלח לשרת ה-proxy עם /developers בסוף ה-URI.

  5. עכשיו מוסיפים תהליך מותנה ל-/apps, ומניחים שרוצים שהתנאי יופעל גם ב-URI וגם בפועל POST בבקשה. ההגדרה כוללת את הפעולות הבאות:
    • שם התהליך: Apps
    • סוג התנאי: Path and Verb
    • נתיב: /apps
    • פועל: POST

    התנאי יופעל (והכללים יבוצעו) אם שיחה תישלח לשרת ה-proxy עם /apps בסוף ה-URI ועם פועל POST.

הזרימות שנוספו מוצגות בחלונית תגובה:

תיבת דו-שיח להוספת זרימה מותנית.

בוחרים באחד מהזרימות כדי לראות את הגדרת הזרימה המותנית בתצוגת הקוד של כלי העריכה של ה-proxy ל-API

<Flow name="Apps">
    <Description>Developer apps registered in Developer Services</Description>
    <Request/>
    <Response/>
    <Condition>(proxy.pathsuffix MatchesPath "/apps") and (request.verb = "POST")</Condition>
</Flow>

כפי שאפשר לראות, משאבי API הם פשוט תהליכי עבודה מותנים שמעריכים את נתיב ה-URI של הבקשה הנכנסת. (המשתנה proxy.pathsuffix מזהה את ה-URI של הבקשה שאחרי BasePath שהוגדר בהגדרות של ProxyEndpoint).

כל משאב API שאתם מגדירים מיושם על ידי תהליך מותנה ב-proxy ל-API. (ראו הגדרת תהליכי עבודה).

אחרי שפורסים את ה-proxy ל-API בסביבת הבדיקה, הבקשה הבאה:

http://example.com/PROXY_PATH/apps

יגרום להערכת התנאי כ-true, והתהליך הזה, יחד עם כל כללי המדיניות המשויכים, יופעל.

תנאי הדוגמה הבא משתמש בביטוי רגולרי של Java כדי לזהות קריאות שבוצעו למשאב /apps עם או בלי קו נטוי בסוף (/apps או /apps/**):

<Condition>(proxy.pathsuffix JavaRegex "/apps(/?)") and (request.verb = "POST")</Condition>

מידע נוסף על סוג התנאי הזה זמין במאמר How to match regardless whether there is a trailing "/" ...‎ בקהילת Apigee.

בניית מודלים של כתובות URI היררכיות

במקרים מסוימים, משאבי ה-API יהיו היררכיים. לדוגמה, Developer apps list API מספק שיטה לרישום כל האפליקציות ששייכות למפתח. נתיב ה-URI הוא:

/developers/DEVELOPER_EMAIL/apps

יכול להיות שיש לכם מקורות שבהם נוצר מזהה ייחודי לכל ישות בקולקציה, ולפעמים הוא מסומן כך:

/genus/:id/species

הנתיב הזה רלוונטי באותה מידה לשני מזהי ה-URI הבאים:

/genus/18904/species
/genus/17908/species

כדי לייצג את המבנה הזה במשאב API, אפשר להשתמש בתווים כלליים לחיפוש. לדוגמה:

/developers/*/apps
/developers/*example.com/apps
/genus/*/species

הם יתאימו את ה-URI ההיררכיים למשאבי API.

במקרים מסוימים, במיוחד בממשקי API היררכיים מאוד, יכול להיות שתרצו פשוט לפתור את כל מה שמתחת לקטע URI מסוים. כדי לעשות את זה, משתמשים בתו כללי של כוכבית כפולה בהגדרת המשאב. לדוגמה, אם מגדירים את משאב ה-API הבא:

/developers/**

משאב ה-API הזה יפתור את נתיבי ה-URI הבאים:

/developers/DEVELOPER_EMAIL/apps
/developers/DEVELOPER_EMAIL/keys
/developers/DEVELOPER_EMAIL/apps/APP_ID/keys

כך ייראה התנאי של הזרימה המותנית בהגדרת proxy ל-API:

<Condition>(proxy.pathsuffix MatchesPath "/developers/**") and (request.verb = "POST")</Condition>

דוגמאות נוספות

תנאי שמצורף ל-RouteRule

<RouteRule name="default">
  <!--this routing executes if the header indicates that this is an XML call. If true, the
    call is routed to the endpoint XMLTargetEndpoint-->
  <Condition>request.header.content-type = "text/xml"</Condition>
  <TargetEndpoint>XmlTargetEndpoint</TargetEndpoint>
</RouteRule>

תנאי שצורף למדיניות

<Step>
  <!--the policy MaintenancePolicy only executes if the response status code is exactly 503 -->
  <Condition>response.status.code = 503</Condition>
  <Name>MaintenancePolicy</Name>
</Step>

תהליך מותנה

<!-- this entire flow is executed only if the request verb is a GET-->
<Flow name="GetRequests">
  <Condition>request.verb="GET"</Condition>
  <Request>
    <Step>
<!-- this policy only executes if request path includes a term like statues-->
<Condition>request.path ~ "/statuses/**"</Condition>
      <Name>StatusesRequestPolicy</Name>
    </Step>
  </Request>
  <Response>
    <Step>
<!-- this condition has multiple expressions. The policy executes if the response code status is exactly 503 or 400-->
<Condition>(response.status.code = 503) or (response.status.code = 400)</Condition>
      <Name>MaintenancePolicy</Name>
    </Step>
  </Response>
</Flow>

אופרטורים לדוגמה בתנאים

הנה כמה דוגמאות לאופרטורים שמשמשים ליצירת תנאים:

  • request.header.content-type = text/xml
  • request.header.content-length < 4096 && request.verb = PUT
  • response.status.code = 404 || response.status.code = 500
  • request.uri MatchesPath /*/statuses/**
  • request.queryparam.q0 NotEquals 10

דוגמה מעשית: התעלמות מ-/ בסוף נתיב

מפתחי Apigee רוצים בדרך כלל לטפל בשני הסיומות האלה של נתיבים: /cat ו-/cat/. הסיבה לכך היא שחלק מהמשתמשים או הלקוחות עשויים לקרוא ל-API עם קו נטוי נוסף בסוף הנתיב, ואתם צריכים להיות מסוגלים לטפל בזה בהצהרות התנאי שלכם. מקרה השימוש הזה נדון במאמר איך להתאים כתובת URL בלי קשר לשאלה אם יש בסוף שלה '/'....

אם רוצים, אפשר להשיג את זה בלי להשתמש בביטוי רגולרי, כך:

    <PreFlow name="PreFlow">
        <Request>
            <Step>
                <Condition>(proxy.pathsuffix = "/cat") OR (proxy.pathsuffix = "/cat/")</Condition>
                <Name>SomePolicy</Name>
            </Step>
        </Request>
        <Response/>
    </PreFlow>

זו אפשרות טובה. היא ברורה וניתנת לקריאה.

אפשר לעשות את אותו הדבר עם ביטוי רגולרי (Regex), אבל כך: הסוגריים משמשים לקיבוץ החלק של הביטוי הרגולרי בהצהרה, אבל הם לא נדרשים.

<Condition>(proxy.pathsuffix JavaRegex "/cat(/?)"</Condition>

קריאות ל-API:

GET http://example.com/matchtest/cat
או ‫GET http://example.com/matchtest/cat/

האם המדיניות מופעלת? כן. שימו לב שבביטוי רגולרי, התו ? מייצג: התאמה לאפס תווים או לתו אחד מהתו הקודם. לכן, גם /cat וגם /cat/ הן התאמות.

קריאה ל-API:

GET http://example.com/matchtest/cat/spotted

האם המדיניות מופעלת? לא. הביטוי הרגולרי תואם לאפס מופעים או למופע אחד בלבד של התו הקודם, ואסור להשתמש בשום דבר אחר.

התאמה של מחרוזות שרירותיות באמצעות JavaRegex

בכל הדוגמאות בנושא הזה, אנחנו מראים איך להתאים לאחד ממשתני הזרימה המובנים: proxy.pathsuffix. חשוב לדעת שאפשר לבצע התאמה לתבנית בכל מחרוזת שרירותית או משתנה זרימה, בין אם זה משתנה זרימה מובנה כמו proxy.pathsuffix או לא.

לדוגמה, אם יש לכם תנאי שבודק מחרוזת שרירותית, אולי מחרוזת שמוחזרת במטען ייעודי (payload) של קצה עורפי, או מחרוזת שמוחזרת מחיפוש בשרת אימות, אתם יכולים להשתמש באופרטורים של התאמה כדי לבדוק אותה. אם משתמשים ב-JavaRegex, הביטוי הרגולרי יושווה למחרוזת הנושא כולה. אם הנושא הוא abc והביטוי הרגולרי הוא [a-z], אין התאמה כי [a-z] תואם בדיוק לתו אחד של אלפא. הביטוי [a-z]+ פועל, וכך גם [a-z]* ו-[a-z]{3}.

נבחן עכשיו דוגמה קונקרטית. נניח ששרת האימות מחזיר רשימת תפקידים כמחרוזת מופרדת בפסיקים: editor, author, guest.

המחרוזת הזו לא תעבוד כדי לבדוק אם יש תפקיד עריכה, כי editor הוא רק חלק מהמחרוזת כולה.

<Condition>returned_roles ~~ "editor"</Condition>

אבל המבנה הזה יעבוד:

<Condition>(returned_roles ~~ ".*\beditor\b.*")</Condition>

היא פועלת כי היא מתחשבת במעברי מילים ובכל חלק אחר במחרוזת עם התחילית והסיומת .*.

בדוגמה הזו, אפשר גם לבדוק את editor באמצעות האופרטור Matches:

<Condition>(returned_roles ~~ "*editor*")</Condition>

עם זאת, במקרים שבהם נדרשת רמת דיוק גבוהה יותר, JavaRegex היא לרוב בחירה טובה יותר.

הוספת תו בריחה למרכאות כפולות בביטויי JavaRegex

תחביר התנאי מחייב להוסיף מירכאות כפולות סביב ביטוי JavaRegex. לכן, אם יש לכם ביטוי רגולרי שכולל מירכאות כפולות, אתם צריכים דרך חלופית להתאמה שלהן. הפתרון הוא Unicode. לדוגמה, נניח שאתם מעבירים כותרת שכוללת מירכאות כפולות, כמו הכותרת הבאה:

 -H 'content-type:multipart/related; type="application/xop+xml"'

אם תנסו להתאים את הכותרת הזו בתנאי של ביטוי רגולרי, תקבלו שגיאת Invalid Condition כי הביטוי כולל את המירכאות הכפולות:

request.header.Content-Type ~~ "(multipart\/related)(; *type="application\/xop\+xml\")"

הפתרון הוא להחליף את המירכאות הכפולות שמבוססות על ASCII במקבילות שלהן ב-Unicode, ‏ \u0022. לדוגמה, הביטוי הבא תקין ומפיק את התוצאה הצפויה:

request.header.Content-Type ~~ "(multipart\/related)(; *type=\u0022application\/xop\+xml\u0022)"