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

הדף הזה רלוונטי ל-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>

האופרטורים המותנים הנתמכים כוללים = (שווה ל-), != (לא שווה ל-) ו-> (גדול מ-). כדי שהקוד יהיה קריא יותר, אפשר גם לכתוב את התנאים כך: text: 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, יוצרים Flow מותנה. תהליכים מותנים מופעלים רק כשהתנאי הוא true. אפשר לצרף כמה מדיניות שרוצים ל-Flow מותנה. בעזרת Flow מותנה, אתם יכולים ליצור כללי עיבוד מיוחדים מאוד להודעות בקשה או תגובה שעומדות בקריטריונים מסוימים.

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

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

כדי ליצור Flow אחד לבקשות של GET ו-Flow אחר לבקשות של 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, התהליך המותנה מופעל ומדיניות XMLtoJSON שנקראת ConvertToJSON נאכפת. אם לא, התהליך לא מבוצע, ותגובת ה-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 לפני שהן מוחזרות לאפליקציית הלקוח, צריך להגדיר את רכיב התגובה ProxyEndpoint Flow באופן הבא.

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

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

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

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

בדוגמת הבקשה הזו, כותרת ה-HTTP‏ User-Agent מוגדרת ל-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 הבאה מוצג תנאי לביצוע השלב. היא מפעילה את המדיניות SomePolicy כשהתנאי מחזיר את הערך true. בדוגמה הזו, אנחנו בודקים את המשתנה proxy.pathsuffix, משתנה מובנה ב-Apigee שמאחסן את הסיומת של נתיב כתובת ה-URL של הבקשה. עם זאת, אפשר לבדוק את הערך של כל משתנה זרימה שמכיל מחרוזת. לכן, במקרה הזה, אם נתיב הבסיס של הבקשה הנכנסת הוא /animals והבקשה היא /animals/cat, הסיומת של נתיב כתובת ה-URL היא המחרוזת המילולית /cat.

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

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

קריאה ל-API:

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

האם המדיניות מופעלת? כן, כי הסיומת של נתיב כתובת ה-URL זהה בדיוק ל-/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 מאפשר להשתמש בתחביר של ביטויים רגולריים במשפטי התניה.

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

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

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

קריאה ל-API:

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

האם המדיניות מופעלת? כן, כי הסיומת של נתיב כתובת ה-URL של ה-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] או grouping של ביטוי רגולרי. הוא תואם לתווים 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 שמאחסן את הסיומת של נתיב כתובת ה-URL של הבקשה. עם זאת, אפשר לבדוק את הערך של כל משתנה זרימה שמכיל מחרוזת.

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

שאלה: איזה הסיומת של נתיב כתובת ה-URL של שרת 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>

שאלה: איזה הסיומת של נתיב כתובת ה-URL של שרת 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

וגם

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 שאוסף Apigee כשמשתמשים ב-proxy ל-API, פרוקסי מאפשר גם להגדיר זרימות מותנות שממופות למשאבים בשרת העורפי. במילים אחרות, אם מתקבלת קריאה למשאב GET /reports, Apigee צריך לבצע פעולה כלשהי.

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

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

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

אם כתובת ה-URL של proxy ל-API ממופה לכתובת הבסיסית של שירות לקצה העורפי (כשיוצרים את ה-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 של ה-proxy של ה-API הבא, כשבקשת 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)"