Work with URL template parameter values in policy templates - azure-api-management

In API Management, how do you access URL template parameters through a policy?
In this instance, my operation is called test, the HTML verb is GET, and the URL template is as below -
/test/{variable_name1}/{variable_name2}
I was under the impression that accessing the value of a parameter was as simple as {variable_name1}. However, the example below does not set the variable "rowkey" as expected. Rather it has a value of {variable_name1}-{variable_name2}.
What am I doing wrong here?
<policies>
<inbound>
<set-variable name="rowkey" value="{variable_name1}-{variable_name2}" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<set-header name="Row-Key" exists-action="override">
<value>#((string)context.Variables["rowkey"])</value>
</set-header>
</outbound>
</policies>

You'd have to use expressions to achieve what you want, like:
<set-variable
name="rowkey"
value="#(context.Request.MatchedParameters["variable_name1"] + "-" + context.Request.MatchedParameters["variable_name2"])" />
or use string interpolation:
<set-variable
name="rowkey"
value="#($"{context.Request.MatchedParameters["variable_name1"]}-{context.Request.MatchedParameters["variable_name2"]}")" />

Related

Azure APIM How to assign Header response to a variable

I have the following policy fragment that connects to an API we are using for authorization.
<fragment>
<send-request mode="new" response-variable-name="bearerToken" timeout="20" ignore-error="true">
<set-url>{{url}}</set-url>
<set-method>POST</set-method>
<set-header name="Content-Type" exists-action="override">
<value>application/x-www-form-urlencoded</value>
</set-header>
<set-body>#{ return "{{target-url}}"; }</set-body>
</send-request>
<set-header name="Authorization" exists-action="override">
<value>#("Bearer " + (String)((IResponse)context.Variables["bearerToken"]).Body.As<JObject>()["access_token"])</value>
</set-header>
</fragment>
This works fine with no issues, however, I would like to capture the information pushed to the Authorization Header and push it to a variable for later use in my APIs.
When I do something similar to the following or it's variants, I only receive errors and no information as to what has happened.
<set-variable name="BearerToken" value="#("Bearer " + (String)((IResponse)context.Variables["bearerToken"]).Body.As<JObject>()["access_token"]" />
I'm not sure if this is an order of execution or something else I might have missed?
Thank you - Greg.

how to check value passed in header is Guid or not in azure APIM policy if not throw bad request

APIM policy:
<set-variable name="xcid" value="#(context.Request.Headers.GetValueOrDefault("x-correlation-id", Guid.NewGuid().ToString()))" />
<choose>
<!--Request will be rejected if vaild x-correlation-id is not passed -->
<when condition="#((bool)Guid.TryParse(context.Variables["xcid"], out newGuid)== false)">
<return-response>
<set-status code="400" reason="Invalid x-correlation-id is passed please pass valid guid x-correlation-id" />
</return-response>
</when>
</choose>
Your solution was almost correct.
I assume you want to achieve the following.
if header x-correlation-id is missing, create a new guid and proceed
if header x-correlation-id is present and the value is a valid guid, then proceed
otherwise, reject the request with a status code 400
I had to fix only this line:
<when condition="#(Guid.TryParse(context.Variables.GetValueOrDefault<string>("xcid"), out Guid newGuid) == false)">
The variable xcid has to be string.
The datatype Guid his to be used for the out parameter newGuid.
Policy:
<set-variable name="xcid" value="#(context.Request.Headers.GetValueOrDefault("x-correlation-id", Guid.NewGuid().ToString()))" />
<choose>
<!--Request will be rejected if vaild x-correlation-id is not passed -->
<when condition="#(Guid.TryParse(context.Variables.GetValueOrDefault<string>("xcid"), out Guid newGuid) == false)">
<return-response>
<set-status code="400" reason="Invalid x-correlation-id is passed please pass valid guid x-correlation-id" />
</return-response>
</when>
</choose>

Azure APIM calls function even though there was an error

