1
votes

NOTE: SEE MY OWN ANSWER BELOW

I have been looking through the slim documentation for hours. What I need is this:

I have an incoming GET request from a system that can ONLY send GET requests. My back-end service requires a POST request.

Can someone point me in the right direction in regards to what APIM policy I should be using or a combination of policies.

I am using the policy reference but still can't come up with a decent solution: https://msdn.microsoft.com/en-us/library/azure/dn894081.aspx

3

3 Answers

3
votes

I found a solution to this.

Here is the scenario: I have a requirement for a client to call an APIM endpoint and only get simple responses back. ie.... authenticated = true / false.

I have an incoming GET request from a system that can ONLY send GET requests. My back-end service requires a POST request.

Here is my GET call from a client that can only send GET requests: https:///api/v1//10010810?pin=1212&property=Sydney&subscription-key=XXXXXXXXXXXXXX&debugging=1

Here is my policy:

<policies>
<inbound>
    <set-header name="Content-Type" exists-action="override">
        <value>application/json</value>
    </set-header>
    <trace source="defaultTrace">
        @{
            return System.String.Format("The passed in querystring paramters were | pin: {0} | debugging: {1} | property: {2} | subscription-key: {3}", 
                context.Request.Url.Query.GetValueOrDefault("pin"), 
                context.Request.Url.Query.GetValueOrDefault("debugging"),
                context.Request.Url.Query.GetValueOrDefault("property"),
                context.Request.Url.Query.GetValueOrDefault("subscription-key")
                );
        }
    </trace>
    <set-variable name="requestPin" value="@(context.Request.Url.Query.GetValueOrDefault("pin"))" />
    <set-variable name="debugging" value="@(context.Request.Url.Query.GetValueOrDefault("debugging"))" />
    <trace source="defaultTrace">
        @{
            return System.String.Format(
            "Removing the following querystring parameters from url as we don't want to pass these ones to the backend service debugging: {0} | subscription-key: {1} | pin: {2}", 
                context.Request.Url.Query.GetValueOrDefault("debugging"),
                context.Request.Url.Query.GetValueOrDefault("subscription-key"),
                context.Request.Url.Query.GetValueOrDefault("pin")
                );
        }
    </trace>
    <set-query-parameter name="subscription-key" exists-action="delete" />
    <set-query-parameter name="debugging" exists-action="delete" />
    <set-query-parameter name="pin" exists-action="delete" />
    <base />
</inbound>
<backend>
    <!-- Setup a response-variable-name to hold the response from the backend service-->
    <send-request mode="copy" response-variable-name="microservice-response" timeout="20" ignore-error="false">
        <!-- Set the method to POST as the backend service MUST receive a POST call-->
        <set-method>POST</set-method>
        <set-body>
            @{
                // Get the pin from the url as we need it to construct the POST body
                var requestPin = context.Variables.GetValueOrDefault<string>("requestPin");

                var postBody = new JObject(
                    new JProperty("Type", "Pin"),
                    new JProperty("Value", requestPin)
                ).ToString();
                return postBody;
            }
        </set-body>
    </send-request>
