This page applies to Apigee and Apigee hybrid.
View Apigee Edge documentation.
Conditional statements are a common control structure in all programming languages. Like a programming language, API proxy configuration supports conditional statements for Flows, Policies, Steps, and RouteRules. By defining conditional statements, you define dynamic behavior for your API. This dynamic behavior lets you do things like converting XML into JSON only for mobile devices, or routing to a backend URL based on the content type or HTTP verb of the request message.
This topic shows you how to use conditions to dynamically apply API management features at runtime, without writing any code.
Configure conditional statements
Conditional behavior is implemented in API proxies by using a combination of conditions and variables. A conditional statement is created using a Condition element. The following is an empty condition:
<Condition></Condition>
To create a conditional statement, add a conditional operator and a variable using the following syntax:
<Condition>VARIABLE_NAME OPERATOR "VALUE"</Condition>
For example:
<Condition>request.verb = "GET"</Condition>
Supported conditional operators include =
(equals), !=
(not equal),
and >
(greater than). For readability, you can also write the conditionals as
text: equals
, notequals
, greaterthan
.
When working with URI paths you can use ~/
or MatchesPath
. You can
also match JavaRegex regular expressions with the ~~
operator.
Conditions are used to define API proxy conditional flows to backend API resources, described in Create conditional flows to backend API resources. For a complete list of conditionals, see Conditions reference.
Variables
Conditions do their work by evaluating the values of variables. A variable is a property of an HTTP transaction executed by an API proxy, or a property of an API proxy configuration itself. Whenever an API proxy gets a request from an app, Apigee populates a long list of variables that are associated with things like system time, the app's network information, HTTP headers on messages, the API proxy configuration, policy executions and so on. This creates a rich context that you can use to set up conditional statements.
Variables always use a dotted notation. For example, HTTP headers on the request message are
available as variables called request.header.HEADER_NAME
. So to evaluate the
Content-type header
, you could use the variable request.header.Content-type
. For
example request.header.Content-type = "application/json"
indicates that the content
type of the request should be JSON.
Imagine that you need to create a conditional statement that will cause a policy to be
enforced only when a request message is a GET
. To create a condition that evaluates the HTTP verb
of a request, you create the conditional statement below. The variable in this condition is
request.verb
. The value of the variable is GET
. The operator is
=
.
<Condition>request.verb = "GET"</Condition>
You could also use:
<Condition>request.verb equals "GET"</Condition>
Apigee uses such a statement to evaluate conditions. The example above evaluates to true if the
HTTP verb associated with the request is GET
. If the HTTP verb associated with the request is
POST
, then the statement evaluates to false.
To enable dynamic behavior, you can attach Conditions to Flows, Steps, and RouteRules.
When you attach a condition to a Flow, you create a conditional Flow. Conditional flows execute only when the condition evaluates to true. You can attach as many Policies as you want to a conditional Flow. A conditional Flow enables you to craft highly specialized processing rules for request or response messages that meet certain criteria.
For example, to create a Flow that executes only when the request verb is a GET
:
<Flows> <Flow name="ExecuteForGETs"> <Condition>request.verb="GET"</Condition> </Flow> </Flows>
To create one Flow for GET
requests and another for POST
requests:
<Flows> <Flow name="ExecuteForGETs"> <Condition>request.verb="GET"</Condition> </Flow> <Flow name="ExecuteForPOSTs"> <Condition>request.verb="POST"</Condition> </Flow> </Flows>
As shown in the example below, you can apply the condition to the Policy Step itself. The
following Condition causes the VerifyAPIKey policy to be enforced only if a request message is a
POST
.
<PreFlow name="PreFlow"> <Request> <Step> <Condition>request.verb equals "POST"</Condition> <Name>VerifyApiKey</Name> </Step> </Request> </PreFlow>
Once you have defined such conditional Flows, you can attach Policies to them, enabling an API
proxy to enforce one set of policies for GET
requests, and another set of policies for POST
requests.
For comprehensive reference information, see the following resources:
Example 1
The following example shows a single conditional flow named Convert-for-devices
,
configured in the ProxyEndpoint response flow. Add the Condition as an element to the entity to
which the condition applies. In this example, the condition is a component of the flow.
Therefore, the flow will execute whenever the statement evaluates to true
.
<Flows> <Flow name="Convert-for-devices"> <Condition>(request.header.User-Agent = "Mozilla")</Condition> <Response> <Step><Name>ConvertToJSON</Name></Step> </Response> </Flow> </Flows>
For each request received from an app, Apigee stores the values of all HTTP headers
present as
variables. If the request contains an HTTP header called User-Agent
, that header and
its value are stored as a variable called request.header.User-Agent
.
Given the ProxyEndpoint configuration above, Apigee checks the value of the
request.header.User-Agent
variable to see whether the condition evaluates to
true.
If the condition does evaluate to true
, that is, the value of the variable
request.header.User-Agent
equals Mozilla
, then the conditional Flow
executes and the XMLtoJSON policy called ConvertToJSON
is enforced. If not, the Flow
is not executed, and the XML response is returned unmodified (in XML format) to the requesting
app.
Example 2
Let's use a specific example in which you need to transform response message from XML to JSON—but only for mobile devices. First, create the policy that will convert the XML-formatted response from the Weather API into JSON:
<XMLToJSON name="ConvertToJSON"> <Options> </Options> <OutputVariable>response</OutputVariable> <Source>response</Source> </XMLToJSON>
The policy configuration above tells the API proxy to take the response message, perform a
conversion from XML to JSON with default settings, and then write the result to the new response
message. (If you are converting a request message from XML to JSON, you simply set both of
these values to request
.)
Since you want to convert responses from XML to JSON, you need to configure a conditional response Flow to perform the conversion. For example, to convert all responses from XML to JSON before they are returned to the client app, configure the following ProxyEndpoint response Flow.
<Flows> <Flow name="Convert-for-devices"> <Response> <Step><Name>ConvertToJSON</Name></Step> </Response> </Flow> </Flows>
When you invoke the API using the standard request, the response is formatted in JSON.
However, your goal is to only convert Weather reports into JSON when the requesting client is a mobile device. To enable such dynamic behavior, you must add a conditional statement to the Flow.
Test the conditional flow
In this sample request, the HTTP User-Agent
header is set to
Mozilla
, causing the conditional statement to evaluate to true and the conditional
flow Convert-for-devices
to execute.
curl -H "User-Agent:Mozilla" http://example.com/weather/forecastrss?w=12797282
or, to pretty print where Python is available:
curl -H "User-Agent:Mozilla" http://example.com/weather/forecastrss?w=12797282 | python -mjson.tool
Sample Response:
. . . "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" } ] } . . .
A request submitted without the User-Agent
header, or with a different value than
Mozilla
, will result in an XML-formatted response.
$ curl http://example.com/weather/forecastrss?w=12797282
The unmodified XML response is returned.
Sample Response:
<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" />
Pattern matching
This section describes how to use pattern matching with conditions in an Apigee flow.
Operators
This section describes how to use the following pattern matching operators in conditional statements:
Matches
operator: Simple pattern matching- JavaRegex operator: Finer control over matching
MatchesPath
operator: Path fragment matching
Matches
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 possibilities. 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 Apigee 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://example.com/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://example.com/matchtest/cat
Does the policy execute? Yes, because the wildcard matches any character, and
"/cat
" is a match.
API Call:
GET http://example.com/matchtest/bat
Does the policy execute? Yes, because the wildcard matches any character, "/bat"
is a match.
API Call:
GET http://example.com/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://example.com/matchtest/cat
Does the policy execute? Yes, because the wildcard matches zero or more of any characters.
API Call:
GET http://example.com/matchtest/bat
Does the policy execute? No, /bat
is not a match.
API call:
GET http://example.com/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://example.com/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!
Question: Is the Matches
operator case sensitive?
Yes. Assume you have a condition like this:
<Condition>(proxy.pathsuffix Matches "/*At")</Condition>
API call:
GET http://example.com/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://example.com/matchtest/bAt
Does the policy execute? Yes, the case matches.
Question: 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://example.com/matchtest/cat
Does the policy execute? No, the Matches
operator is looking for the literal string
c*at
.
API call:
GET http://example.com/matchtest/c*at
Question:Does the policy execute?
Yes, this path, although a bit unusual, matches.
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 Apigee 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!
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
evaluates to true
. In this example, we test the variable proxy.pathsuffix
, a built-in
variable in Apigee that stores the path suffix of the request. 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://example.com/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 preceding characters.
<Condition>(proxy.pathsuffix JavaRegex "/c*t")</Condition>
API call:
GET http://example.com/matchtest/cat
Does the policy execute? No! The *
quantifier matches zero or more of the
preceding character, which is a c
.
API Call:
GET http://example.com/matchtest/ccccct
Does the policy execute? Yes, because the wildcard matches zero or more of the preceding character.
Next, we use the ?
quantifier, which matches the preceding character once, or not
at all.
<Condition>(proxy.pathsuffix JavaRegex "/ca?t")</Condition>
API call:
GET http://example.com/matchtest/cat
Does the policy execute? Yes. The ?
quantifier matches zero or one occurrence of
the preceding character, which is an a
.
API Call:
GET http://example.com/matchtest/ct
Does the policy execute? Yes. The ?
quantifier matches one or
none of the preceding character. In this case, there is no a
character, so the
condition evaluates to true
.
API Call:
GET http://example.com/matchtest/caat
Does the policy execute? No. The ?
quantifier matches one of the preceding
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://example.com/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://example.com/matchtest/bat
GET http://example.com/matchtest/rat
But this is not a match:
GET http://example.com/matchtest/mat
Question: Is the JavaRegex operator case sensitive?
Yes. Assume you have a condition like this:
<Condition>(proxy.pathsuffix JavaRegex "/ca?t")</Condition>
API call:
GET http://example.com/matchtest/cat
Does the policy execute? Yes, the regex matches zero or one of the preceding character, which
is a
.
API call:
GET http://example.com/matchtest/cAt
Question: Does the policy execute?
No, because the capital A
does not match lowercase a
.
MatchesPath
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 Apigee 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://example.com/matchtest/animals
Question: Does the policy execute?
No, because condition requires another path element after
/animals
, as specified by /*
.
API call:
GET http://example.com/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://example.com/matchtest/animals/cats
Does the policy execute? Yes, because the path clearly has an element (/cats
)
that comes after /animals
API Call:
GET http://example.com/matchtest/animals/cats/wild
Question: 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://example.com/matchtest/animals
Does the policy execute? No, because condition requires at least one following path element
specified by /**
.
API call:
GET http://example.com/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://example.com/matchtest/animals/cats
Does the policy execute?
Yes, because the path has at least one element that comes after
/animals
API Call:
GET http://example.com/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://example.com/matchtest/animals/cats/wild/
and
GET http://example.com/matchtest/animals/dogs/wild/austrailian
and
GET
http://example.com/matchtest/animals/birds/wild/american/finches
API resources
RESTful services are collections of API resources. An API resource is a URI path fragment that identifies some entity that developers can access by calling your API. For example, if your service provides weather reports and weather forecasts, your backend service might define two API resources:
http://mygreatweatherforecast.com/reports
http://mygreatweatherforecast.com/forecasts
When you create an API proxy (as shown in the Build your first API proxy), at a minimum you're creating an alias base URL that maps to your backend service. For example:
Backend base URL | New/equivalent API proxy URL |
---|---|
http://mygreatweatherforecast.com |
http://example.com/mygreatweatherforecast |
At this point you can make API calls to your backend using either base URL. But when you use the API proxy URL, things start to get interesting.
In addition to API analytics that Apigee starts to collect when you use the API proxy, proxies
also let you define conditional flows that map to the resources on your backend. In essence, If
a GET
call comes in to the /reports
resource, Apigee should do something.
The following image shows the behavior difference between two URLs that ultimately access the same backend. One is the un-proxied resource URL, the other is an Apigee API proxy with a conditional flow to the same backend resource. We'll describe conditional flows in more detail below.
How API proxies map to specific backend resources
With an API proxy URL mapped to the base URL of the backend service (when you create the
proxy), you can add conditional flows to specific resources, such as the /reports
and /forecasts
resources mentioned earlier.
Let's say you wanted to have Apigee do something when calls come in to the
/reports
or /forecasts
resources. At this point you're not telling Apigee
what to do, just that it should be listening for calls to those resources. You do this
with conditions. In your Apigee API proxy, you can create conditional flows for
/reports
and /forecasts
. For conceptual purposes, the following API
proxy XML shows what those conditions might look like.
<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>
Those conditions say, When a GET
request comes in with /reports
and
/forecasts
in the URL, Apigee will do whatever you (the API developer) tell it to,
through the policies you attach to those flows.
Now here's an example of telling Apigee what to do when a condition is met. In the following API
proxy XML, when a GET
request is sent to
https://example.com/mygreatweatherforecast/reports
, Apigee executes
the XML-to-JSON-1
policy in the response.
<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>
In addition to those optional conditional flows, each API proxy also comes with two default
flows: a <PreFlow>
executed before your conditional flows, and a
<PostFlow>
executed after your conditional flows. Those are useful for
executing policies when any call is made to an API proxy. For example, if you want to
verify an app's API key with every call, regardless of the backend resource being accessed, you
could put a Verify API Key policy on the <PreFlow>
. For more on flows, see
Configuring flows.
Create conditional flows to backend resources
Defining conditional flows to backend resources in an API proxy is completely optional. However, those conditional flows give you the ability to apply fine-grained management and monitoring.
You will be able to:
- Apply management in way that reflects the semantics of your API model
- Apply policies and scripted behavior to individual resource paths (URIs)
- Collect fine-grained metrics for Analytics Services
For example, imagine that you need to apply different types of logic to your backend
/developers
to /apps
resources.
To do so, you add two conditional flows in your API proxy: /developers
and
/apps
.
New Proxy Editor
To add a conditional flow:
- Select the Develop tab in the Proxy Editor.
- Select Proxy endpoints > default in the left-hand pane.
- Click the + button above the Response pane.
- In the Add Conditional Flow dialog, enter the following configurations:
- Flow name:
Developers
- Condition Type:
Path
- Path:
/developers
The condition will be triggered (and policies will be executed) if a call is sent to the proxy with
/developers
at the end of the URI. - Flow name:
- Now add a conditional flow for
/apps
, and assume you want the condition to be triggered on both the URI and thePOST
verb in a request. The configuration involves setting the following:- Flow Name:
Apps
- Condition Type:
Path and Verb
- Path:
/apps
- Verb:
POST
The condition will be triggered (and policies will be executed) if a call is sent to the proxy with
/apps
at the end of the URI and aPOST
verb. - Flow Name:
The added flows are displayed in the Response pane:
Classic Proxy Editor
In the Develop pane of the API proxy editor Navigator pane, click next to default in the Proxy Endpoints.
In the New Conditional Flow window, enter the following key configurations:
- Flow name:
Developers
- Condition Type:
Path
- Path:
/developers
The condition will be triggered (and policies will be executed) if a call is sent to the proxy
with /developers
at the end of the URI.
Now add a conditional flow for /apps
, and assume you want the condition to be triggered on
both the URI and the POST
verb in a request. The configuration involves setting the
following:
- Flow Name:
Apps
- Condition Type:
Path and Verb
- Path:
/apps
- Verb:
POST
The condition will be triggered (and policies will be executed) if a call is sent to the proxy
with /apps
at the end of the URI and a POST
verb.
In the Navigator pane, you'll see new flows for Apps and Developers.
Select one of the flows to view the conditional flow configuration in the API proxy editor code view:
<Flow name="Apps"> <Description>Developer apps registered in Developer Services</Description> <Request/> <Response/> <Condition>(proxy.pathsuffix MatchesPath "/apps") and (request.verb = "POST")</Condition> </Flow>
As you can see, API resources are simply conditional Flows that evaluate the URI path of the
inbound request. (The proxy.pathsuffix
variable identifies the URI of the request that follows
the BasePath configured in the ProxyEndpoint configuration.)
Each API resource that you define is implemented by a conditional flow in the API proxy. (See Configuring flows.)
Once you deploy the API proxy to the test environment, the following request:
http://example.com/PROXY_PATH/apps
will cause the condition to evaluate to true
, and this flow, along with any associated
policies, will execute.
The following example condition uses a Java regular expression to recognize calls made to the
/apps
resource with or without a trailing forward slash (/apps
or
/apps/**
):
<Condition>(proxy.pathsuffix JavaRegex "/apps(/?)") and (request.verb = "POST")</Condition>
For more about this type of condition, see How to match regardless whether there is a trailing "/" ... in the Apigee Community.
Modeling hierarchical URIs
In some cases, you will have hierarchical API resources. For example, the Developer apps list API provides a method for listing all apps that belong to a developer. The URI path is:
/developers/DEVELOPER_EMAIL/apps
You may have resources where a unique ID is generated for each entity in a collection, which is sometimes annotated as follows:
/genus/:id/species
This path applies equally to the following two URIs:
/genus/18904/species /genus/17908/species
To represent this structure in an API resource, you can use wildcards. For example:
/developers/*/apps /developers/*example.com/apps /genus/*/species
These will resolve the hierarchical URIs as API resources appropriately.
In some cases, especially for deeply hierarchical APIs, you may simply want to resolve everything below a certain URI fragment. To do so, use a double asterisk wildcard in your resource definition. For example, if you define the following API resource:
/developers/**
That API resource will resolve the following URI paths:
/developers/DEVELOPER_EMAIL/apps /developers/DEVELOPER_EMAIL/keys /developers/DEVELOPER_EMAIL/apps/APP_ID/keys
Here's what the conditional flow condition would look like in the API proxy definition:
<Condition>(proxy.pathsuffix MatchesPath "/developers/**") and (request.verb = "POST")</Condition>
More examples
Condition attached to 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>
Condition attached to a policy
<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>
Sample operators in conditions
Here are some examples of operators used to create conditions:
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
A practical example: Ignore
/
at the end of a path
Apigee 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 in
how to match regardless whether there is a trailing '/' on the url....
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://example.com/matchtest/cat
or GET http://example.com/matchtest/cat
/
Does the policy execute? Yes. Note that in a regular expression, the ?
character means: match zero or one of the preceding character. Therefore, both
/cat
and /cat/
are matches.
API Call:
GET http://example.com/matchtest/cat/spotted
Does the policy execute? No. The regular expression matches zero or only one occurrence of the preceding 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.
Escaping double quotes in JavaRegex expressions
The Condition syntax requires a JavaRegex expression to be wrapped in double quotes; therefore, if you have a regex expression that includes double quotes, you need an alternate way to match them. The answer is Unicode. For example, let's say you pass in a header that includes double quotes, like the following:
-H 'content-type:multipart/related; type="application/xop+xml"'
If you try to match that header in a regex condition, you will get an Invalid Condition
error because the expression
includes the double quotes:
request.header.Content-Type ~~ "(multipart\/related)(; *type="application\/xop\+xml\")"
The solution is to replace the ASCII-based double quotes with their Unicode equivalent, \u0022
. For example,
the following expression is valid and produces the expected result:
request.header.Content-Type ~~ "(multipart\/related)(; *type=\u0022application\/xop\+xml\u0022)"