I wrote some posts about Azure API management earlier last year, but last week I was discussing some things with a colleague of mine and he asked a question neither of us could answer out of our heads:
When you send a request to Azure API Management, what order is maintained to execute the policies?
After some searching neither of us could find it, so here's a blog post to save you the trouble after some testing :-)
Scenario
We want to determine the execution order in which Azure API Management executes policies for a request. We consider the following policies are considered here:
- Operation policy
- API policy
- All APIs policy
- Product policy
Set-up
To test this specific case, I provisioned an Azure API Management instance within the relatively new Consumption tier. Using this tier I can just set this instance up in my MSDN subscription with limited Azure credit and still only get billed when I use it... Downside is that I don't have any developer portal, but for testing this is not needed.
I setup a new API "Test" with a free and easy RequestBin back-end:
I created one operation there called "post" (yes, I sometimes struggle with creativity):
I also setup a new Product called "genericProduct":
Now we just need to come up with some policies...
We know that policies often have a <base /> policy, so we need to take that into account as well.
I decided to setup the following policies:
- Our All APIs policy:
Here we don't have a <base /> policy, so I just add a single new query parameter. - Our Test API 'All Operations' policy:
We add separate query parameters before and after the base policy. - Our Test API operation policy:
We add separate query parameters before and after the base policy. - Our Product policy
We add separate query parameters before and after the base policy.
These are the policies in detail:
All APIS policy
<policies> <inbound> <set-query-parameter name="policy-allapis" exists-action="append"> <value>allapis</value> </set-query-parameter> </inbound> <backend> <forward-request /> </backend> <outbound /> <on-error /> </policies>
Test API - All Operations policy
<policies> <inbound> <set-query-parameter name="policy-allops-before" exists-action="append"> <value>allops-before</value> </set-query-parameter> <base /> <set-query-parameter name="policy-allops-after" exists-action="append"> <value>allops-after</value> </set-query-parameter> </inbound> <backend> <base /> </backend> <outbound> <base /> </outbound> <on-error> <base /> </on-error> </policies>
Test API - post operation policy
<policies> <inbound> <set-query-parameter name="policy-operation-before" exists-action="append"> <value>operation-before</value> </set-query-parameter> <base /> <set-query-parameter name="policy-operation-after" exists-action="append"> <value>operation-after</value> </set-query-parameter> </inbound> <backend> <base /> </backend> <outbound> <base /> </outbound> <on-error> <base /> </on-error> </policies>
Product policy
<policies> <inbound> <set-query-parameter name="policy-product-before" exists-action="append"> <value>product-before</value> </set-query-parameter> <base /> <set-query-parameter name="policy-product-after" exists-action="append"> <value>product-after</value> </set-query-parameter> </inbound> <backend> <base /> </backend> <outbound> <base /> </outbound> <on-error> <base /> </on-error> </policies>
So what do we see now in RequestBin?
Conclusion
We consistently see the following appearing in our traces:
3 source "set-query-parameter" timestamp "2019-05-23T09:25:03.0219982Z" elapsed "00:00:00.1175110" data message "Request URL was updated with the query value." request url "http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before" 4 source "set-query-parameter" timestamp "2019-05-23T09:25:03.0219982Z" elapsed "00:00:00.1175248" data message "Request URL was updated with the query value." request url "http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before&policy-allops-before=allops-before" 5 source "set-query-parameter" timestamp "2019-05-23T09:25:03.0219982Z" elapsed "00:00:00.1175298" data message "Request URL was updated with the query value." request url "http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before&policy-allops-before=allops-before&policy-product-before=product-before" 6 source "set-query-parameter" timestamp "2019-05-23T09:25:03.0219982Z" elapsed "00:00:00.1175350" data message "Request URL was updated with the query value." request url "http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before&policy-allops-before=allops-before&policy-product-before=product-before&policy-allapis=allapis" 7 source "set-query-parameter" timestamp "2019-05-23T09:25:03.0219982Z" elapsed "00:00:00.1175417" data message "Request URL was updated with the query value." request url "http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before&policy-allops-before=allops-before&policy-product-before=product-before&policy-allapis=allapis&policy-product-after=product-after" 8 source "set-query-parameter" timestamp "2019-05-23T09:25:03.0219982Z" elapsed "00:00:00.1175458" data message "Request URL was updated with the query value." request url "http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before&policy-allops-before=allops-before&policy-product-before=product-before&policy-allapis=allapis&policy-product-after=product-after&policy-allops-after=allops-after" 9 source "set-query-parameter" timestamp "2019-05-23T09:25:03.0219982Z" elapsed "00:00:00.1175504" data message "Request URL was updated with the query value." request url "http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before&policy-allops-before=allops-before&policy-product-before=product-before&policy-allapis=allapis&policy-product-after=product-after&policy-allops-after=allops-after&policy-operation-after=operation-after"
I tried several dozens of requests and different requestbins, the order always stays the same.
So, it seems we can conclude this is the order in which APIM executes policies:
- Operation policy before <base />
- All Operations policy before <base />
- Product policy before <base />
- All APIs policy
- Product policy after <base />
- All Operations policy after <base />
- Operation policy after <base />
As usual, finding the solution proved to be much quicker than writing the blog post :-)
Please place any feedback in the comments below. I hope you have a great day!
Cheers, Pieter
Noteworthy: initially I was looking at RequestBin itself to see the order of the query parameters. However, this doesn't match with the query parameters I saw in the APIM traces. They are in a different order or even alphabetically. Don't depend on it too much!