How to extract the send-request policy's response status code - azure-api-management

Using the send-request Policy in Azure APIM.
Able to get the response and extract the Body.
However, struggling with extracting the response.status.code
The value of this #(context.Response.StatusCode) is 200 if the request is sent, i am trying to capture this value...
send-request (88 ms)
{
"response": {
"status": {
"code": 200,
"reason": "OK"
}
}
}

Hope below solution will work for you.
<choose>
<when condition="#(((IResponse)context.Variables["response-variable-name"]).StatusCode == 200)">
<set-variable name="xxx" value="#(((IResponse)context.Variables["response-variable-name"]).Body.As<JObject>(preserveContent: true).ToString())" />
</when>
<otherwise>
<return-response>
<set-status code="404" reason="Not Found" />
<set-body>#(((IResponse)context.Variables["response"]).Body.As<JObject>(preserveContent: true).ToString())</set-body>
</return-response>
</otherwise>
</choose>

Related

cannot get the User Email from context in Azure API Management

I have an inbound policy that extracts the User Email from the context like this:
<inbound>
<base />
<set-backend-service id="apim-generated-policy" backend-id="azure-func-staging" />
<set-header name="customer-email" exists-action="override">
<value>#(context.User.Email)</value>
</set-header>
</inbound>
But no matter what I always get the following message:
set-header (0.788 ms)
{
"messages": [
{
"message": "Expression evaluation failed.",
"expression": "context.User.Email",
"details": "Object reference not set to an instance of an object."
},
"Expression evaluation failed. Object reference not set to an instance of an object.",
"Object reference not set to an instance of an object."
]
}
Can you help me understand what I am doing wrong?
When you set the user of the developer into the header of the request, you would add the following to the APIM policy:
Here you have missed the return statement in policy code:
<set-header name="customer-email" exists-action="override">
<value>#(context.User.Email)</value>
</set-header>
Modify your above code bit like below and try:
<set-header name="user" exists-action="override">
<value>#{var usr = context.User;
return usr.Email;}</value>
</set-header>
For more details on examples of context.user, refer this

Azure API Gateway Policy

I would like to add query parameter for the request by using response from my auth service. These are the example:
<policies>
<inbound>
<!-- Extract Token from Authorization header parameter -->
<set-variable name="token" value="#(context.Request.Headers.GetValueOrDefault("Authorization","JWT").Split(' ').Last())" />
<!-- Send request to Token Server to validate token (see RFC 7662) -->
<send-request mode="new" response-variable-name="tokenstate" timeout="20" ignore-error="false">
<set-url>AUTH Service</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>#($"token={(string)context.Variables["token"]}")</set-body>
</send-request>
<choose>
<when condition="#((bool)((IResponse)context.Variables["tokenstate"]).Body.As<JObject>()["active"] == false)">
<return-response response-variable-name="existing response variable">
<set-status code="401" reason="Unauthorized" />
</return-response>
</when>
<otherwise>
<set-query-parameter name="domain_id" exists-action="append">
<value>
#((string)((IResponse)context.Variables["tokenstate"]).Body.As<JObject>()["content"]["domain_id"])
</value>
</set-query-parameter>
</otherwise>
</choose>
<base />
</inbound>
</policies>
But I am getting this error:
{
"messages": [
{
"message": "Expression evaluation failed.",
"expression": "(string)((IResponse)context.Variables[\"tokenstate\"]).Body.As<JObject>()[\"content\"]",
"details": "Object reference not set to an instance of an object."
},
"Expression evaluation failed. Object reference not set to an instance of an object.",
"Object reference not set to an instance of an object."
]
}
Any idea how should I do it ?
By getting decoded JWT from my AUTH service and added it to the request to backend
Thank You
For performance reasons APIM service always avoids caching full request/response bodies in memory, thus when you call ((IResponse)context.Variables["tokenstate"]).Body.As() response is streamed directly from auth server and at the same time converted to JObject, so it is consumed after that and second call to .Body.As() will produce null value.
To avoid that you have two options:
call ((IResponse)context.Variables["tokenstate"]).Body.As<JObject>(true) - this additional parameter instructs service to cache response in memory so it will be preserved for later inspection. but using call to .As() later on again will once more parse raw response into JSON doing same work again and being a performance hit.
or do <set-variable name="tokenstate" value="((IResponse)context.Variables["tokenstate"]).Body.As<JObject>()" />. This will overwrite value of tokenstate variable with parsed JSON body that can be used later on multiple times as it will be stored in memory now.
just to add a solution, if you need validate some fields from request api policy may use:
place this below inbound tag:
<set-variable name="<some>" value="#(context.Request.Body.As<string>())" />
<choose>
<when condition="#(!((string) context.Variables["<some>"]).Contains("<some>"))">
<return-response>
<set-status code="400" reason="badRequest" />
<set-header name="<some>" exists-action="override">
<value><some></value>
</set-header>
<set-body />
</return-response>
</when>
<otherwise>
<return-response />
</otherwise>
</choose>

Mule: Unable to pass Error Status to calling API

