Send Docs Feedback

Note: Most user interface tasks can be performed in Edge Classic or the New Edge experience. For an overview, getting started topics, and release notes specific to the New Edge experience, see the docs.

Pattern matching in conditional statements

This topic explains how to use these pattern matching operators in conditional statements in Apigee Edge:

  • Matches operator
  • JavaRegex operator
  • MatchesPath operator

Conditional statements are used in conditional flows, routes, and steps. You can read more about conditional statements in the Conditions reference.

Experiment

You can experiment with the conditions described in this topic, and make up and test your own conditions, using the condition-pattern-matching proxy in our api-platform-samples repository in GitHub. See the README for details. 

Simple matching with the Matches operator

Let's look at the "Matches" or "~" conditional operator first. These two operators are the same -- the English version, "Matches", is considered to be a more readable option.

Summary: The "Matches" operator gives you two possiblities. Either match the string literally, or do a wildcard match with "*". As you might expect, the wildcard matches zero or more characters. Let's see how this works.

The following XML shows a Step condition. It executes the SomePolicy policy when the condition evaluates to true. In this example, we test the variable proxy.pathsuffix, a built-in variable in Edge that stores the path suffix of the request. Note, however, you can test the value of any flow variable that contains a string. So, in this case, if the base path of the incoming request is /animals, and the request is /animals/cat, then the path suffix is the literal string "/cat".

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

Question: What proxy path suffix will cause SomePolicy to execute? There's only one possibility.

API call:

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

Does the policy execute? Yes, because the proxy path suffix matches "/cat" exactly. It will not execute if the suffix is /bat or /dog or "/" or anything else.

Now, consider this conditional statement where we use the wildcard character "*":

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

API call:

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

Does the policy execute? Yes, because the wildcard matches any character, and "/cat" is a match.

API Call:

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

Does the policy execute? Yes, because the wildcard matches any character, "/bat" is a match.

API Call:

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

Does the policy execute? Certainly not -- although the wildcard matches the "o", the letters "wl" are not matched.

Now, lets move the wildcard to the end of the suffix:

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

API call:

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

Does the policy execute? Yes, because the wildcard matches zero or more of any characters.

API Call:

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

Does the policy execute? No, "/bat" is not a match.

API call:

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

Does the policy execute? Yes, the wildcard matches zero or more of any characters, therefore "123" produces a match.

API call:

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

Does the policy execute? Yes, because the wildcard matches zero or more of any characters, so "/bird/mouse" produces a match. Note how an expression like this can get you into trouble because it matches everything after the literal characters!

Is the Matches operator case sensitive?

Yes. Assume you have a condition like this:

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

API call:

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

Does the policy execute? No, the wildcard matches any letter (regardless of case), but the lowercase "a" does not match "A". 

API call:

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

Does the policy execute? Yes, the case matches. 

How do I escape characters with the Matches operator?

Use the percent "%" character to escape reserved characters. For example:

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

API call:

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

Does the policy execute? No, the Matches operator is looking for the literal string "c*at". 

API call:

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

Does the policy execute? Yes, this path, although a bit unusual, matches.

 

Finer control with JavaRegex

As you can see, the "Matches" operator is great for simple situations. But you can use another operator, the "JavaRegex" or "~~" operator. These two are the same operator, except JavaRegex is considered to be more readable. It's called JavaRegex because it allows regular expression pattern matching, and Edge follows the same rules as the classes in the java.util.regex package in the Java language. The way the JavaRegex operator works is very different from the Matches operator, so it's important not to confuse the two!

If you're used to doing Regex in Perl scripts, it's important to note that the JavaRegex operator in Edge tests the entire subject string. For example, if the subject is "abc" and the regex is "[a-z]" , there is no match because "[a-z]" matches exactly ONE alpha character. The expression "[a-z]+" would work, as would "[a-z]{3}". This matching differs from the usual application of regular expressions in tools like Perl or grep or Apache's mod_rewrite. JavaRegex uses java.util.regex.Pattern.matches(str), which is defined to return positive if the entire string (str) matches the pattern. The regex in Perl or mod_rewrite are applied to "any" part of the string. They return true if the pattern matches anywhere. See the example Matching arbitrary strings with JavaRegex.

This section is not intended to be a detailed introduction to regular expressions. The main intent is to illustrate a few simple examples, and highlight the difference between the Matches and JavaRegex operators. If you'd like to know more about regular expressions, many resources exist for you to explore. 

Summary: The "JavaRegex" operator lets you use regular expression syntax in conditional statements.

The following code shows a Step condition. It executes the SomePolicy policy if the condition evalutes to true. In this example, we test the variable proxy.pathsuffix, a built-in variable in Edge that stores the path suffix of the request. Note, however, you can test the value of any flow variable that contains a string. So, in this case, if the base path of the incoming request is /animals, and the request is /animals/cat, then the path suffix is the literal string "/cat".

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

Question: What proxy path suffix will cause SomePolicy to execute? Just like with the Matches operator, there's only one possibility in this case.

API call:

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

Does the policy execute? Yes, because the proxy path suffix matches "/cat" exactly. It will not execute if the suffix is /bat or /dog or anything else.

Now, let's create a regular expression using "*" quantifier. This quantifier matches zero or more of the preceeding character.

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

