Azure API Gateway Policy - azure-api-management

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>

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

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

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>

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.

How to validate content type=JSON in Mule

I have a Mule config in which there is 2 flows :-
One flow expose a REST Service :-
<flow name="restServiceFlow1" doc:name="restFlow1">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8082" doc:name="HTTP"/>
<jersey:resources doc:name="REST">
<component class="com.test.services.schema.maindata.v1.Impl.MainDataImpl"/>
</jersey:resources>
</flow>
and another flow that consume the service by placing JSON request through file inbound :-
<flow name="restFlow2">
<file:inbound-endpoint path="E:\backup\test" responseTimeout="10000" connector-ref="File_Global">
<file:filename-regex-filter pattern="aa.txt" caseSensitive="false"/>
</file:inbound-endpoint>
<json:json-to-object-transformer returnClass="java.util.HashMap"/>
<foreach collection="#[payload.insertDataRequest]">
<http:outbound-endpoint exchange-pattern="request-response"
contentType="application/json" method="GET"
address="http://localhost:8082/getData/insert/?id=#[payload.id]&name=#[payload.name]&age=#[payload.age]&designation=#[payload.designation]"/>
</foreach>
</flow>
Now requirement to to check the content type after the file inbound endpoint whether the content type is JSON ... if the content Type is not equal to JSON then it will show not JSON message in log ..
I have tried the following :-
I placed a choice router after File inbound endpoint :-
<when evaluator="groovy" expression="payload.ContentType=='JSON'">
to check the content type the payload and if the content type is not JSON it will show not JSON in log and so I placed the log in Default of choice router ... But I am getting following exception :-
Exception stack is:
1. No such property: ContentType for class: org.mule.transport.file.ReceiverFileInputStream (groovy.lang.MissingPropertyException)
org.codehaus.groovy.runtime.ScriptBytecodeAdapter:50 (null)
2. groovy.lang.MissingPropertyException: No such property: ContentType for class: org.mule.transport.file.ReceiverFileInputStream (javax.script.ScriptException)
org.codehaus.groovy.jsr223.GroovyScriptEngineImpl:323 (http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/script/ScriptException.html)
Now is there any better way to check the content type after file inbound endpoint ??? please suggest some better way ...Please note I don't want to use is-json-filter because I want to control the else condition and display message in log ...
You can still use the is-json-filter but you need to wrap it in a message filter so you can control the "else" path:
<message-filter onUnaccepted="noJsonFlow" throwOnUnaccepted="false">
<json:is-json-filter />
</message-filter>

Why is Mule exception strategy so chatty?

In my Mule app, I've configured some of the flows to use a catch exception strategy in order to do some special processing. For these cases, I want to pop the error and the original payload into an object store. Everywhere else, the default exception strategy is fine.
<flow name="saveLookup">
<vm:inbound-endpoint exchange-pattern="one-way" ref="Lookup_Save_VM" />
<component>
<spring-object bean="insertLookupMDCvalues"/>
</component>
<set-variable variableName="originalPayload" value="#[payload]"/>
<json:json-to-object-transformer returnClass="com.company.LookupData"/>
<set-variable variableName="transactionId" value="#[payload.transactionId]"/>
<transactional action="ALWAYS_BEGIN">
<logger message="${lookup.SQL}" level="INFO"/>
<jdbc:outbound-endpoint exchange-pattern="request-response" queryKey="saveLookup" queryTimeout="-1" connector-ref="JdbcConnector" />
<foreach collection="#[payload.transactional.lookupItems.items]">
<logger message="${lookup.item.SQL}" level="INFO" />
<jdbc:outbound-endpoint exchange-pattern="request-response" queryKey="saveLookupItem" queryTimeout="-1" connector-ref="JdbcConnector"/>
</foreach>
</transactional>
<component>
<spring-object bean="clearLookupMDCvalues"/>
</component>
<catch-exception-strategy>
<message-properties-transformer scope="invocation">
<add-message-property key="errorMap" value="#[['id' : transactionId, 'body' : originalPayload, 'error' : exception.summaryMessage]]"/>
</message-properties-transformer>
<choice>
<when expression="#[message.inboundProperties['resubmit']]">
<logger message="Resubmission of lookup data failed, saving to Dead Letter object store. ID=#[transactionId]" level="INFO"/>
<objectstore:store config-ref="lookupDeadLetterOS" key="#[transactionId]" overwrite="true" value-ref="#[errorMap]"/>
</when>
<otherwise>
<logger message="Saving lookup data failed, saving to Error object store. ID=#[transactionId]" level="INFO"/>
<objectstore:store config-ref="lookupErrorOS" key="#[transactionId]" overwrite="true" value-ref="#[errorMap]"/>
</otherwise>
</choice>
<set-payload value="Error: #[exception.summaryMessage]"/>
<component>
<spring-object bean="clearLookupMDCvalues"/>
</component>
</catch-exception-strategy>
</flow>
My problem is that when an error is encountered, let's say a Null Pointer Exception in the foreach component, I'm seeing four ERROR log statements for each event:
Exception stack is: 1. null (java.lang.NullPointerException) ...and so on. This is logged twice.
CatchMessagingExceptionStrategy - Message : Execution of the expression "payload.transactional.lookupItems.items" failed. (org.mule.api.expression.ExpressionRuntimeException). Message payload is of type: LookupData
DefaultMessagingExceptionStrategy - Message : Execution of the expression "payload.transactional.lookupItems.items" failed. (org.mule.api.expression.ExpressionRuntimeException). Message payload is of type: LookupData
I thought that a flow-specific exception strategy should override the default strategy. Why the duplicate log messages, and is there a way to shush them? I'd like to avoid having to configure the default exception strategy, as it's perfectly acceptable behavior in the majority of the flows.
The issue is that the built in exception strategies inherit from AbstractExceptionListener, and they all use the logException template method. The base implementation always logs at ERROR level, which is sometimes not appropriate for your application.
You can create a simple subclass of CatchMessagingExceptionStrategy that overrides the logException method, and logs however you want. Then, use it in your flow in place of the <catch-exception-strategy> like so:
<custom-exception-strategy class="com.mycompany.mule.QuietCatchExceptionStrategy">
<!-- your message processors here -->
</custom-exception-strategy>