Unable to set body & return response in APIM policies, getting 500 error

692 Views Asked by At

I am using this code for azure APIM policies

<set-variable name="newRequest" value="@(context.Request.Body?.As<JObject>(preserveContent: true))" />
<set-variable name="insured-id" value="@(context.Request.MatchedParameters["id"].Last())" />
<send-request mode="new" timeout="20" response-variable-name="id" ignore-error="false">
    <set-url>@($"https://api.dev.com/external/workRequest/get")</set-url>
    <set-method>POST</set-method>
    <set-header name="Content-Type" exists-action="override">
        <value>application/json</value>
    </set-header>
    <set-header name="Authorization" exists-action="override">
    <value>@(context.Request.Headers.GetValueOrDefault("Authorization","scheme param"))</value>
    </set-header>
    <set-body>{"insuredId": @($"{(string)context.Variables["insured-id"]}")}</set-body>
</send-request>
<choose>
    <when condition="@((int)((IResponse)context.Variables["id"]).Body.As<JObject>(preserveContent: true)["workRequests"]["entityStatus"]== 1)">
        <return-response response-variable-name="id">
            <set-status code="400" reason="VOID" />
            <set-header name="Content-Type" exists-action="override">
                <value>application/json</value></set-header>
            <set-body>{"statusCode": 400,
                        "message": "The insured cannot be voided as it is currently attached with one or more active workrequest"}</set-body>
            </return-response>
    </when>
    <otherwise />
</choose>

I am taking an insuredId from template parameter of the API operation where I am implementing the APIM policies & using it in the set-body, this will list all the workrequests for that insuredId.

The payload for the POST is something like

{"insuredId": template-parameter}

When returning a response getting 500 error. How to resolve this. The condition which is there is okay. I am suspecting error in set body.

Also how to check whether a particular string like "entityStatus": 1 is there in the response of api, because this https://api.dev.com/external/workRequest/get url will give a list of workrequest records in array form

2

There are 2 best solutions below

5
Markus Meyer On BEST ANSWER

The request to https://api.dev.com/external/workRequest/get is failing:

"message": "POST request to 'https://api.dev.com/external/workRequest/get' has been sent, result stored in 'id' variable.",
"statusCode": "MethodNotAllowed",
"statusReason": "Method Not Allowed",

The response of failed request is stored in the variable id. But this response not return any json document in the body.

Therefore, the following expression is failing and an error 500 is returned:

"(int)((IResponse)context.Variables[\"id\"]).Body.As<JObject>(preserveContent: true)[\"workRequests\"][\"entityStatus\"]== 1",

The message body is not a valid JSON. Unexpected character encountered while parsing value: <. Path '', line 0, position 0.\r\n at Newtonsoft.Json.JsonTextReader.ParseValue()\r\n at Microsoft.WindowsAzure.ApiManagement.Proxy.Runtime.Configuration.Models.SyncOnlyDepthTrackingJsonTextReader.Read()\r\n at Newtonsoft.Json.Linq.JObject.Load(JsonReader reader, JsonLoadSettings settings)\r\n at Gateway.Pipeline.IO.JObjectFormatter.Format(JsonTextReader jsonTextReader)\r\n at Gateway.Pipeline.IO.JsonConverter.Convert[T](Stream stream, Encoding encoding, ILog log, Object conversionSettings)\r\n at Microsoft.WindowsAzure.ApiManagement.Proxy.Gateway.MessageBody.As[T](Boolean preserveContent)

Please check the supported http-methods of https://api.dev.com/external/workRequest/get.
Maybe your code has to changed to GET:

<send-request mode="new" timeout="20" response-variable-name="id" ignore-error="false">
<set-url>@($"https://api.dev.com/external/workRequest/get")</set-url>
<set-method>GET</set-method>

Trace of request:
enter image description here


UPDATE for question from comments:

thanks, I resolved the issue. The when condition I was using was syntactically wrong as I had a response in the form of array list { "workRequests": [{ "id": 154, "workRequestNumber": "W000154", "insured": {"id": 268,"uri": null, "entityStatus": 1,"workRequestStatus": 0,"entityStatus": 1 },....

It's better to create a new Stackoverflow Question instead of doing it in comments. Maybe that's what you asking for:

Request body:

{
  "workRequests": [
    {
      "id": 154,
      "workRequestNumber": "W000154",
      "insured": {
        "id": 268,
        "uri": null,
        "entityStatus": 1,
        "workRequestStatus": 0
      }
    },
    {
      "id": 154,
      "workRequestNumber": "W000154",
      "insured": {
        "id": 268,
        "uri": null,
        "entityStatus": 0,
        "workRequestStatus": 0
      }
    }
  ]
}

Response:

enter image description here

A variable is assigned with a true value of any entityStatus equals 1.
The choose policy returns a 400 with entityStatus equals true .

Policy:

<inbound>
    <base />
    <set-variable name="isEntityStatusEqualsOne" value="@{
        var body = (JObject)context.Request.Body.As<JObject>(true);
        JArray workrequests = body["workRequests"] as JArray; 
        for(int i = 0; i < workrequests.Count; i++)
        {
            JObject workrequest = workrequests[i] as JObject;
            var entityStatus = ((JValue)workrequest["insured"]["entityStatus"]).Value<int>();
            if(entityStatus == 1)
            {
                return true;
            }
        }

        return false;
    }" />
    <choose>
        <when condition="@(context.Variables.GetValueOrDefault<bool>("isEntityStatusEqualsOne") == true)">
            <return-response response-variable-name="id">
                <set-status code="400" reason="VOID" />
                <set-header name="Content-Type" exists-action="override">
                    <value>application/json</value>
                </set-header>
                <set-body>{"statusCode": 400,
                            "message": "The insured cannot be voided as it is currently attached with one or more active workrequest"}</set-body>
            </return-response>
        </when>
        <otherwise>
            <return-response response-variable-name="id">
                <set-status code="200" reason="OK" />
            </return-response>
        </otherwise>
    </choose>
</inbound>
0
Surendra Singh Aswal On

Try below code, for me it's fine and sending 500 when response code is not 200. I am able to set-body with 500 status code.

   <send-request mode="new" response-variable-name="createBookingResponse">
            <set-url>@(context.Variables.GetValueOrDefault<string>("domainUrl") + $"/api/Booking")</set-url>
            <set-method>POST</set-method>
            <set-header name="Content-Type" exists-action="override">
                <value>application/json</value>
            <set-body>@(context.Variables.GetValueOrDefault<string>("requestPayload"))</set-body>
        </send-request>
        <choose>
            <when condition="@(((IResponse)context.Variables["createBookingResponse"]).StatusCode == 200)">
                <return-response>
                    <set-header name="Content-Type" exists-action="override">
                        <value>application/json</value>
                    </set-header>
                    <set-body>@(((IResponse)context.Variables["createBookingResponse"]).Body.As<JObject>(preserveContent: true).ToString())</set-body>
                </return-response>
            </when>
            <!-- Return status code if non-200 status from OPA -->
            <otherwise>
                <return-response>
                    <set-status code="500" reason="Internal Server Error" />
                    <set-header name="Content-Type" exists-action="override">
                        <value>application/json</value>
                    </set-header>
                    <set-body>@(((IResponse)context.Variables["createBookingResponse"]).Body.As<JObject>(preserveContent: true).ToString())</set-body>
                </return-response>
            </otherwise>
        </choose>