In this blog I show an example of a APIM send-request policy , subsequently using the response, a JSON array, in another call to the backend service. I highlight a special problem I had retrieving a value from the JSON array and how I overcame it.
Incidentally the API aggregation solution is a tactical solution until a purpose built API can be created in MYSite.
The APIM Send -request policy
I show a simple version of the policy .
The rest this blog will talk about the line where we retrieve a JSON value for ItemCode namely
@((string)((IResponse)context.Variables[“code”]).Body.As<JObject>()[0][“ItemCode”])” />
The Issue
On running the policy I get an error like
"messages": [
{
"message": "Expression evaluation failed.",
"expression": "(string)((IResponse)context.Variables[\"code\"]).Body.As<JObject>()[0][\"ItemCode\"]",
"details": "The message body is not a valid JSON. Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray. Path '', line 1, position 1.\r\n at Newtonsoft.Json.Linq.JObject.Load(JsonReader reader, JsonLoadSettings settings)\r\n at Microsoft.WindowsAzure.ApiManagement.Proxy.Gateway.MessageBody.AsJObject(Stream stream, Encoding encoding, JsonSerializerSettings settings)\r\n at Microsoft.WindowsAzure.ApiManagement.Proxy.Gateway.MessageBody.As[T](Boolean preserveContent)"
},
"Expression evaluation failed. The message body is not a valid JSON. Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray. Path '', line 1, position 1.\r\n at Newtonsoft.Json.Linq.JObject.Load(JsonReader reader, JsonLoadSettings settings)\r\n at Microsoft.WindowsAzure.ApiManagement.Proxy.Gateway.MessageBody.AsJObject(Stream stream, Encoding encoding, JsonSerializerSettings settings)\r\n at Microsoft.WindowsAzure.ApiManagement.Proxy.Gateway.MessageBody.As[T](Boolean preserveContent)",
"Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray. Path '', line 1, position 1."
] }
The JSON that is returned is a headless array shown below
[ { “ItemCode”: 5573, “ProductCode”: “5573” }]
This is valid JSON but APIM does not like it because it expects non-blank path like
{“Codes”: [ { “ItemCode”: 5573, “ProductCode”: “5573” }] }
A solution
I can’t change the APIM implemenation of JObject but I can workaround it. Firstly I cast the IResponse to a string instead of a JObject. Secondly I prepend the string with {“Codes”: and postpend with a }. Now the array has a head. Thirdly parse the string as a JObject. Finally retrieve the ItemCode value. thus the final solution contains this fragment.
<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"]) + "/")" />
Conclusion
While the JObject implementation in APIM does not support a “headless” JSON array it can be worked around using an APIM expression.
Explore more on Secure Azure Functions Using API Management