I want to record the timings for the backend web service being fronted by the API Management service. We front a few 3rd party services so the only way we can get performance data is to log the backend request before and after. My plan was to utilise the base policy (All APIs / All Operations) like this
<backend>
<!-- log start of request -->
<forward-request />
<!-- log end of request -->
</backend>
But trying this I get;
Error in element 'backend' on line XX, column XX: backend section
allows only one policy to be specified
Can you have any policy expressions in the backend element?
backend section is very limited in what policies are allowed there. Your best option would be to do your logging last thing in inbound and first thing in outbound. You can control what policy is executed first by moving "base" tag to a desired position, it effectively controls when (and if) upper level policies are executed.
You can use Log to EventHub in this case to log whatever you want for a given request (which has Id you can use as correlation id).
Related
I am having difficulty adding text into my url path between my frontend and backend when using Azure Api Management.
I have a Azure functions api with a url like this:
e.g. https://pXXXXX-myapi.azurewebsites.net/api/pXXXXX/alm/{name}
The default api created by Azure API Management is this:
e.g. https://myapi.azure-api.net/pXXXXX/pXXXXX/alm/{name}
Ideally I'd like to Frontend to remove the redundant pXXXXX:
e.g. https://myapi.azure-api.net/pXXXXX/alm/{name}
If I remove it from the front end e.g. GET /pXXXXX/alm/{name} becomes GET /alm/{name}.
Then I receive a 500 error.
I assume this is because removing "/pXXXXX" removes it from the backend path as well.
So I have tried to use the rewrite-uri policy like so:
<rewrite-uri template="/pXXXXX/alm/{name}" copy-unmatched-params="false" />
I've tried multiple variations of this with no success.
Assuming
you've changed the frontend for your operation to "GET /alm/{name}"
and your backend function app service url is "https://pXXXXX-myapi.azurewebsites.net/api"
and your API Base URL is "https://myapi.azure-api.net/pXXXXX"
then rewrite-uri exactly like you pasted is correct. You want to add leading "pXXXXX" to the url.
I've reproduced exact same scenario on my side and it works.
500 error does not mean there is something wrong with the path. If you would wrongly configure your url you would get 404. Most probably there is an internal error inside your function app or other policies in the APIM. Just call this API with Ocp-Apim-Trace header set to true and subscription key that allows tracing and you will see what is wrong. Or you can paste trace here if you want so we can help.
Posting my own answer but thanks to those who tried to help.
I believe the issue came from an error from APIM itself.
I assume it was not setting up the policies correctly, because now it is automatically including set-backend-service in the inbound policy whereas it did not over the past 2 days when I have been repeatably re-creating my api.
Now my inbound policy looks like this:
<inbound>
<base />
<set-backend-service id="apim-generated-policy" backend-id="pXXXXX-myapi" />
<rewrite-uri template="/pXXXXX/alm/{name}" copy-unmatched-params="false" />
</inbound>
And my front end api looks as before:
/pXXXXX/alm/{name}
I am trying to create a policy in APIM which fetches the product for the subscription-key passed in the header. I need to get that product name and do set-header with that name for the backend service. Is it possible to achieve it through APIM Policies ?
First, we added two more subscription scopes: all APIs and a single API. The scope of subscriptions is no longer limited to an API product. It's now possible to create keys that grant access to an API, or all APIs within an API Management instance, without needing to create a product and add the APIs to it first. Also, each API Management instance now comes with an immutable, all-APIs subscription. This subscription makes it easier and more straightforward to test and debug APIs within the test console.
You can get the Product Name with #(context.Product.Name).
<inbound>
<base />
<set-variable name="aaa" value="#(context.Product.Name)" />
<set-body template="liquid">
{
"success": true,
"var1": {{context.Variables["aaa"]}}
}
</set-body>
</inbound>
In test, set the product name as Starter and you will get the snapshot as below.
I am using Azure API Management as front end for my Logic App. The "subscription required" setting needs to be enabled as we do need the protection. However, we must send the key via query parameter because our calling application only supports GET, not POST.
So my API call was sent to Azure using the format of https://my.azure-api.net/myapi/manual/paths/invoke?subscription-key=mykey
Now in Azure API setting I did create a policy set to delete action on the "subscription-key" query parameter, but here's the problem:
Even though the parameter is removed from the request body into Logic App, upon digging into the "RAW" outputs in Logic App where it shows various headers, we can see the subscription-key in these two headers:
"X-WAWS-Unencoded-URL": "/myapi/manual/paths/invoke?subscription-key=xxx
"X-Original-URL": /myapi/manual/paths/invoke?subscription-key=xxx
In other words, the full original query URL was made available to Logic App before the parameter was removed. This exposes the API subscription key to the Logic App.
Is there any workaround for this?
Ah I see now that those headers were actually sent automatically by Azure API Management to the backend Logic App API, so all I had to do was to set header policies to remove them in addition to the query parameter policy.
<set-query-parameter name="subscription-key" exists-action="delete" />
<set-header name="X-WAWS-Unencoded-URL" exists-action="delete" />
<set-header name="X-Original-URL" exists-action="delete" />
This takes care of it.
Is it possible to do a short polling internally in an APIM request then respond with sync response?
The flow goes like this:
1. APIM receives a request
2. APIM triggers a durable function orchestration.
3. APIM waits and polls the durable function response (through a policy?)
4. APIM responds with the actual result
To the API Consumer, it will be a single request.
Is this a common pattern? Or is it better to just provide the 202 Response polling URL to the API consumer? I'm expecting the request to take only around a few seconds to under a minute.
Is there a more standard way of doing this that I haven't considered yet?
It is subjective to how you want your API to be presented to the consumers.
If you want the front end users to poll the API then 202 Accepted response with a polling url in location header is the way.
If you want to disguise your API as a synchronous API , you will have to implement the polling pattern itself inside the APIM policy. For this refer following post
Use API-M To Mask Async APIs When Moving Implementation to Logic Apps, even though it pertains to logic app, you can implement the same pattern for any async API
excerpt from the link:
<outbound>
<base />
<retry condition="#(((IResponse)context.Variables["var"]).StatusCode == 202)" count="10" interval="30">
<send-request mode="new" response-variable-name="var" ignore-error="false">
<set-url>#(context.Response.Headers["location"][0])</set-url>
<set-method>GET</set-method>
</send-request>
</retry>
<return-response response-variable-name="var" />
</outbound>
As I pointed out earlier, it really depends upon how you want your API to be presented to your consumers. Both patterns are valid.
When setting the rate-limit I apply to All Operations does it sum ALL endpoint calls together or is this rate limit per endpoint? This is crucial info when calculating a baseline for the maximum nr of calls, but I'm afraid I can't find any information in Azure itself about how this works...
Here is the policy I want to add to All Operations:
<rate-limit-by-key />
Then in my endpoint I want to just inherit by adding:
<base />
As it stands the calls under All operations get summed up in a single queue. So the rate limit counter is not for individual endpoints, not even if it is applied directly to an individual endpoint the reason being that the key is not unique to that enpoint.
So a call coming from one IP adress to different endpoints under the same inbound rule for example will update the counter for all enpoints using the same key (in this case the IP address).
To circumvent this I've made my keys unique by chaining IP address and operation name together forming a queue per endpoint and adding <rate-limit-by-key> to my endpoint instead of the <base />
The finished example inbound rule on my endpoint:
<rate-limit-by-key calls="xx" renewal-period="xx" counter-key="#(string.Join("-", context.Request.IpAddress, context.Operation.Id))" />
During request processing policies are executed sequentially. tag on lower level policies defines when upper level policies are executed. rate-limit-by-key works by maintaining one counter per provided key value irrelevant of where it's placed. If this counter goes over limits specified in policy request is blocked.
In other words, two rate-limit-by-key policies placed in different scopes will share counter value, but not limits.