תנאים עם משתני זרימה

אתם צופים במסמכי התיעוד של Apigee Edge.
אפשר לעבור אל מסמכי התיעוד של Apigee X.
מידע

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

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

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

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

<Condition></Condition>

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

<Condition>{variable.name}{operator}{"value"}</Condition>

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

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

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

משתנים

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

המשתנים תמיד משתמשים בסימון נקודות. לדוגמה, כותרות HTTP בהודעת הבקשה זמינות כמשתנים שנקראים request.header.{header_name}. לכן, כדי להעריך את כותרת Content-type, אפשר להשתמש במשתנה 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>

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

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

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

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

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

כדי ליצור 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>

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

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

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

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

דוגמה 2

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

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

הגדרת המדיניות שלמעלה מציינת ל-API proxy לקחת את הודעת התגובה, לבצע המרה מ-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://{org_name}-test.apigee.net/weather/forecastrss?w=12797282

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

$ curl -H "User-Agent:Mozilla" http://{org_name}-test.apigee.net/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://{org_name}-test.apigee.net/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, נחשבת לאפשרות קריאה יותר.

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

.

בדוגמת ה-XML הבאה מוצג תנאי של שלב. המערכת מפעילה את המדיניות SomePolicy אם התנאי מחזיר את הערך true. בדוגמה הזו, אנחנו בודקים את המשתנה proxy.pathsuffix, משתנה מובנה ב-Edge שמאחסן את הסיומת של נתיב הבקשה. עם זאת, אפשר לבדוק את הערך של כל משתנה של זרימת נתונים שמכיל מחרוזת. לכן, במקרה הזה, אם נתיב הבסיס של הבקשה הנכנסת הוא /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://artomatic-test.apigee.net/matchtest/cat

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

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

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/cat

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/bat

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/owl

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

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

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/cat

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/bat

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/cat123

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/cat/bird/mouse

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

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

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

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/cat

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/bAt

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

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

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

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/cat

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/c*at

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

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

JavaRegex

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

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

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

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

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/cat

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

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

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/cat

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/ccccct

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

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

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/cat

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/ct

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/caat

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

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

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/cat

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

GET http://artomatic-test.apigee.net/matchtest/bat

GET http://artomatic-test.apigee.net/matchtest/rat

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

GET http://artomatic-test.apigee.net/matchtest/mat

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

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

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/cat

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/cAt

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

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

MatchesPath

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

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

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

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

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

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/animals

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

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/animals/

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/animals/cats

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/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://artomatic-test.apigee.net/matchtest/animals

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/animals/

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

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/matchtest/animals/cats

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

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

קריאה ל-API:

GET http://artomatic-test.apigee.net/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://artomatic-test.apigee.net/matchtest/animals/cats/wild/

וגם

GET http://artomatic-test.apigee.net/matchtest/animals/dogs/wild/austrailian

וגם

GET http://artomatic-test.apigee.net/matchtest/animals/birds/wild/american/finches

משאבי API

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

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

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

כתובת URL בסיסית של ה-Backend כתובת URL חדשה או שוות ערך של proxy ל-API
http://mygreatweatherforecast.com http://{your_org}-{environment}.apigee.net/mygreatweatherforecast

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

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

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

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

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

נניח שרוצים ש-Edge "יעשה משהו" כשמתקבלות שיחות למשאבי /reports או /forecasts. בשלב הזה אתם לא אומרים ל-Edge מה לעשות, אלא רק שהוא צריך להאזין לקריאות למשאבים האלה. הפעולה הזו מתבצעת באמצעות תנאים. ב-Edge API proxy, אפשר ליצור זרימות מותנות עבור /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,‏ Edge יבצע את הפעולה שתגדירו (מפתח ה-API) באמצעות כללי המדיניות שתצרפו לזרימות האלה.

עכשיו נראה דוגמה להוראה ל-Edge מה לעשות כשמתקיים תנאי מסוים. בדוגמה הבאה של XML של שרת proxy ל-API, כשבקשת GET נשלחת אל https://yourorg-test.apigee.net/mygreatweatherforecast/reports, ‏ Edge מפעיל את המדיניות 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>

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

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

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

תוכלו:

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

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

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

בתצוגת הפיתוח של חלונית הניווט בכלי לעריכת שרתי proxy של API, לוחצים על סמל הפלוס לצד default ב-Proxy Endpoints.

בחלון 'זרימה חדשה עם תנאים', מזינים את הגדרות המפתח הבאות:

  • שם התהליך: מפתחים
  • Condition Type: Path
  • נתיב: /developers

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

עכשיו מוסיפים זרימה מותנית ל-‎ /apps, ומניחים שרוצים שהתנאי יופעל גם ב-URI וגם בפועל POST בבקשה. ההגדרה כוללת את הפעולות הבאות:

  • שם התהליך: אפליקציות
  • Condition Type: Path and Verb
  • נתיב: ‎/apps
  • Verb: POST

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

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

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

<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 שאתם מגדירים מיושם על ידי Flow מותנה ב-API proxy. (ראו הגדרת תהליכי עבודה).

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

http://{org_name}-test.apigee.net/{proxy_path}/apps

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

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

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

מידע נוסף על סוג התנאי הזה זמין במאמר How to match regardless ... בקהילת Apigee.

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

במקרים מסוימים, משאבי ה-API יהיו היררכיים. לדוגמה, Developer Services 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

יבצע התאמת נתונים (resolve) של כתובות ה-URI ההיררכיות האלה כמשאבי API בצורה מתאימה.

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

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

/developers/{developer_email}/apps
/developers/{developer_email}/keys
/developers/{developer_email}/apps/{app_id}/keys

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

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

Conditional Flow

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

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

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

אם אתם מעדיפים, אתם יכולים להשיג את זה בלי להשתמש בביטוי רגולרי (Regex) באופן הבא:

    <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://artomatic-test.apigee.net/matchtest/cat
or

GET http://artomatic-test.apigee.net/matchtest/cat/

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

קריאה ל-API:‏

GET http://artomatic-test.apigee.net/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.

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

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

עם זאת, המבנה הזה יעבוד:

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

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

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

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

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

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

התחביר של Condition מחייב להוסיף מירכאות כפולות לביטוי JavaRegex. לכן, אם יש לכם ביטוי Regex שכולל מירכאות כפולות, אתם צריכים למצוא דרך אחרת להתאים אותן. התשובה היא Unicode. לדוגמה, נניח שמעבירים כותרת שכוללת מרכאות כפולות, כמו בדוגמה הבאה:
 -H 'content-type:multipart/related; type="application/xop+xml"'
אם תנסו להתאים את הכותרת הזו לתנאי Regex, תקבלו שגיאה של תנאי לא תקין כי הביטוי כולל את המירכאות הכפולות:
request.header.Content-Type ~~ "(multipart\/related)(; *type="application\/xop\+xml\")"
הפתרון הוא להחליף את המירכאות הכפולות שמבוססות על ASCII במקבילות שלהן ב-Unicode, ‏ \u0022. לדוגמה, הביטוי הבא תקין ומפיק את התוצאה הצפויה:
request.header.Content-Type ~~ "(multipart\/related)(; *type=\u0022application\/xop\+xml\u0022)"