API call:

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

Does the policy execute? No! The "*" quantifier matches zero or more of the preceeding character, which is a "c".

API Call:

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

Does the policy execute? Yes, because the wildcard matches zero or more of the preceeding character.


Next, we use the "?" quantifier, which matches the preceeding character once, or not at all.

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

API call:

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

Does the policy execute? Yes. The "?" quantifier matches zero or one occurance of the preceeding character, which is an "a".

API Call:

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

Does the policy execute? Yes. The "?" quantifier matches one or none of the preceeding character. In this case, there is no "a" character, so the condition evaluates to true.

API Call:

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

Does the policy execute? No. The "?" quantifier matches one of the preceeding character, which is an "a".

 

Next, we use the "[abc]" or "grouping" style of regex expression. It matches the characters "a" or "b" or "c".

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

API call:

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

Does the policy execute? Yes. We're using regular expressions here, and the "[cbr]" expression matches a "c", "b", OR "r". These calls are also matches:

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

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

But this is not a match:

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

 

Is the JavaRegex operator case sensitive?

Yes. Assume you have a condition like this:

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

API call:

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

Does the policy execute? Yes, the regex matches zero or one of the preceeding character, which is "a".  

API call:

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

Does the policy execute? No, because the capital "A" does not match lowercase "a".

Matching path fragments with the MatchesPath operator

The MatchesPath operator can also be specified like this "~/". It looks a little bit like the Matches (~) and the JavaRegex (~~) operators. But MatchesPath is entirely different.

Just remember that this operator looks at a path as a series of parts. Therefore, if the path is: /animals/cats/wild, you can think of the path as consisting of the parts "/animals", "/cats", and "/wild".

The MatchesPath operator lets you use two wildcard notations: a single asterisk (*) and a double asterisk (**). The single asterisk matches one path element. The double asterisk matches one or many path elements.

Let's look at an example. In this example, we test the variable proxy.pathsuffix, a built-in variable in Edge that stores the path suffix of the request. Note, however, you can test the value of any flow variable that contains a string.

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

Question: What proxy path suffix will cause SomePolicy to execute?

API call:

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

Does the policy execute? No, because condition requires another path element after "/animals", as specified by "/*".

API call:

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

Does the policy execute? Yes, the path does have another path element (the part after "/animals/"), but it's just empty.

API Call:

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

Does the policy execute? Yes, because the path clearly has an element ("/cats") that comes after "/animals"

API Call:

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

Does the policy execute? No, because the single asterisk only matches one path element, and this API has more than one element after "/animals".

Now let's use the double asterisk:

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

Question: What proxy path suffix will cause SomePolicy to execute?

API call:

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

Does the policy execute? No, because condition requires at least one following path element specified by "/**".

API call:

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

Does the policy execute? Yes, the path does have another path element (the part after "/animals/"), but it's just empty.

API Call:

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

Does the policy execute? Yes, because the path has at least one element that comes after "/animals"

API Call:

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

Does the policy execute? Yes, because the path has more than one element that comes after "/animals"

Mixing asterisks

You can use combinations of the single (*) and double (**) asterisk to further refine your path matching.

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

API call:

All of these API calls will produce a match:

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

and

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

and

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

Examples

A practical example: Ignore "/" at the end of a path

Edge developers commonly want to handle both of these path suffixes: "/cat" and "/cat/". This is because some users or clients might call your API with the extra slash on the end of the path, and you need to be able to handle that in your conditional statements. This exact use case has been discussed on the Apigee Community.

If you prefer, you can achieve this without using Regex like this:

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

This is a good option. It is clear and readable.

You can do the same thing with Regex, however, like this. The parentheses are used to group the regex part of the statement, but they are not required.

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

API Calls:

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

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

Does the policy execute? Yes. Note that in a regular expression, the "?" character means: match zero or one of the preceeding character. Therefore, both "/cat" and "/cat/" are matches.

API Call:

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

Does the policy execute? No. The regular expression matches zero or only one occurance of the preceeding character, and nothing else is allowed.

Matching arbitrary strings with JavaRegex

In all of the examples in this topic, we show how to match one of the built-in flow variables: proxy.pathsuffix. It's good to know that you can do pattern matching on any arbitrary string or flow variable, whether or not it's a built-in flow variable like proxy.pathsuffix. 

If for example you have a condition that tests an arbitrary string, perhaps a string returned in a backend payload, or a string returned from an authentication server lookup, you can use matching operators to test it. If you use JavaRegex, the regular expression will be compared against the entire subject string. If the subject is "abc" and the regular expression is "[a-z]", there is no match, because "[a-z]" matches exactly one alpha character. The expression "[a-z]+" works, as does "[a-z]*", and "[a-z]{3}.

Let's look at a concrete example. Suppose the authentication server returns a list of roles as a comma-delimted string: "editor, author, guest".

To test the presence of the editor role, this construction will not work, because "editor" is only part of the entire string.

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

However, this construction will work:

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

It works because it takes into account word breaks and any other parts of the string with the .* prefix and suffix. 

In this example, you can also test for "editor" with the Matches operator:

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

However, in cases where you need more precision, JavaRegex is often a better choice. 

 

 

Help or comments?