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
Related
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>
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>
While using MULE, I convert payload received from HTTP request to json object and mapped it to variables. In case, when I do not pass one of the variable, I get exceptions while evaluating the expression.
<flow name="test">
<http:listener config-ref="HTTP_Listener_Configuration" path="/" doc:name="HTTP"/>
<json:json-to-object-transformer returnClass="java.lang.Object" doc:name="JSON to Object"/>
<set-variable variableName="a" value="#[message.payload.a]"
<set-variable variableName="b" value="#[message.payload.b]"
<logger message="a: #[a]" level="INFO" doc:name="Logger"></logger>
</flow>
When the payload is
{
"a": "val1",
"b": "val2"
}
The applications runs fine.
But if the payload is
{
"b": "val2"
}
I get the following exception:
ERROR [].HTTP_Listener_Configuration.worker.01] org.mule.exception.DefaultMessagingExceptionStrategy:
********************************************************************************
Message : Execution of the expression "a" failed. (org.mule.api.expression.ExpressionRuntimeException).
Payload : {b="val2"}
Payload Type : java.util.LinkedHashMap
Element : /test/processors/10 # test:test.xml:47 (Logger)
Element XML : <logger message="a: #[a]" level="INFO" doc:name="Logger"></logger>
--------------------------------------------------------------------------------
Root Exception stack trace:
[Error: unresolvable property or identifier: a]
[Near : {... a ....}]
Can I check and append the value of variable 'a' as NULL ?
P.S. I would like to use this variable for sending as one of the parameters in payload for some other http request.
using #[flowVars.a] instead of #[a] will result in null instead of a exception.
Something is not right with your implementation. After the http listener, the payload is a raw response(JSON), which in any way cannot be access by #[payload.a] or #[payload['a']]. You should add an json to Object(which converts the json to map). From here you could reference it like this #[payload['a']] or #[payload.a]
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.
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