Search code examples
c#azureapi-gatewayapimliquid-template

Azure APIM - set-body policy returning Byte data


I've been trying to forward requests based on an Authorization token to a certain backend using Azure APIM.

I'm able to reach my application (the traces show me that the error is when attempting to parse the Model to JSON), but when I try to parse the request body to my Model (DTO) I get an error:

Microsoft.AspNetCore.Http.BadHttpRequestException: Failed to read parameter "FooBarModel request" from the request body as JSON.  
---> System.Text.Json.JsonException: '0x0A' is invalid within a JSON string. The string should be correctly escaped. Path: $ | LineNumber: 1 | BytePositionInLine: 19.

My APIM Policy is as follows:

<policies>
    <inbound>
        <base />
        <choose>
            <when condition="@(context.Request.Headers.GetValueOrDefault("Authorization","").AsJwt()?.Claims.GetValueOrDefault("key", "") == "AuthTokenValue")">
                <set-backend-service backend-id="BackendId" />
                <set-body template="liquid" xsi-nil="null">
                "@(
                    JObject body = context.Request.Body.As&lt;JObject&gt;();
                    var newBody = {
                        "key1": {{body.key1}},
                        "key2": {{body.key2}},
                        {
                            "key3": {{body.key3}},
                            "key4": {{body.key4}}
                        }
                    };
                    return newBody.ToString();
                )"
                    <set-header name="Content-Type" exists-action="override"><value>application/json</value></set-header></set-body>
                <rewrite-uri template="@{  
                    return "/api/v1/end/{value}/point";   
                }" />
            </when>
            <otherwise />
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

I believe it is something to do with the data type that the request body is being sent, but I can't seem to find a solution.

Please refrain from posting links to the docs unless it absolutely is the answer to this question, for I have read it again and again.


Solution

  • After a lot of research I was able to figure out a way to do this:

    <set-body>@{
      var incomingRequestBody = context.Request.Body.As<JObject>();
      // get the incoming RequestBody and set it as a JSON object.
    
      var outgoingRequestBody = new JObject();
      // create a new JSON object, one that will be the new RequestBody.
    
      outgoingRequestBody["key1"] = incomingRequestBody["key1"];
      // set the new RequestBody keys from the old RequestBody key values.
    
      outgoingRequestBody["key2"] = incomingRequestBody["key2"];
    
      var nestedDict = new JObject();
      // in case you have a nested object, such as a dictionary in your payload, 
      // it is necessary to create a new JSON Object and store the desired values in the created object.
    
      nestedQrCodeInfo["nestedKey1"] = incomingRequestBody["nestedKey1"];
      nestedQrCodeInfo["nestedKey2"] = incomingRequestBody["nestedKey2"];
      // setting the nested dictionary keys values (for example) 
      
      outgoingRequestBody["nestedKey"] = nestedDict;
      // setting the nested dictionary to a key on the new RequestBody to be returned.
      
      return outgoingRequestBody.ToString();
      // it is necessary to return the new Body.
    }
    </set-body>
    

    It's a simple yet effective way to transform and return a request body.