azure-api-management

How do I extract and match values inside APIM management policy


I have JSON array in form below

[
  {
    "hostname": "hostname1.company.com",
    "value": 28
  },
  {
    "hostname": "hostname2.company.com",
    "value": 55
  }
]

This is stored on external URL and retrieved during policy run and put into cache.

I need to extract value from this array matching to incoming hostheader of request and having trouble writing correct Azure APIM Policy to do that

<inbound>
        <cache-lookup-value key="hostnames" variable-name="hostnames" />
        <choose>
            <when condition="@(!context.Variables.ContainsKey("hostnames"))">
                <send-request mode="new" response-variable-name="response" timeout="20" ignore-error="true">
                    <set-url>@($"https://gist.githubusercontent.com/artisticcheese/b72ecad52e5c6804c7f0e81dd0344dc7/raw/908bf7f93891674ca665d3c4982a02de43b6ed6f/gistfile1.txt")</set-url>
                    <set-method>GET</set-method>
                </send-request>
                <set-variable name="hostnames" value="@(((IResponse)context.Variables["response"]).Body.As<JObject>())" />
                <cache-store-value key="hostnames" value="@((string)context.Variables["hostnames"])" duration="100000" />
            </when>
        </choose>
        <set-variable name="requestHostname" value="@(context.Request.Headers["X-Forwarded-Host"].First())" />
        <set-variable name="matching_value" value="@{ 
          var inputJson = context.Variables["hostnames"];
    var incomingHostheader = context.Variables["requestHostname"];  
    var jsonArray = (JArray)inputJson;
 var matchedEntry = jsonArray.FirstOrDefault(entry => 
                (string)entry["hostname"] == incomingHostheader );
                return matchedEntry;
}" />
        <return-response>
            <set-status code="200" reason="OK" />
            <set-header name="Content-Type" exists-action="override">
                <value>application/json</value>
            </set-header>
            <set-body>@{
        var hostnames = (string)context.Variables["hostnames"];
        return hostnames;
    }</set-body>
        </return-response>
        <base />
    </inbound>

Code does not work, it's probably easier to do compared to what I did so far

Getting following error

"messages": [
                        {
                            "message": "Expression evaluation failed.",
                            "expression": " \n          var inputJson = (JObject)context.Variables[\"hostnames\"];\n    var incomingHostheader = context.Variables[\"requestHostname\"];  \n    var jsonArray = (JArray)inputJson[\"0\"];\n var matchedEntry = jsonArray.FirstOrDefault(entry => \n                (string)entry[\"hostname\"] == incomingHostheader );\n                return matchedEntry;\n",
                            "details": "Unable to cast object of type 'System.String' to type 'Newtonsoft.Json.Linq.JObject'."
                        },
                        "Expression evaluation failed. Unable to cast object of type 'System.String' to type 'Newtonsoft.Json.Linq.JObject'.",
                        "Unable to cast object of type 'System.String' to type 'Newtonsoft.Json.Linq.JObject'."
                    ]

Solution

  • You can use the below given policy to get the matched value.

    <policies>
        <inbound>
            <base />
            <cache-lookup-value key="hostnames" variable-name="hostnames" />
            <choose>
                <when condition="@(!context.Variables.ContainsKey("hostnames"))">
                    <send-request mode="new" response-variable-name="response" timeout="20" ignore-error="true">
                        <set-url>@("https://gist.githubusercontent.com/artisticcheese/b72ecad52e5c6804c7f0e81dd0344dc7/raw/908bf7f93891674ca665d3c4982a02de43b6ed6f/gistfile1.txt")</set-url>
                        <set-method>GET</set-method>
                    </send-request>
                    <set-variable name="hostnames" value="@(((IResponse)context.Variables["response"]).Body.As<string>())" />
                    <cache-store-value key="hostnames" value="@((string)context.Variables["hostnames"])" duration="100000" />
                </when>
            </choose>
            <set-variable name="requestHostname" value="@(context.Request.Headers["X-Forwarded-Host"].First())" />
            <set-variable name="matching_value" value="@{
                var inputJson = (string)context.Variables["hostnames"]; 
                var incomingHostheader = (string)context.Variables["requestHostname"]; 
                var jsonArray = JArray.Parse(inputJson); 
                var matchedEntry = jsonArray.FirstOrDefault(entry => 
                    (string)entry["hostname"] == incomingHostheader); 
                return (int?)matchedEntry["value"]; 
            }" />
            <return-response>
                <set-status code="200" reason="OK" />
                <set-header name="Content-Type" exists-action="override">
                    <value>application/json</value>
                </set-header>
                <set-body>@{
                    return $"{{ \"matching_value\": \"{context.Variables["matching_value"]}\" }}";
                }</set-body>
            </return-response>
        </inbound>
    </policies>
    

    Response :-

    enter image description here

    Trace :-

    enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here