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:
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