APIM – Caching

Posted: May 10, 2020  |  Categories: Azure Uncategorized
Tags: APIM

Following my last blog some one said

“Would like to see the caching and throttling policies added in too …”

https://twitter.com/kpatton/status/1255430889603031049?s=20

Continuing from this, firstly I show how to use advanced caching policies to cache an authorisation token. In a subsequent blog I will show use simple caching of a the image response.

Advanced Caching Policies

Let’s use the same example as last time. This is shown above. Step 2 retrieves an authorisation token from an Identity Server. The token expires after 30 minute and rather than rerieving it every time we store in the cache and resue it until it expires. Firstly you set the cache lookup value as shown below after the “Set Cache Lookup” comment .

<!--- Set Cache Lookup -->
     <cache-lookup-value key="@("token-au-dev-" + context.Variables["client-id"])" variable-name="accessToken" />

Secondly decide whether to “Retrieve the token or not”. If the token is not in the cache then a send-request policy runs to retrieve the token and “Stores result in cache”.

    <choose>
         <-- Retrieve the token or not -->
         <when condition="@(!context.Variables.ContainsKey("accessToken"))">
	    <send-request mode="new" response-variable-name="identityResponse" timeout="20" ignore-error="false">
	    <!--CODE OMITTED HERE FOR CLARITY  -->
	   </send-request>

	   <set-variable name="accessToken" value="@((string)((IResponse)context.Variables["identityResponse"]).Body.As<JObject>()["access_token"])" />

           <!-- Stores result in cache -->
	   <cache-store-value key="@("token-au-dev-" + context.Variables["client-id"])" value="@((string)context.Variables["accessToken"])" duration="1700" />

         </when>
     </choose>

Finally the stored token value is used in a subsequent service call as shown in the full policy below.

<policies>
   <inbound>
<!-- Client identity is application dependent will add effectivity. Hardcoded now-->
     <set-variable name="client-id" value="ToBeDone" />
     <set-variable name="site-BaseUrl-AU" value="{{site-BaseUrl-AU}}" />
     <set-variable name="au-request" value="@((string)context.Variables[&quot;site-BaseUrl-AU&quot;] + &quot;/api/s_v1/Product/GetItemCodes?codes=&quot; +(string)context.Request.MatchedParameters[&quot;productCode&quot;])" />

<!--- Set Cache Lookup -->
     <cache-lookup-value key="@("token-au-dev-" + context.Variables["client-id"])" variable-name="accessToken" />

     <choose>
         <-- Retrieve the token or not -->
         <when condition="@(!context.Variables.ContainsKey("accessToken"))">
	    <send-request mode="new" response-variable-name="identityResponse" timeout="20" ignore-error="false">
	    <!--PARMETERIZATION REQUIRED BELOW  -->
		<set-url>{{identity-TokenUrl-AU}}</set-url>
	        <set-method>POST</set-method>
		<set-header name="Content-Type" exists-action="override">
			<value>application/x-www-form-urlencoded</value>
		</set-header>
		<set-body> @("grant_type=password&username=1&password=2&client_id=app")</set-body>
	   </send-request>

	   <set-variable name="accessToken" value="@((string)((IResponse)context.Variables["identityResponse"]).Body.As<JObject>()["access_token"])" />

           <!-- Stores result in cache -->
	   <cache-store-value key="@("token-au-dev-" + context.Variables["client-id"])" value="@((string)context.Variables["accessToken"])" duration="1700" />

         </when>
     </choose>
     <send-request mode="new" response-variable-name="code" timeout="20" ignore-error="true">
     <!--PARMETERIZATION REQUIRED HERE  -->
	<set-url>@((string)context.Variables[&quot;au-request&quot;])</set-url>
	<set-method>GET</set-method>
	<set-header name="Content-Type" exists-action="override">
		<value>application/json; charset=utf-8</value>
	</set-header>
	<set-header name="Authorization" exists-action="override">
		<value>@(" Bearer " + (string)context.Variables["accessToken"])</value>
	</set-header>
    </send-request>
    <!-- Line below does not work because of headless JSON array
        <set-variable name="itemcode" value="@((string)((IResponse)context.Variables["code"]).Body.As<JObject>()[0]["ItemCode"])" />
      Cast as string and add head in expression but not working.
      See https://www.connected-pawns.com/2020/04/29/apim-handling-a-json- 
      array-response-from-a-send-request-policy/
-->
       <set-variable name="codes" value="@("{\"Codes\":" + (string)((IResponse)context.Variables["code"]).Body.As<string>() + "}")" />
    <set-variable name="itemcode" value="@{
          JObject codes = JObject.Parse((string)context.Variables["codes"]);
                    return (string)codes["Codes"][0]["ItemCode"];
                }" />
    <set-backend-service base-url="{{site-BaseUrl-AU}}" />
    <base />
       <rewrite-uri template="@("/api/s_v1/Image/GetImage/" + ((string)context.Variables["itemcode"]) + "/")" />
    </inbound>
    <backend>
       <base />
    </backend>
    <outbound>
       <base />
    </outbound>
     <on-error>
       <base />
    </on-error>
</policies>
#1 Azure Monitoring Platform

Conclusion

I have shown how to use a custom caching policy to reduce the number of times an authorisation token is retrieved. Using the internal APIMcache allows me a 50Mb cache per unit on a Basic APIM tier. Alternatively, Redis can be used as an external cache but I did not explore this option. While we never used this solution it was a good vehicle for me to learn about APIM..

Explore more on Secure Azure Functions Using API Management

turbo360

Back to Top