I am trying to capture error status from one API & and send it to the calling API. In my downstream API I have the following catch exception strategy:
<catch-exception-strategy>
<set-payload value= "500" />
<logger message="***BACKEND API: #[payload]" level="INFO" doc:name="Logger"/>
</catch-exception-strategy>
In my calling API I have the following:
<http:request config-ref="downstream API " path="/downstream/{id}" method="POST " doc:name="Generate "/>
<json:object-to-json-transformer doc:name="Object to JSON"/>
<logger message="PAYLOAD: #[payload]" level="INFO" doc:name="Logger"/>
However I am able to print the payload in the downstream API but not in the calling API. Am I missing something?
If your API throw http.status == 500, it should trigger this condition
<choice-exception-strategy doc:name="Choice Exception Strategy">
<catch-exception-strategy when="#[message.inboundProperties.'http.status' == 500]" doc:name="Error 500">
<set-payload value= "500" />
<logger message="***BACKEND API: #[payload]" level="INFO" doc:name="Logger"/>
</catch-exception-strategy>
</choice-exception-strategy>
I you didn't add any condition, it should be the default catch exception
everything should work properly.

In Azure API Management, skip the <backend> request if basic validation fails

In Azure API Management, is it possible to skip the backend call if some simple validation fails? I require this because every call to the backend service in this case uses a portion the clients quota, and this is undesirable if we know that the request will fail.
Take the following example, where the URL template is /MyOperation/{MyParameter}:
the <inbound> portion of the policy first checks whether or not {MyParameter} is numeric, and then rewrites the URI.
the <outbound> portion of the policy checks whether or not {MyParameter} was valid, and if not instead returns some custom text to the client.
Here is the example policy -
<policies>
<inbound>
<set-variable name="isValidMyParameter" value="#{
Match match = Regex.Match(context.Request.MatchedParameters["MyParameter"], "^[0-9]*$");
return ( match.Value.ToString() != "" ) ? true : false;
}" />
<rewrite-uri template="#("/Path/To/Application/" + ""+context.Request.MatchedParameters["MyParameter"])" />
</inbound>
<outbound>
<choose>
<when condition=""#(!Convert.ToBoolean(context.Variables["isValidMyParameter"]))">
<set-status code="400" reason="Bad Request" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>{ "statusCode": 400, "message": "Invalid 'MyParameter'." }</set-body>
</when>
</outbound>
</policies>
While the works, even if {MyParameter} is invalid (say the client has passed "asdf"), a request to the backend service is made. As explained above this is undesirable because it eats in to the clients quota.
I've considered using <choose> and checking the value of isValidMyParameter, but the trouble there is that a request to the backend service is still made, just without the rewritten URI. This again eats in to the clients quota.
Is it at all possible to just skip the <backend> portion of the policy and go straight back to the client?
Move choose and add return-response policy within inbound body. This will result in immediate response to client skipping the backend request.
<inbound>
<set-variable name="isValidMyParameter" value="#{
Match match = Regex.Match(context.Request.MatchedParameters["MyParameter"], "^[0-9]*$");
return ( match.Value.ToString() != "" ) ? true : false;
}" />
<choose>
<when condition="#(!Convert.ToBoolean(context.Variables["isValidMyParameter"]))">
<return-response>
<set-status code="400" reason="Bad Request" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>{ "statusCode": 400, "message": "Invalid 'MyParameter'." }</set-body>
</return-response>
</when>
</choose>
<rewrite-uri template="#("/Path/To/Application/" + ""+context.Request.MatchedParameters["MyParameter"])" />
</inbound>
Look into return-response policy. It would allow you to immediately stop request processing and return response to a client.

Check for json key or object property in mule

Scenario - I have to iterate over this payload, and for those listings with error I would need to increment count. But how to check if error property exists?
{
"jobGuid": "123",
"status": "COMPLETED",
"listings": [
{
"exteralListingId": 7654320
},
{
"exteralListingId": 7654321,
"error": {
"code": "inventory.listings.sellerCreditCardNotfound",
"description": "Seller credit card not found"
}
}
]
}
Option1 - Check using json syntax
Option2 - Iterating in a for-each loop over listings, checked for #[payload.error !=null]. But it gave error - Message payload is of type: LinkedHashMap
You can use jsonPath something like xpath but for JSON
I attached my example with the json provided. As you can see there are #[json:listings] which return array, this array will be iterated by foreach and then validate if contains error tag using #[json:error]. errorCount variable store the number of errors and it will be printed in the console.
<http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
<flow name="demoFlow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/" doc:name="HTTP"/>
<set-payload value="{"jobGuid":"123","status":"COMPLETED","listings":[{"exteralListingId":7654320},{"exteralListingId":7654321,"error":{"code":"inventory.listings.sellerCreditCardNotfound","description":"Seller credit card not found"}},{"exteralListingId":7654321,"error":{"code":"inventory.listings.sellerCreditCardNotfound","description":"Seller credit card not found"}},{"exteralListingId":7654321,"error":{"code":"inventory.listings.sellerCreditCardNotfound","description":"Seller credit card not found"}}]}" doc:name="Set Payload"/>
<expression-transformer expression="#[json:listings]" doc:name="Expression"/>
<set-variable variableName="errorCount" value="#[0]" doc:name="Variable"/>
<foreach collection="#[message.payload]" doc:name="For Each">
<expression-filter expression="#[json:error]" doc:name="Expression"/>
<set-variable variableName="errorCount" value="#[flowVars.errorCount + 1 ]" doc:name="Variable"/>
<logger message="counter: #[errorCount]" level="INFO" doc:name="Logger"/>
</foreach>
</flow>
For more information check the official documentation at mule .
http://www.mulesoft.org/documentation/display/current/JSON+Module+Reference