1
votes

I am relatively new to Azure APIM and I have this backend which I am able to invoke which returns the response in format

{
    "data": {
        "attributes": {
            "municipalities": []
        }
    }
}

I want to modify the response so that the data is returned in the format

{
    "data": {
            "municipalities": []
    }
}

I have tried using the set body liquid template

<outbound>
<set-body template="liquid">
   {
        "data": {
                "municipalities": {{body.data.attributes.municipalities}}
               }
    }</set-body>
</outbound>

But the response I get is just

{
    "data": {
        "municipalities":
    }
}

If someone can point out to me what I am doing wrong or if there is a better way to do this?

I also tried to use the below code just to check if I am able to retrieve the "data" attribute but I got the below error in the trace part of Azure APIM Test

<outbound>
    <set-body> 
    @{ 
        JObject inBody = context.Request.Body.As<JObject>();
        return inBody["data"].ToString(); 
    } 
    </set-body>
</outbound>

ERROR:

{
    "messages": [
        {
            "message": "Expression evaluation failed.",
            "expression": " \n    JObject inBody = context.Request.Body.As<JObject>();\n    return inBody[\"data\"].ToString(); \n",
            "details": "Object reference not set to an instance of an object."
        },
        "Expression evaluation failed. Object reference not set to an instance of an object.",
        "Object reference not set to an instance of an object."
    ]
}
2
Hi LeGo, please refer to my solution provided below. If it helps your problem, please accept it as answer(click on the check mark beside my answer to toggle it from greyed out to filled in). Thanks in advance~ - Hury Shen
@HuryShen Thank you for your reply, but this would be tricky if I had 3 different routes on my API say GET /muncipalities, GET /names GET/schools and each of them had their own return schema eg body.data.attributes.municipalities , body.data.attributes.names, body.data.attributes.schools with each having different values returned. Is there an easier way I can just remove the .attributes part from all without having to write every property of each array item separately ?? because this would then need to to have 3 separate policies for my 3 routes ??? - LeGo
Hi LeGo Before talking about "is there an easier way...", I think you need to check if you can get the response data under <outbound><set-body>. According to some test, I guess you didn't get the response data success there. - Hury Shen
Please refer to the "Update" in my answer. - Hury Shen
sorry was busy so could only try this right now but it is perfect :) - LeGo

2 Answers

1
votes

As body.data.attributes.municipalities is a array, we can't put it "municipalities": directly. For your requirement of modify the format of the json data, we need to write every property of each array item. Below is my steps for your reference.

My json is:

{
    "data": {
        "attributes": {
            "municipalities": [
                {
                    "name": "hury",
                    "email": "[email protected]"
                },
                {
                    "name": "mike",
                    "email": "[email protected]"
                }
            ]
        }
    }
}

And my liquid template show as: enter image description here

==============================Update============================

First the code JObject inBody = context.Request.Body.As<JObject>(); you shared can't get the response data. You should use JObject inBody = context.Response.Body.As<JObject>();.

Then for your question about "Is there an easier way to remove .attributes part, I provide a solution below for your reference.

Do not use liquid template, use Replace to replace "attributes": { with empty and use Substring to remove the last }.

<set-body>@{ 
    JObject inBody = context.Response.Body.As<JObject>();
    string str = inBody.ToString();
    string str1 = str.Replace("\"attributes\": {", "");
    string result = str1.Substring(0, str1.Length-1);
    return result; 
}</set-body>

Notice: This approach requires a high degree of specification for your response data format.

0
votes

I also managed to get the desired output with the below code, so that the code is generic and doesnt need to do any string operations.

<set-body>@{ 
    JToken inBody = context.Response.Body.As<JToken>()["data"]["attributes"];
    JObject jsonObject = new JObject{
    ["data"] = inBody
}; 
return jsonObject.ToString();
}</set-body>