</backend>
<outbound>
    <choose>
        <when condition="@(((IResponse)context.Variables["microservice-response"]).StatusCode == 200)">
            <!-- When the micro-service returned a valid response we put the response into the previously created variable called  microservice-response -->
            <return-response>
                <set-status code="200" reason="Ok" />
                <set-body>
                    @{
                        var debuggingVariable = context.Variables.GetValueOrDefault<string>("debugging");
                        var microserviceResponse = ((IResponse)context.Variables["microservice-response"]).Body.As<JObject>();

                        if(debuggingVariable == "1")
                        {
                            var returnResponse = new JObject(
                                new JProperty("Authenticated", true),
                                new JProperty("MicroserviceResponse", microserviceResponse),
                                new JProperty("StatusCode", ((IResponse)context.Variables["microservice-response"]).StatusCode)
                            ).ToString();
                            return returnResponse.ToString();
                        }
                        else
                        {
                            var returnResponse = new JObject(new JProperty("Authenticated", true)).ToString();
                            return returnResponse.ToString();
                        }
                    }
                </set-body>
            </return-response>
        </when>
        <when condition="@(((IResponse)context.Variables["microservice-response"]).StatusCode == 401)">
            <!-- When the micro-service returned a valid response we put the response into the previously created variable called  microservice-response -->
            <return-response>
                <set-status code="401" reason="Error" />
                <set-body>
                    @{
                        var debuggingVariable = context.Variables.GetValueOrDefault<string>("debugging");
                        var microserviceResponse = ((IResponse)context.Variables["microservice-response"]);

                        if(debuggingVariable == "1")
                        {
                            var returnResponse = new JObject(
                                new JProperty("Authenticated", false),
                                new JProperty("MicroserviceResponse", microserviceResponse.Body.As<JObject>().ToString()),
                                new JProperty("StatusCode", ((IResponse)context.Variables["microservice-response"]).StatusCode)
                            ).ToString();
                            return returnResponse.ToString();
                        }
                        else
                        {
                            var returnResponse = new JObject(new JProperty("Authenticated", false)).ToString();
                            return returnResponse.ToString();
                        }
                    }
                </set-body>
            </return-response>
        </when>
        <when condition="@(((IResponse)context.Variables["microservice-response"]).StatusCode == 400)">
            <!-- When the micro-service returned a valid response we put the response into the previously created variable called  microservice-response -->
            <return-response>
                <set-status code="200" reason="Ok" />
                <set-body>
                    @{
                        var debuggingVariable = context.Variables.GetValueOrDefault<string>("debugging");
                        var microserviceResponse = ((IResponse)context.Variables["microservice-response"]);

                        if(debuggingVariable == "1")
                        {
                            var returnResponse = new JObject(
                                new JProperty("Authenticated", false),
                                new JProperty("MicroserviceResponse", microserviceResponse.Body.As<JObject>().ToString()),
                                new JProperty("StatusCode", ((IResponse)context.Variables["microservice-response"]).StatusCode)
                            ).ToString();
                            return returnResponse.ToString();
                        }
                        else
                        {
                            var returnResponse = new JObject(new JProperty("Authenticated", false)).ToString();
                            return returnResponse.ToString();
                        }
                    }
                </set-body>
            </return-response>
        </when>
        <otherwise>
            <return-response>
                <!-- When the micro-service threw an exception we just want to show the caller Authenticated = false -->
                <!--<set-status code="500" reason="Error" />-->
                <set-body>
                        @{
                            var debuggingVariable = context.Variables.GetValueOrDefault<string>("debugging");
                            var microserviceResponse = ((IResponse)context.Variables["microservice-response"]);

                            if(debuggingVariable == "1")
                            {
                                var returnResponse = new JObject(
                                    new JProperty("Authenticated", false),
                                    new JProperty("MicroserviceResponse", microserviceResponse.Body.As<JObject>().ToString()),
                                    new JProperty("StatusCode", ((IResponse)context.Variables["microservice-response"]).StatusCode)
                                ).ToString();
                                return returnResponse.ToString();
                            }
                            else
                            {
                                var returnResponse = new JObject(new JProperty("Authenticated", false)).ToString();
                                return returnResponse.ToString();
                            }
                        }
                </set-body>
            </return-response>
        </otherwise>
    </choose>
    <base />
</outbound>
<on-error>
    <!-- When APIM threw an exception -->
    <trace source="defaultTrace">
        @{
            var returnResponse = new JObject
                (
                    new JProperty("Authenticated", false),
                    new JProperty("Source", context.LastError.Source),
                    new JProperty("Reason", context.LastError.Reason),
                    new JProperty("Message", context.LastError.Message),
                    new JProperty("Scope", context.LastError.Scope),
                    new JProperty("Section", context.LastError.Section),
                    new JProperty("Path", context.LastError.Path),
                    new JProperty("PolicyId", context.LastError.PolicyId)
                ).ToString();
            return returnResponse.ToString();
        } 
    </trace>
    <return-response>
        <set-status code="500" reason="Error" />
        <set-body>
            @{
                var debuggingVariable = context.Variables.GetValueOrDefault<string>("debugging");
                if(debuggingVariable == "1")
                {
                    var returnResponse = new JObject
                        (
                            new JProperty("Authenticated", false),
                            new JProperty("Source", context.LastError.Source),
                            new JProperty("Reason", context.LastError.Reason),
                            new JProperty("Message", context.LastError.Message),
                            new JProperty("Scope", context.LastError.Scope),
                            new JProperty("Section", context.LastError.Section),
                            new JProperty("Path", context.LastError.Path),
                            new JProperty("PolicyId", context.LastError.PolicyId)
                        ).ToString();
                    return returnResponse.ToString();
                }
                else
                {
                    var returnResponse = new JObject(new JProperty("Authenticated", false)).ToString();
                    return returnResponse.ToString();
                }
            }
        </set-body>
    </return-response>
</on-error>

The policy does the following:

IN-BOUND

  • Writes some trace statements throughout
  • Gets a couple of parameters from the querystring for use in the policy
  • Removes a couple of parameters as we can't pass these to the backend service as the call won't match the method signature

BACK-END - Send Request

  • Sends a request to the backend service using the existing URL that was pass but obviously without the parameters we removed
  • Sets the method to POST
  • Sets the message body using another parameter

OUT-BOUND

  • If we get a 200 response then we know the backend service functioned correctly
  • Setup a variable called microservice-response to store the response from the backend service
  • Setup a debugging variable which will be removed in production. This is so the testers can we more detailed information. We will display the contents of microservice-response to them
  • If something went wrong at the backend service (not a 200 response) then throw an error

ON-ERROR

  • When APIM captures an error. Extract all the details from the context.LastError variable and if in debugging mode then sent to caller

Here is an example of context.LastError: enter image description here

I hope this helps someone as it took me a long time to get to this point. The documentation on this is good but there is not much out there

1
votes

You can use the tag to change the HTTP method for a request. Please refer to https://msdn.microsoft.com/en-us/library/azure/dn894085.aspx#SetRequestMethod for more info

0
votes

In the inbound section of the policy for the operation you want to change, you can use the set-method policy to change the HTTP method of the request.

<inbound>
      <set-method>POST</set-method>
</inbound>