I define a full-name API that takes in First Name and Last Name and combine them to return the Full Name:
https://my-org.azure-api.net/my-app/full-name?firstName=John&lastName=Smith
# Returns: John Smith
In Azure APIM, I set both firstName and lastName parameters as "Required" in the API Schema.
In the Inbound policy, I want to validate that both are present in the request. I found the validate-parameters policy which appears to do just that:
<inbound>
<validate-parameters specified-parameter-action="prevent"
unspecified-parameter-action="detect"
errors-variable-name="validationErrors" />
</inbound>
To my surprise, this does absolution nothing! I have to run a manual check on each param (this code is so verbose):
<inbound>
<choose>
<when condition="#( context.Request.OriginalUrl.Query.GetValueOrDefault("firstName") == null )">
<return-response>
<set-status code="400" reason="Bad Request" />
<set-header name="Context-Type" exists-action="override">
<value>text/plain</value>
</set-header>
<set-body>'firstName' is required</set-body>
</return-response>
</when>
<when condition="#( context.Request.OriginalUrl.Query.GetValueOrDefault("lastName") == null )">
<return-response>
<set-status code="400" reason="Bad Request" />
<set-header name="Context-Type" exists-action="override">
<value>text/plain</value>
</set-header>
<set-body>'lastName' is required</set-body>
</return-response>
</when>
<otherwise>
</otherwise>
</choose>
</inbound>
Is there any more convenient way to validate the request against the API's schema in Azure APIM?
We can find the document shows us we need to use api-version which later than 2021-01-01-preview to create the api in APIM, then the policy can work.
So if your api in APIM was created in past, you need to create it again. I checked the create api operation on azure portal, it has been updated to using api-version=2021-01-01-preview shown as below screenshot.
You can also request the api to create it by yourself.
Related
We have existing production services which are based on this policy sample which acquires a bearer token via client grant flow. However we want to switch the client application settings to a new app, which requires updating 4 named values. How can this be done with high throughput traffic volumes?
We have updated named value pairs as documented and we've looked at the REST API. There is only 1-by-1 update of values. We applied this on lower volume environments with no problems. However when we applied this on production environments we have outages with the expression unable to parse the value, which results in a null and caused an outage.
This was the policy, copied from the portal.
<choose>
<when condition="#(!context.Variables.ContainsKey("access_token"))">
<send-request ignore-error="true" timeout="20" response-variable-name="response" mode="new">
<set-url>{{authorizationServer}}</set-url>
<set-method id="apim-generated-policy">POST</set-method>
<set-header name="Content-Type" exists-action="override">
<value>application/x-www-form-urlencoded</value>
</set-header>
<set-body>#{return "client_id={{clientid}}&resource={{scope}}&client_secret={{clientsecret}}&grant_type=client_credentials";}</set-body>
</send-request>
<set-variable name="responseJson" value="#(((IResponse)context.Variables["response"]).Body.As<JObject>())" />
<set-variable name="access_token" value="#("Bearer " + (String)((JObject)context.Variables["responseJson"])["access_token"])" />
<set-variable name="expires_in" value="#((int)((JObject)context.Variables["responseJson"])["expires_in"] - 20)" />
<cache-store-value key="access_token" value="#((string)context.Variables["access_token"])" duration="#(((int)context.Variables["expires_in"]))" />
</when>
</choose>
<set-header name="Authorization" exists-action="override">
<value>#((string)context.Variables["access_token"])</value>
</set-header>
"Elapsed": 109,
"Source": "set-variable[3]",
"Reason": null,
"Message": "Expression evaluation failed. Value cannot be null.\r\nParameter name: value\r\n at Newtonsoft.Json.Linq.JToken.EnsureValue(JToken value)\r\n at Newtonsoft.Json.Linq.JToken.op_Explicit(JToken value)",
"Scope": "product",
"Section": "inbound",
"Path": "choose\\when[1]",
"PolicyId": "",
"TransportErrorCode": 0,
"HttpErrorCode": 0
}
I suspected that the issue was related to one of these variables being null but our logs were unclear as to which variable.
There is no API to anatomically update multiple named value properties. There are two approaches that come to mind. You could store all your values in a single named value encoding them into a single string and parsing out inside policy.
Or you could have two sets of named values and a fifth one that controls which one to use. This way you'll be free to update your passive set freely and only activate it later.
In Azure API Management, when the response going back to the client is a 500, I wish to check the body of the response to see if it matches "Some text". I need to do this so that I may change the body of the response to contain some more helpful text in this particular scenario.
The following <outbound> section of my policy is accepted by the API Management console, but when I test and get a 500, API Management generates an error -
Expression evaluation failed. Unable to cast object of type 'Microsoft.WindowsAzure.ApiManagement.Proxy.Gateway.MessageBody' to type 'System.String'.
I'm guessing this is my fault, but does anybody know how I can amend the ploicy so that it does not generate an error? To clarify, the error is being generated by this line - ((string)(object)context.Response.Body == "Some text").
<outbound>
<choose>
<when condition="#((context.Response.StatusCode == 500) && ((string)(object)context.Response.Body == "Some text"))">
<set-status code="500" reason="Internal Server Error" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>
{
"statusCode": "500",
"Message": "Some different, more helpful text."
}
</set-body>
</when>
</choose>
</outbound>
Update
I've discovered that context.Response.Body is of type IMessageBody. There seems to be woefully little documentation around this type, and the only reference I can find comes under <set-body> in the Transformation Policies API management documentation.
The troube is, the example that MS havd documented produces an exception when I try and save my policy -
<set-body>
#{
JObject inBody = context.Request.Body.As<JObject>();
if (inBody.attribute == <tag>) {
inBody[0] = 'm';
}
return inBody.ToString();
}
</set-body>
Property or indexer 'string.this[int]' cannot be assigned to -- it is read only
Try context.Request.Body.As<string>(). Method As currently supports following types as generic argument value:
byte[]
string
JToken
JObject
JArray
XNode
XElement
XDocument
Mind that if you try to call .As<JObject> over response that does not contain valid JSON you would get an exception, same applies to other types as well.
I have created an API in the WSO2 ESB (4.8.1) and I wanted to send a PUT request to that API with a request body. I have tried with the sample
and I wanted to log a property values in the insequence of the defined API.
This is my request body:
This is the way how I tried to log the location name:
But I’m getting an error like this:
(ERROR - SynapseJsonPath #stringValueOf. Error evaluating JSON Path . Returning empty result. Error>>> invalid path)
So how can I read these values?
To achieve your requirement, you should send the "Content-Type" HTTP header with the request like below,
"Content-Type : application/json"
enter image description here
Then you can log the specific JSON element like below.
<log>
<property name="location" expression="json-eval($.coordinates.location[0].name)"></property>
</log>
Then you can see following log,
enter image description here
Thanks.
If you want to get single variable from user in json request you can use this code
Use This json:
{
"namee":"UsmanYaqooooooooooob"
}
Api Code:
<api xmlns="http://ws.apache.org/ns/synapse" name="Hello" context="/hello">
<resource methods="POST" uri-template="/post">
<inSequence>
<log level="custom">
<property name="===========inSequence" value="****"></property>
<property name="locationsssssssss" expression="json-eval(.namee)"></property>
</log>
<payloadFactory media-type="json">
<format>{"hello":"world"}</format>
<args></args>
</payloadFactory>
<property name="messageType" value="text/xml"></property>
<respond></respond>
</inSequence>
</resource>
</api>
I have a JSON response which is like {"id":10,"name":"ABCD","deptId":0,"address":null}
I need to split this JSON and extract the id to pass on to another service.
My mule xml is as below
<jersey:resources doc:name="REST">
<component class="com.employee.service.EmployeeService"/>
</jersey:resources>
<object-to-string-transformer doc:name="Object to String"/>
<logger message="Employee Response #[payload]" level="INFO" doc:name="Logger"/>
<set-payload value="#[payload]" doc:name="Set Payload" />
<json:object-to-json-transformer doc:name="Convert String to JSON" />
<logger message="JSON Response #[payload]" level="INFO" doc:name="Logger"/>
<json:json-to-object-transformer returnClass="java.util.Map" />
<expression-transformer expression="#[payload]" />
<collection-splitter />
When I run this I get the error
Object "java.util.LinkedHashMap" not of correct type. It must be of type "{interface java.lang.Iterable,interface java.util.Iterator,interface org.mule.routing.MessageSequence,interface java.util.Collection}" (java.lang.IllegalArgumentException). Message payload is of type: LinkedHashMap
How can I fix this error?
Thanks
I was able to get this done by writing a custom converter
remove your last four lines of code. set logger #[payload.id] in flowvars and access it
I believe the error you are getting is already on this part, <collection-splitter />. Have you debugged this already?
Not sure what the splitter is for but you can simply do #[payload.id] to get id once you have a HashMap type of payload.
The JSon module as well has the ability to use jsonpath in expressions, such as:
#[json:/id]
i want add some extra information on incomming Pojo, i have used message enricher in mule and do that, here is my full flow. I am using subflow to get the payload and select some values in DB and set that value in same pojo and returning, while in target i am setting as payload but i am getting error like this "An Expression Enricher for "payload" is not registered with Mule."
here is mu flow
<enricher doc:name="Message Enricher">
<core:flow-ref name="flows1Flow1" doc:name="Flow Reference"/>
<enrich source="#[groovy:payload]" target="#[payload]"/>
<logger message="AFTER Enrich: #[payload]" level="INFO" doc:name="Logger"/>
<component class="com.enrich.AfterEnricher" doc:name="Java"/>
<sub-flow name="flows1Flow1" doc:name="flows1Flow1">
<component class="com.enrich.MessageEnrichPattern" doc:name="Java"/>
<jdbc:outbound-endpoint exchange-pattern="request-response" queryKey="selectData" connector-ref="jdbcConnector" doc:name="Database (JDBC)">
<jdbc:query key="selectData" value="SELECT Username, Password, ModuleId from Credentials where ModuleId=#[map-payload:moduleId]"/>
</jdbc:outbound-endpoint>
<logger message="#[payload]" level="INFO" doc:name="Logger"/>
<component class="com.enrich.ReceiveMessageEnrichPattern" doc:name="Java"/>
</sub-flow>
here ReceiveMessageEnrichPattern returing
Credential credential = new Credential();
credential.setUname(hashMap.get("USERNAME").toString());
credential.setPwd(hashMap.get("PPPP").toString());
credential.setMid(hashMap.get("MODULEID").toString());
return credential;
but in after enrich component i am getting exception. Please help me how can enrich my incoming pojo with extra info can add.
According to the docs, Mule currently only supports two targets for enrichment:
flow variables,
message headers.
To achieve your goal you need to:
store the enricher result (Credential object) in a flow variable,
use a custom transformer to copy the values from the Credential object found in the flow variable to the POJO payload in your main flow.