Azure API Management caching policy - azure

In my Azure API Managemenent I'm defining a header based caching policy at API level.
The policy is quite simple:
<policies>
<inbound>
<check-header name="token" failed-check-httpcode="400" failed-check-error-message="Token header is missing" ignore-case="true" />
<cache-lookup vary-by-developer="false" vary-by-developer-groups="false" downstream-caching-type="none">
<vary-by-header>token</vary-by-header>
</cache-lookup>
<base />
</inbound>
<backend>
<forward-request />
</backend>
<outbound>
<cache-store duration="3600" />
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
This works ok in the case my downstream returns a 200 with a body - next request with same header token will hit the cache and the response will be returned from the API Management cache.
However is a error code is returned by the downstream (eg: 401 Unauthorized) that response is not cached by the API Management (confirmed by the tracing I've enabled on the API Management).
I was under the impression that whole responses are cached, but this doesn't seem to be the case...
Can somebody let me know if it's possible to cache responses also in case of unsuccessful http codes and if yes point me to some doc - I've been googling all day yesterday, but was unable to find more.
Thanks in advance!

This is by design. If you look into traces you should see message there "Backend service responed with the status code of 401 rather than 200 OK. Cache Store policy was not applied." The reasoning is that at APIM level we assume that non 200 responses are more transient than 200.
Say that client gets 401, goes on to do whatever to make sure that token is allowed to do the operation and retries the call. And still gets 401 from cache until cache expires.
That certainly may be added as an extra configuration option on a policy: http://aka.ms/apimwish
You could workaround that by using cache-lookup-value and cache-store-value. I.e. in the outbound section if you get 401 store some value in cache using cache-store-value to keyed with token. And in the inbound before cache-lookup do cache-lookup-value with token and see if you get value stored earlier. If you do you can generate 401 response right in place.

Related

Why don't I see any results when I run my test on Azure API Management

I signed up for an API that retrieves a zip file and downloads it. For this, I received a bearer token.
I have created an apim instance and was looking to test the API I had subscribed to which downloads the file.
When I tested it, the apim instance kept loading and got stuck.
I tested this on Postman and it works perfectly. I got the "200 OK response", but still got weird characters like "�vt�" in my body, but that's probably because I'm retrieving a zip file. But why doesn't it work also within Azure's apim? Are there any extra policies I need to add?
Kind regards
You got the "200 OK response", but still facing the issue, you can try to troubleshoot by following approach:
1. If Backend section is taking most of the time (approx. 5 seconds), which means there is some slowness or long running operation is taking place at the backend.
Once you have isolated that the slowness is at the backend, you need to investigate the backend application code of the Web API application. For scenarios where you don't have access to the backend, you can implement caching at APIM level like below. Read about how you can implement caching policies to improve performance in Azure API Management.
<?xml version="1.0" encoding="UTF-8"?>
<policies>
<inbound>
<base />
<cache-lookup vary-by-developer="true" vary-by-developer-groups="true" must-revalidate="true" downstream-caching-type="public" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<cache-store duration="60" />
</outbound>
<on-error>
<base />
</on-error>
</policies>
You can refer to REST API service when called from azure APIM returning empty response body with Status code 200,
Azure API Management returning 200 status without response body, gzip Compression, and Azure API management gives 200 response code,

How to setup 405 Method Not Allowed for each of the methods in APIs using azure API Management

In azure API Management how to setup the 405 (Method not allowed) policy. I am using azure API management APIs and adding different policies like jwt validation, IP filtering, rate limit and all. But I couldn't find a way for add the 405 method not allowed in APIM. I want to setup this for each of the methods. That means I want to block the incoming unrecognized method requests from APIM. (eg: Get instead of POST (Throws 405 method not allowed from APIM). Currently APIM passes the wrong method to backend and it returns the 404 from the application. Anyone know how we can block the wrong request from APIM side and returns 405 instead of passing it to backend and returns 404?.
You could use a Control Flow policy along with the Context Variable on the Inbound policy of each Method to intercept any requests that don't match the defined http method and then use a Set Status policy to return a 405. So for a GET method something along the lines of:
<policies>
<inbound>
<choose>
<when condition="#(context.Request.Method.ToString() != "GET")">
<return-response>
<set-status code="405" reason="No Content" />
</return-response>
</when>
</choose>
<base />
</inbound>
... rest of policies
</policies>
If you've got multiple methods with the same path you might need to apply this at the API level rather than the Method level and make the condition equals methods not in use rather than not equal to method in use
To set this at the API level and check against a collection of methods not in use create a policy along the lines of:
<policies>
<inbound>
<choose>
<when condition="#{
ICollection<string> disallowedMethods = new List<string>() { "POST", "PUT" };
return disallowedMethods.Contains(context.Request.Method.ToString());
}">
<return-response>
<set-status code="405" reason="No Content" />
</return-response>
</when>
</choose>
<base />
</inbound>
... rest of policies
</policies>
The http methods not in use in this example are POST and PUT but you can change the list to whatever applies in your use case.

Use Azure API Management for pass-through but returning html showing redirecting