I have an Azure APIM which checks the request bearer JWT token:
<inbound>
<base />
<set-variable name="TenantId" value="
#{
var tenantId = context.Request.Headers.GetValueOrDefault("TenantId","");
if (tenantId != null) {
return tenantId;
}
return null;
}" />
<limit-concurrency key="#((string)context.Variables["TenantId"])" max-count="1">
<set-header name="Ocp-Apim-Subscription-Key" exists-action="delete" />
<set-header name="subscription-key" exists-action="delete" />
<set-variable name="JWTIssuer" value="
#{
var authStr = context.Request.Headers.GetValueOrDefault("Authorization","");
if (authStr.StartsWith("Bearer")) {
var jwt = authStr.Substring("Bearer ".Length).AsJwt();
if (jwt != null) {
return jwt.Issuer;
}
}
return null;
}" />
<choose>
<when condition="#(context.Variables.GetValueOrDefault("JWTIssuer", "") == "<URL of issuer>")">
<!-- Authorisation using HPCA token -->
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized" require-expiration-time="true" require-scheme="Bearer" require-signed-tokens="true">
<openid-config url="<Open Id Url>" />
<required-claims>
<claim name="MyClaim" match="any" separator=",">
<value>X</value>
</claim>
</required-claims>
</validate-jwt>
</when>
<!-- HPCA Error handling -->
<otherwise>
<return-response>
<set-status code="401" reason="Unauthorized" />
<set-header name="WWW-Authenticate" exists-action="override">
<value>Bearer error "Invalid HPCA token"</value>
</set-header>
</return-response>
</otherwise>
</choose>
</limit-concurrency>
</inbound>
<on-error>
<base />
<return-response>
<set-status code="#(context.Response.StatusCode)" reason="#(context.Response.StatusReason)" />
</return-response>
</on-error>
This all works as expected: if either the issuer is incorrect or the JWT token is invalid, then the OnError handler is invoked and a 401 error status is returned to the caller. The problem is, if i add a call to a function after the Choose policy:
</otherwise>
</choose>
<send-request mode="copy" response-variable-name="Response" timeout="10" ignore-error="false">
<set-url>https://myfuncApp.azurewebsites.net/api/Function1</set-url>
<set-method>POST</set-method>
</send-request>
<return-response response-variable-name="Response">
<set-status code="#(context.Response.StatusCode)" reason="#(context.Response.StatusReason)" />
</return-response>
</limit-concurrency>
</inbound>
Then even with a invalid bearer token and the error handler being invoked, the APIM still proceeds to call the function: the return response within the error handler is seemingly ignored - why ? I can fudge it by wrapping the send-request within another conditional (i.e. check the context.Response.StatusCode isn't 401) but this doesn't seem right to me.
For your question, I think the result of your test is normal.
You added the <send-request> to request the function in <inbound> area. The <validate-jwt> can just determine if the APIM to continue request the backend url but can not determine if it continues to request function url in <inbound> area.

unable to parse soap response using Azure API Management liquid template

I want to change the output format of the soap response, before passing it onto the client. I am using the soap pass-through as specified in the documentation https://azure.microsoft.com/en-ca/blog/soap-pass-through/
Soap service used in this example is hosted at
https://fazioapisoap.azurewebsites.net/FazioService.svc?singleWsdl
I am unable to extract the soap response in the liquid template. Soap response does get generated, however, there is no data.
Is there anything wrong with this code?
<policies>
<inbound>
<set-header name="Content-Type" exists-action="override">
<value>text/xml</value>
</set-header>
<set-body template="liquid">
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<soapenv:Header />
<soapenv:Body>
<tem:GetOpenOrders>
<!--Optional:-->
<tem:cust>{{body.Envelope.Body.GetOpenOrders.cust}}</tem:cust>
</tem:GetOpenOrders>
</soapenv:Body>
</soapenv:Envelope>
</set-body>
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<set-body template="liquid">
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetOpenOrdersResponse xmlns="http://tempuri.org/">
<GetOpenOrdersResult xmlns:a="http://schemas.datacontract.org/2004/07/FazioAPISoap" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
{% for summary in body.Envelope.Body.GetOpenOrdersResponse.GetOpenOrdersResult.OrderSummary -%}
<a:OrderSummary><a:order_id>{{summary.order_id}}</a:order_id></a:OrderSummary>
{% endfor -%}
</GetOpenOrdersResult>
</GetOpenOrdersResponse>
</s:Body>
</s:Envelope>
</set-body>
<set-header name="Content-Type" exists-action="override">
<value>text/xml</value>
</set-header>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
You don't have to prefix the namespaces. This requests is valid
<set-body template="liquid">
<Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<GetOpenOrdersResponse xmlns="http://tempuri.org/">
<GetOpenOrdersResult xmlns:a="http://schemas.datacontract.org/2004/07/FazioAPISoap" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
{% for summary in body.Envelope.Body.GetOpenOrdersResponse.GetOpenOrdersResult.OrderSummary -%}
<OrderSummary>
<order_id>
{{summary.order_id}}
</order_id>
</OrderSummary>
{% endfor -%}
</GetOpenOrdersResult>
</GetOpenOrdersResponse>
</s:Body>
</s:Envelope>
</set-body>
I can see two potential problems with your request.
You might be missing SOAPAction header, example for submitOrder
<set-header name="SOAPAction" exists-action="override">
<value>http://tempuri.org/IFazioService/submitOrder</value>
</set-header>
Also you may want to set backend url and rewrite path to the resource (if APIM path is different than the SOAP service)
<set-backend-service base-url="{{s-iserve-store-inventory-backend-url}}" />
<rewrite-uri template="?" copy-unmatched-params="false" />

How Do I Throw An Error In Azure API Management Policy?

In my Azure API Management Policy I am checking for some headers and do certain actions depending on what is found.
How do I throw an error when none of the conditions are matched (i.e. in the otherwise block)
<policies>
<inbound>
<choose>
<when condition="">
</when>
<when condition="">
</when>
<otherwise>
</otherwise>
</choose>
<base/>
</inbound>
<backend>
<base/>
</backend>
<outbound>
<base/>
</outbound>
<on-error>
<base/>
</on-error>
</policies>
I probably want to return a 401 since I am checking groups in the headers.
You can use a <choose> policy to detect and report failure, return a 401 response.
<otherwise>
<return-response >
<set-status code="401" reason="Unauthorized" />
<set-header name="WWW-Authenticate" exists-action="override">
<value>Bearer error="invalid_token"</value>
</set-header>
</return-response>
</otherwise>
Here is also a similar SO thread you could refer to.