2
votes

Good Day,

I am trying to store the incoming JSON payload into the Azure API Manager internal cache using the "cache-store-value" policy. The key will be one of the field coming inside the payload. I am able to extract the key but when I am trying to store the payload I am getting the error

"Expression evaluation failed. Object reference not set to an instance of an object."

Here is the code I am writing

<policies>
<inbound>
    <base />
    <set-variable name="processIdKey" value="@((string)context.Request.Body.As<JObject>()["id"])" />
    <set-variable name="validationResults" value="@(context.Request.Body.As<JObject>())" />
    <cache-store-value key="@((string)context.Variables["processIdKey"])" value="@((string)context.Variables["validationResults"])" duration="30" />
</inbound>
<backend>
    <base />
</backend>
<outbound>
    <base />
</outbound>
<on-error>
    <base />
</on-error>

I later need to extract the value in another method of the api so need to know how to store the JSON payload in the cache and extract the same to send as a response in another method.

Thanks in advance for your help.

2
Make a test call from Azure Portal and check trace to see which expression exactly produces null ref. Without knowing details of you request, I'd say you need to check if Body is null (it is for GET/HEAD requests). - Vitaliy Kurokhtin
Greetings Vitaliy, sorry I should have put the trace error. I was getting the error at the below statement <set-variable name="validationResults" value="@(context.Request.Body.As<JObject>())" /> Now I have found the solution. The reason was the context variable Request can not be referenced more than once. so I had to store that in a variable then convert that into string. - BKA
Ah. The reason behind that is that APIM by default does not cache request body. When you read it in policy (.As<JObject>()) the body is consumed and replaced with null to not store it in memory twice (once as parsed JObject, and second time as a stream). Putting it into variable is the right approach if you always need it as a JObject. But if you ever have a need to parse it twice, you can do so by calling .As<JObject>(preserveContent: true). Such call will still give you JObject, but will also cache original body for further consumption. - Vitaliy Kurokhtin
Greetings Vitaliy, great that explanation is really helpful. You are a * :-) - BKA
I wish the disposal of the body would be explicitly stated in the documentation! Thanks for the question and answer! - max3d

2 Answers

2
votes

I have found the answer, the issue was, we can't extract the context.Request object more than once in the entire proxy. So what I had to do was store that in a variable as JObject then extract the "id" field out of that. Later convert that into string to store in the cache. Here is the updated code.

<policies>
<inbound>
    <base />
    <set-variable name="validationResults" value="@(context.Request.Body.As<JObject>())" />
    <set-variable name="processIdKey" value="@((string)((JObject)context.Variables["validationResults"])["id"])" />
    <set-variable name="payload" value="@((string)((JObject)context.Variables["validationResults"]).ToString())" />
    <cache-store-value key="@((string)context.Variables["processIdKey"])" value="@((string)context.Variables["payload"])" duration="30" />
</inbound>
<backend>
    <base />
</backend>
<outbound>
    <base />
</outbound>
<on-error>
    <base />
</on-error>

Hope that helps. Thanks.

0
votes

Try context.Request.Body.As<string>(). Method As currently supports following types as generic argument value:

byte[]
string
JToken
JObject
JArray
XNode
XElement
XDocument

Mind that if you try to call .As<JObject> over response that does not contain valid JSON you would get an exception, same applies to other types as well.

Here is the article about caching you could refer to.