I try to modify the backend of our api management from resource to a webapp api.
I have taken Use Azure Api Management as a passthrough as a reference.
the exposed api is:
https://service-api-management.azure-api.net/api-name/operation-name
the actual api I want to call is:
https://service-name.azurewebsites.net/api/Skills/Async/operation-name
apart from the uri, the request body and header should remain the same. So I have the below policy set up to change the uri. But the response is a html showing redirect.
Curiously about why it returns a html showing redirecting and how can I make the redirect work properly. Any input would be appreciated.
Policy:
<policies>
<inbound>
<base />
<set-backend-service base-url="https://service-name.azurewebsites.net" />
<rewrite-uri template="#(context.Request.Url.Path.Replace("/operation-name","/api/Skills/Async/operation-name"))" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Response:
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
<!DOCTYPE html>
<html>
<head>
<title>Redirecting</title>
......
Test and see what your backend service returns to a tool like Postman or Fiddler, those would be best to emulate regular HTTP request not sent by browser. Perhaps backend expects some header in request that is sent implicitly, or maybe redirect is just very fast and not noticeable by user. To make it work through APIM you'll hve to take care to either not trigger redirect or make it redirect to APIM again. To some extent redirect-content-urls policy may be of use here: https://learn.microsoft.com/en-us/azure/api-management/api-management-transformation-policies#MaskURLSContent

Invoking API fails from web page, though succeeds in Test pages

I've create a API populated from a swagger.json file and verified it works in the Test UI. The test UI request succeeds showing the headers passed below.
POST https://apiforqubo.azure-api.net/api/Qubo HTTP/1.1
Host: apiforqubo.azure-api.net
Content-Type: application/json
Ocp-Apim-Subscription-Key: ••••••••••••••••••••••••••••••••
Ocp-Apim-Trace: true
I've added a CORS policy for all operations. This is now updated to reflect the answer below.
The updated policy is:
<policies>
<inbound>
<base />
<cors allow-credentials="true">
<allowed-origins>
<origin>https://apiforqubo.developer.azure-api.net</origin>
</allowed-origins>
<allowed-methods>
<method>POST</method>
<method>GET</method>
<method>PUT</method>
</allowed-methods>
</cors>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
When I try to execute the API from the web page that's generated for the API at: https://apiforqubo.developer.azure-api.net/api-detail, the request fails with the error:
Unable to complete the request
Since the browser initiates the request, it requires Cross-Origin Resource Sharing (CORS) enabled on the server.
What am I missing?
UPDATE: policy statement to reflect specific website as suggested by Vitaliy Kurokhtin in the comments.
Regards,
Rajesh
You've configured it to allow CORS requests only from https://azure-api.net, not from https://apiforqubo.developer.azure-api.net. Either use full URL or wildcard:
<origin>https://*.azure-api.net/</origin>
Please add allowed-headers policy element in your policy in APIM

Is it possible to use the subscription-key query string parameter with Azure API Management SOAP-passthrough?

We use API Management to expose several API's. One of the API's we expose is configured as a SOAP-passthrough API but we are facing some issues with it regarding authentication of APIM.
When we use the Ocp-Apim-Subscription-Key header for passing the query string it all works correct and the API is returning it's content correct.
When we use the subscription-key query string parameter the API is returning a 401 Unauthorized. I tested this behavior in Postman and changing the way of sending the subscription key is resulting in this behavior.
An implementation detail of this API is that it exposes an existing WSDL and routes this SOAPAction to an Azure Function via the policy. In the Application Insights of the function I can verify that the function is never invoked when I get a 401 but it is invoked when I get a successful call (using the header).
Is this normal behavior? Am I doing things wrong? Or is it a bug in APIM?
This might be an issue with the way we do routing for SOAP Passthrough. You will notice in the API setup that we add on a query parameter to identify the SoapAction that an operation will be matched to. It may be that your the api key query parameter is getting overwritten when adding the SoapAction parameter to the inbound request. I will investigate and let your know.
We currently use a workaround around this problem with the following policy. Instead of changing the backend-server url in the policy we send a request and set the response of that request as a response for this api. Below you can find our policy which is working with the subscription-key in the query string.
<policies>
<inbound>
<base />
<send-request mode="copy" response-variable-name="response" timeout="20" ignore-error="false">
<set-url>{{BackendServer_URL}}</set-url>
</send-request>
<!--return-response response-variable-name="reponse" /-->
<choose>
<!-- If StatusCode is not OK, return Unauthorized with the reason. -->
<when condition="#(((IResponse)context.Variables["response"]).StatusCode != 200)">
<return-response response-variable-name="reponse">
<set-status code="401" reason="Unauthorized" />
<set-body>#(((IResponse)context.Variables["response"]).Body.As<string>())</set-body>
</return-response>
</when>
<otherwise>
<return-response response-variable-name="reponse">
<set-status code="200" />
<set-header name="Content-Type" exists-action="override">
<value>text/xml; charset=utf-8</value>
</set-header>
<set-body>#(((IResponse)context.Variables["response"]).Body.As<string>())</set-body>
</return-response>
</otherwise>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>

Resources