output of the json not formatted in APIM developer portal and HTTP response gives as follows also
HTTP/1.1 200 OK
api-supported-versions: 1.0,2.0
content-length: 1979
content-type: application/json; charset=utf-8
date: Tue, 06 Sep 2022 11:50:38 GMT
strict-transport-security: max-age=15552001; includeSubDomains; preload
in above it say content-type: application/json but out put as below (in the developer portal responses section content type also "application/json" )
{
"totalRowCount": 1,
"data": [
{
"Code": "",
"prCode": "CW1208",
"Name": "CW1208 Quarterly Milestones",
"description": "",
"CategoryCode": "Efficiency",
"dataSource": "",
"custodian": "",
"TimeframeCode": "Lag",
"TypeName": "Single",
"active": true,
"InputTypeCode": "Input",
"rOfficer": "Stephen MCKAY",
"rOfficerCode": "Stephen ",
"AggregationMethodCode": "Average",
"reportingPeriod": "Quarter(s)",
"responsibleOfficer": "Stephen ",
"unit": "%",
"method": "MOREISBETTER",
"datefrom": "2021-04-01T00:00:00",
"dateto": "2021-06-30T00:00:00",
"target": 100.00,
"actual": 0.00,
"variance": 0.00,
"performance": 0.000000,
"trafficLight": "OFFTRACK",
"comment": "Quarterly comment.."
}
]
}
Any idea how to fix this?
This is how I added the response
in the developer portal example output
all operation policies
<set-header name="Token" exists-action="skip">
<value>#(context.Request.OriginalUrl.Query.GetValueOrDefault("Token"))</value>
</set-header>
<choose>
<!--QA-->
<when condition="#(context.Subscription.PrimaryKey.Equals("8ug4ac4a02"))">
<set-backend-service base-url="https://xxxxx.com/API/api/v1/" />
</when>
<!--Other Clients-->
<otherwise>
<set-backend-service base-url="https://xxxxemo.xxxx.com/api/v1/" />
</otherwise>
</choose>
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<set-header name="X-Powered-By" exists-action="delete" />
<set-header name="X-AspNet-Version" exists-action="delete" />
<set-body template="none"></set-body>
<choose>
<when condition="#(context.Response.StatusCode == 404)">
<set-status code="200" reason="No Records Found" />
<set-body template="none">{"totalRowCount":0,"data":[{"":""}]}</set-body>
</when>
</choose>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Please set the response content type to application/json.
Outbound policy:
<outbound>
<set-header name="X-Powered-By" exists-action="delete" />
<set-header name="X-AspNet-Version" exists-action="delete" />
<set-body template="none"></set-body>
<choose>
<when condition="#(context.Response.StatusCode == 404)">
<set-status code="200" reason="No Records Found" />
<!--
<set-body template="none">{"totalRowCount":0,"data":[{"":""}]}</set-body>
-->
<set-body>#{
var body = new JObject();
body["totalRowCount"] = 0;
var data = new JArray();
var dataValue = new JObject();
dataValue[""] = "";
data.Add(dataValue);
body["data"] = data;
return body.ToString();
}</set-body>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
</when>
</choose>
<base />
</outbound>
Result:
Related
I need some help Parsing JSON Array in Azure APIM policy. My input to my API is a JSON Array but I want the output of the API to be JSON without the "[]", I just can't get the policy to remove them.
Here is the JSON Array input to my API:
[
{
"id": "myId",
"topic": "myTopic",
"subject": "/apis/test;Rev=1",
"data": {
"resourceUri": "myResourceUri"
},
"eventType": "Microsoft.ApiManagement.APIUpdated",
"dataVersion": "1",
"metadataVersion": "1",
"eventTime": "2022-09-08T14:22:46.7708654Z"
}
]
But I would like the Output of the policy to remove the square brackets [], like this:
{
"id": "myId",
"topic": "myTopic",
"subject": "/apis/test;Rev=1",
"data": {
"resourceUri": "myResourceUri"
},
"eventType": "Microsoft.ApiManagement.APIUpdated",
"dataVersion": "1",
"metadataVersion": "1",
"eventTime": "2022-09-08T14:22:46.7708654Z"
}
Here is my policy (which I copied from a tutorial and manipulated):
<policies>
<inbound>
<base />
<set-variable value="#(context.Request.Headers["Aeg-Event-Type"].Contains("SubscriptionValidation"))" name="isEventGridSubscriptionValidation" />
<set-variable value="#(context.Request.Headers["Aeg-Event-Type"].Contains("Notification"))" name="isEventGridNotification" />
<choose>
<when condition="#(context.Variables.GetValueOrDefault<bool>("isEventGridSubscriptionValidation"))">
<return-response>
<set-status code="200" reason="OK" />
<set-body>#{
var events = context.Request.Body.As<string>();
JArray a = JArray.Parse(events);
var eventGridData = a.First["data"];
var validationCode = eventGridData["validationCode"];
var jOutput =
new JObject(
new JProperty("validationResponse", validationCode)
);
return jOutput.ToString();
}</set-body>
</return-response>
</when>
<when condition="#(context.Variables.GetValueOrDefault<bool>("isEventGridNotification"))">
<send-one-way-request mode="new">
<set-url>https://hooks.slack.com/services/mySlackHandle</set-url>
<set-method>POST</set-method>
<set-body>#{
var events = context.Request.Body.As<string>();
JArray a = JArray.Parse(events);
var eventGridData = a.First["data"];
var song = eventGridData["song"];
return new JObject(
new JProperty("text", String.Format(" {1}",
song, a))).ToString();
}</set-body>
</send-one-way-request>
<return-response>
<set-status code="200" reason="OK" />
</return-response>
</when>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
ANY HELP GREATLY APPRECIATED! Extra points if you can remove the reference to "song", which I cannot without breaking the policy...
You have to convert your json array to a json object like this:
<set-body>#(Newtonsoft.Json.JsonConvert.SerializeObject((JObject)(context.Request.Body.As<JArray>(preserveContent: true))[0]))</set-body>
Your can remove the serialization if you want to keep the body as a json object. But the trick is to capture the body as a JArray and then use the [0] to choose the "first" object in the array and thereafter convert your body to a JObject.
I'm trying to build an API in azure and trying to modify the incoming json request using liquid template via set body policy. But the json elements are coming as null
Incoming json request
{
"GetOrderDetailsResponse": {
"OrderId": 1,
"OrderDate": "2018-08-13",
"OrderLines": {
"OrderLine": [
{
"Product": "Pizza Margherita",
"Amount": 5
},
{
"Product": "Pizza Calzone",
"Amount": 2
},
{
"Product": "Pizza Funghi",
"Amount": 1
}
]
}
}
}
Policy code
<policies>
<inbound>
<base />
<return-response response-variable-name="existing response variable">
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body template="liquid">
{
"orderId" : "{{body.GetOrderDetailsResponse.OrderId}}",
"orderDate" : "{{body.GetOrderDetailsResponse.OrderDate | Date: "dd/MM/yyyy"}}",
"orderLines" : [
{% JSONArrayFor orderline in body.GetOrderDetailsResponse.OrderLines %}
{
"product" : "{{orderline.Product}}",
"amount" : "{{orderline.Amount}}"
}
{% endJSONArrayFor %}
]
}
</set-body>
</return-response>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<!--Create JSON Response via liquid template-->
</outbound>
<on-error>
<base />
</on-error>
</policies>
Response is blank
{
"orderId": "",
"orderDate": "",
"orderLines": []
}
I'm new to this,please advise if i'm missing something obvious
As mentioned here in the official docs, the Content-Type header must be set to application/json for the body to parsed and available to the liquid template.
In this case, you could either make sure the request sent has it set or manually set it in the inbound scope (not inside return-response) using the set-header policy
Is it possible to apply compression (gzip) to a mocked response? Or is it limited to the outbound section? I tried to apply mocking in the outbound section but then I would need to "skip" the entire <backend> section as there is no backend yet..
This policy below works and I get a nice sample json back, but it's not compressed.
<policies>
<inbound>
<base />
<set-header name="Content-Encoding" exists-action="override">
<value>gzip</value>
</set-header>
<mock-response status-code="200" content-type="application/json" />
</inbound>
<backend />
<outbound />
<on-error>
<base />
</on-error>
</policies>
I actually got it to work using <return-response> instead. So if anyone in the future stumbles upon this, here is the complete policy:
<policies>
<inbound>
<base />
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-header name="Content-Encoding" exists-action="override">
<value>gzip</value>
</set-header>
<set-body>{
"success": true,
"data": {
"foo": [
{
"id": 1,
"bar": "xxx"
},
{
"id": 2,
"bar": "yyy"
},
{
"id": 3,
"bar": "zzz"
}
]
}
}</set-body>
</return-response>
</inbound>
<backend />
<outbound />
<on-error>
<base />
</on-error>
</policies>
I am working on API Management, the backend is ecured with oauth client credentials. If backend is off, I get the response of 200 and detail error as below.
{
"error": {
"name": "StatusCodeError",
"statusCode": 404,
"message": "HTTP Error 404. The requested resource is not found.",
"options": {
"url": "https://...net.au/api/case/mycases/",
"method": "GET",
"headers": {
"Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhb....."
},
"simple": true,
"resolveWithFullResponse": false,
"transform2xxOnly": false
},
"response": {
"statusCode": 404,
"body": "HTTP Error 404. The requested resource is not found.",
"headers": {
"content-length": "315",
"content-type": "text/html; charset=us-ascii",
"server": "Microsoft-HTTPAPI/2.0 Microsoft-HTTPAPI/2.0",
"date": "Fri, 14 Jun 2019 02:12:36 GMT",
"connection": "close"
},
"request": {
"uri": {
"protocol": "https:",
"slashes": true,
"auth": null,
"host": ".....",
"port": 443,
"hostname": "....net.au",
"hash": null,
"search": null,
"query": null,
"pathname": "/api/case/mycases/",
"path": "/api/case/mycases/",
"href": "https://...."
},
"method": "GET",
"headers": {
"Authorization": "Bearer eyJ0eXAiO....."
}
}
}
},
"status": 501
}
I only want to return the response like that when an api is called. and hide all extra details include access token.
{
"error": {
"name": "StatusCodeError",
"statusCode": 404,
"message": "HTTP Error 404. The requested resource is not found.",
}
}
as per the answer below, I have updated my policy, I am getting desired response when backend is offline but empty response when backend is online.
<choose>
<when condition="#{
var token = context.Response.Body.As<JToken>();
if (token is JObject){
return true;
}
return false;
}">
<return-response>
<set-status code="404" reason="NotFound" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>{
"error": {
"name": "StatusCodeError",
"statusCode": 404,
"message": "HTTP Error 404. The requested resource is not found.",
}
}</set-body>
</return-response>
</when>
</choose>
There are two cases when you can get 404 response from APIM. One is when you try to call an API/operation that APIM does not know of, in that case APIM will generate 404 response and it will be very short, much like your second one.
The other case is when APIM identifies API/operation, makes a call to backend and backend responds with 404. In that scenario APIM does not treat this as a problem, and merely relays backend response to client. What you have in your first example looks like something backend would reply with. You can confirm that by making a call from test console in Azure portal and inspecting provided trace.
So what you want to do is to replace body of 404 response with one of your choosing, that can easily be done with policies. At any scope put in something along these lines:
<outbound>
<base/>
<choose>
<when condition="#(context.Response.StatusCode == 404)">
<return-response>
<set-status code="404" reason="NotFound">
<set-header name="Content-Type">
<value>application/json</value>
</set-header>
<set-body>{
"error": {
"name": "StatusCodeError",
"statusCode": 404,
"message": "HTTP Error 404. The requested resource is not found.",
}
}</set-body>
</return-response>
</when>
</choose>
</outbound>
I am using Anypoint 6.1 and Mule 3.8.1 and am looking at extra error handling to the global exception handling created by the API kit.
I am looking to add:
408 Request Timeout
429 Too many requests
500 Internal Server Error
503 Service unavailable
What is the best approach to handle these errors?
I have looked to use the API Kit exception handling but I cannot find an expected type to use for the above. Does anyone know where I can find the expected types to use for these errors?
XML API Kit Exception mappings:
<apikit:mapping-exception-strategy name="apiKitGlobalExceptionMapping">
<apikit:mapping statusCode="400">
<apikit:exception value="org.mule.module.apikit.exception.BadRequestException" />
<set-property propertyName="Content-Type" value="application/json" doc:name="Property"/>
<set-payload value="{ "message": "Bad request" }" doc:name="Set Payload"/>
</apikit:mapping>
<apikit:mapping statusCode="404">
<apikit:exception value="org.mule.module.apikit.exception.NotFoundException" />
<set-property propertyName="Content-Type" value="application/json" doc:name="Property"/>
<set-payload value="{ "message": "Resource not found" }" doc:name="Set Payload"/>
</apikit:mapping>
<apikit:mapping statusCode="405">
<apikit:exception value="org.mule.module.apikit.exception.MethodNotAllowedException" />
<set-property propertyName="Content-Type" value="application/json" doc:name="Property"/>
<set-payload value="{ "message": "Method not allowed" }" doc:name="Set Payload"/>
</apikit:mapping>
<apikit:mapping statusCode="406">
<apikit:exception value="org.mule.module.apikit.exception.NotAcceptableException" />
<set-property propertyName="Content-Type" value="application/json" doc:name="Property"/>
<set-payload value="{ "message": "Not Acceptable" }" doc:name="Set Payload"/>
</apikit:mapping>
<apikit:mapping statusCode="408">
<apikit:exception value="java.util.concurrent.TimeoutException"/>
<set-property propertyName="Content-Type" value="application/json" doc:name="Property"/>
<set-payload value="{ "message": "Request Timeout" }" doc:name="Set Payload"/>
</apikit:mapping>
<apikit:mapping statusCode="415">
<apikit:exception value="org.mule.module.apikit.exception.UnsupportedMediaTypeException" />
<set-property propertyName="Content-Type" value="application/json" doc:name="Property"/>
<set-payload value="{ "message": "Unsupported media type" }" doc:name="Set Payload"/>
</apikit:mapping>
<apikit:mapping statusCode="429">
<apikit:exception value="java.lang.Exception"/>
<set-property propertyName="Content-Type" value="application/json" doc:name="Property"/>
<set-payload value="{ "message": "Unsupported media type" }" doc:name="Set Payload"/>
</apikit:mapping>
<apikit:mapping statusCode="500">
<apikit:exception value="java.lang.Exception"/>
<set-property propertyName="Content-Type" value="application/json" doc:name="Property"/>
<set-payload value="{ "message": "Unsupported media type" }" doc:name="Set Payload"/>
</apikit:mapping>
<apikit:mapping statusCode="503">
<apikit:exception value="java.lang.Exception"/>
<set-property propertyName="Content-Type" value="application/json" doc:name="Property"/>
<set-payload value="{ "message": "Unsupported media type" }" doc:name="Set Payload"/>
</apikit:mapping>
</apikit:mapping-exception-strategy>
Thanks
APIKIT matches the exception based on the apikit:exception value defined. So, it should be 1:1 for the status codes to the exception class defined. In your case, if you want to set 500, then have it to return only when the exception matches org.mule.module.apikit.exception.InternalServerErrorException