I am using Azure API Management REST API's to import a WSDL and convert it to a REST endpoint calling SOAP backend.
However when you import the WSDL all the methods are imported as POST (which makes sense since you need to send the soap envelope). Now I want to convert the operation from POST to GET via the REST API (which I can do through portal).
Has anyone tried that before, and if yes which API's should I call?
For this case I used a public soap service:
https://www.dataaccess.com/webservicesserver/numberconversion.wso?op=NumberToDollars
I imported this SOAP service to API Management:
Request-Body:
<?xml version="1.0" encoding="utf-8"?>
<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope">
<Body>
<NumberToDollars xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.dataaccess.com/webservicesserver/">
<dNum>1</dNum>
</NumberToDollars>
</Body>
</Envelope>
The NumberToDollars operation has to read the XML, transform it into JSON, and pass the data to a GET API.
For testing purposes I created an mocked Operation which returns 200:
https://rfqapiservicey27itmeb4cf7q.azure-api.net/echo/200/test
<policies>
<inbound>
<base />
<set-variable name="num" value="#{
string xml = context.Request.Body.As<string>(preserveContent: true);
xml = Regex.Unescape(xml);
// Remove the double quotes
xml = xml.Remove(0,1);
xml = xml.Remove(xml.Length-1,1);
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
var data = JObject.Parse(JsonConvert.SerializeObject(doc));
JObject envelope = data["Envelope"] as JObject;
JObject body = envelope["Body"] as JObject;
JObject numberToDollars = body["NumberToDollars"] as JObject;
return numberToDollars["dNum"].Value<string>();
}" />
<set-method>GET</set-method>
<set-backend-service base-url="https://rfqapiservicey27itmeb4cf7q.azure-api.net/echo/200/" />
<rewrite-uri template="#("/test?q=" + context.Variables.GetValueOrDefault<string>("num"))" copy-unmatched-params="false" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Testing the operation:
The trace log:
The JSON document generated from XML:
{
"?xml": {
"#version": "1.0",
"#encoding": "utf-8"
},
"Envelope": {
"#xmlns": "http://www.w3.org/2003/05/soap-envelope",
"Body": {
"NumberToDollars": {
"#xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
"#xmlns": "http://www.dataaccess.com/webservicesserver/",
"dNum": "1"
}
}
}
}
Related
I had an issue with validate-content policy our API, so for sharing in public
I created a simple Open API Spec yaml file with a schema to create an API and then added validate-content policy. It's validating as expected when a JSON when the right JSON is sent or if I miss any required fields it's preventing with right validation error. However when I send a completely unrelated JSON it's going through fine, expectation is to prevent otherwise it will break the policy expressions.
As suggested in one of the other threads, I also tried adding request representation and specified message schema but behaviour is same.
I also verified it on https://www.jsonschemavalidator.net/, where validation is as expected
I am not sure it's ignoring as a fact that JSON validation ignores extra elements
Contents of the YAML used
openapi: "3.0.0"
info:
title: address-schema-validation
description: This is a loop back API to test schema validation
version: '1.0'
license:
name: MIT
paths:
/validate:
post:
summary: Submit a request for validation
operationId: validate
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/AddressBody"
responses:
'200':
description: valid payload
content:
application/json:
schema:
$ref: "#/components/schemas/AddressBody"
components:
schemas:
AddressBody:
type: object
properties:
address:
type: string
example: "01 Auckland"
name:
type: object
required:
- fistName
- lastName
properties:
fistName:
type: string
example: Fist Name
lastName:
type: string
example: Last Name
Policy
<policies>
<inbound>
<base />
<validate-content unspecified-content-type-action="prevent" max-size="102400" size-exceeded-action="prevent" errors-variable-name="requestBodyValidation">
<content type="application/json" validate-as="json" action="prevent" />
</validate-content>
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>#(context.Request.Body.As<String>())</set-body>
</return-response>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
JSON that picks up the validation error
{
"address": "01 Auckland",
"name": {
"fistName1": "Fist Name",
"lastName": "Last Name"
}
}
with error message
{
"statusCode": 400,
"message": "Body of the request does not conform to the definition which is associated with the content type application/json. Required properties are missing from object: fistName. Line: 6, Position: 5"
}
The following JSON is expected to be prevented but it's not
{"prevent":"me"}
In fact validation is being done as expected, to prevent unexpected JSON, the schema should have a required root element, if the root element is optional then when completely unrelated JSON is posted the validation policy will simply ignore the unexpected elements.
I have put together a file uploader with HTTP4 to push data to HTTP server environments. there are 2 systems, 1 for CSV data files and 1 for JSON delivery. sending a CSV to the CSV system works fine. however, converting the data to JSON and sending to the JSON system fails with the below exception. the error is intuitive, however, I don't really know what to do about it.
Does JSON not use multipart form data and should specifically be an IO Stream? being my first HTTP service I'm at a lose of protocol acceptance. apparently the JSON system does not expect Multipart data, I'm not sure what a Json http send should be?? thank you for your help!
Caused by: org.apache.camel.InvalidPayloadException: No body available of type: java.io.InputStream but has value: org.apache.http.entity.mime.MultipartFormEntity
Caused by: No type converter available to convert from type: org.apache.http.entity.mime.MultipartFormEntity to the required type: java.io.InputStream with value org.apache.http.entity.mime.MultipartFormEntity
here is the uploader class that works for CSV data files but not for Json?
LOG.info("Uploading File for CustKey: " + custKey + " and Tenant: " + tenant);
StringBuilder authHeader = new StringBuilder("Bearer ");
authHeader.append(token);
LOG.info("Authorization: " + authHeader.toString());
exchange.setProperty("CamelCharsetName", "UTF-8"); //"CamelCharsetName" Exchange.CHARSET_NAME
exchange.getIn().setHeader("CamelHttpCharacterEncoding", "UTF-8"); //"CamelHttpCharacterEncoding" Exchange.HTTP_CHARACTER_ENCODING
exchange.getIn().setHeader("CamelAcceptContentType", "application/json"); //"CamelAcceptContentType" Exchange.ACCEPT_CONTENT_TYPE
exchange.getIn().setHeader("CamelHttpUri", uploadUrl); //"CamelHttpUri" Exchange.HTTP_URI
exchange.getIn().setHeader("CamelHttpMethod", "POST"); //"CamelHttpMethod" Exchange.HTTP_METHOD
exchange.getIn().setHeader("x-ge-csvformat", "ODB");
exchange.getIn().setHeader("Tenant", tenant);
// exchange.getIn().setHeader("Content-Type", "multipart/form-data"); //"Content-Type" ; boundary=
exchange.getIn().setHeader("Authorization", authHeader.toString());
// Process the file in the exchange body
File file = exchange.getIn().getBody(File.class);
String fileName = (String) exchange.getIn().getHeader(Exchange.FILE_NAME);
LOG.info("fileName: " + fileName);
MultipartEntityBuilder entity = MultipartEntityBuilder.create();
entity.addBinaryBody("file", file);
entity.addTextBody("name", fileName);
exchange.getIn().setBody(entity.build()); //*** use for CSV uploads
HERE IS the route, the only difference in the route is the JsonMapper process
<route
id="core.predix.upload.route"
autoStartup="false" >
<from uri="{{uploadEntranceEndpoint}}" />
<process ref="customerEntitesProcessor" /> <!-- sets up the message with the customer environment entities to upload data -->
<process ref="customerTokenProcessor" /> <!-- sets up the message with the cusotmer's token -->
<process ref="jsonMapper" />
<to uri="{{jsonEndpoint}}" />
<process ref="uploadProcessor" /> <!-- conditions the message with the HTTP header info per customer env -->
<setHeader headerName="CamelHttpUri">
<simple>${header.UPLOADURL}?throwExceptionOnFailure=false</simple>
</setHeader>
<setHeader headerName="CamelHttpMethod">
<constant>POST</constant>
</setHeader>
<to uri="http4://apm-timeseries-query-svc-prod.app-api.aws-usw02-pr.predix.io:443/v2/time_series/upload?throwExceptionOnFailure=false" />
<log message="After POSTING JSON: ${body}" loggingLevel="INFO"/>
<to uri="{{afteruploadLocation}}" />
<!-- <log message="JSON Route: ${body}" loggingLevel="INFO"/> -->
<!-- <to uri="{{jsonEndpoint}}" /> -->
</route>
aparently sending json is as an IO Stream. Multipart form data Content-type is for file transfers only, and system is expecting Multipart form data as Content-Type.
to get this to work I am simply sending data as IO Stream and changed the Content-Type to application/json.
Code changed to, to send Json.
StringBuilder authHeader = new StringBuilder("Bearer ");
authHeader.append(token);
LOG.info("Authorization: " + authHeader.toString());
exchange.setProperty("CamelCharsetName", "UTF-8"); //"CamelCharsetName" Exchange.CHARSET_NAME
exchange.getIn().setHeader("CamelHttpCharacterEncoding", "UTF-8"); //"CamelHttpCharacterEncoding" Exchange.HTTP_CHARACTER_ENCODING
exchange.getIn().setHeader("CamelAcceptContentType", "application/json"); //"CamelAcceptContentType" Exchange.ACCEPT_CONTENT_TYPE
exchange.getIn().setHeader("CamelHttpUri", uploadUrl); //"CamelHttpUri" Exchange.HTTP_URI
exchange.getIn().setHeader("CamelHttpMethod", "POST"); //"CamelHttpMethod" Exchange.HTTP_METHOD
exchange.getIn().setHeader("x-ge-csvformat", "ODB");
exchange.getIn().setHeader("Tenant", tenant);
exchange.getIn().setHeader("Content-Type", "application/json");
exchange.getIn().setHeader("Authorization", authHeader.toString());
//*** use for CSV uploads - uncomment all commented lines for CSV file upload
// Process the file in the exchange body
// File file = exchange.getIn().getBody(File.class);
// String fileName = (String) exchange.getIn().getHeader(Exchange.FILE_NAME);
// LOG.info("fileName: " + fileName);
// MultipartEntityBuilder entity = MultipartEntityBuilder.create();
// entity.addBinaryBody("file", file);
// entity.addTextBody("name", fileName);
// exchange.getIn().setBody(entity.build());
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'm attempting to call the smartsheet.com api and read the JSON data from a list sheet request. Im new to API's so I'm certain I'm missing much with my code.
Here is what I have so far:
<cfscript>
apiURL = "https://api.smartsheet.com/2.0/sheets";
apiToken = "xxxxxxxxxxxxxxxxxxxxxxxxx";
</cfscript>
<cfhttp url="#apiURL#" method="GET" result="httpResp" timeout="120" charset="utf-8">
<cfhttpparam type="header" name="Authorization" value="Bearer #apiToken#" />
</cfhttp>
However I do not receive the desired response:
I was attempting to use the sample provided to retrieve the data:
SmartSheet API 2.0
Example Request:
curl https://api.smartsheet.com/2.0/sheets -H "Authorization: Bearer ACCESS_TOKEN"
Example Response:
{
"pageNumber":1,
"pageSize":100,
"totalPages":1,
"totalCount":2,
"data":[
{
"accessLevel":"OWNER",
"id":4583173393803140,
"name":"sheet 1",
"createdAt":"2015-06-05T20:05:29Z",
"modifiedAt":"2015-06-05T20:05:43Z"
},
{
"accessLevel":"OWNER",
"id":2331373580117892,
"name":"sheet 2",
"createdAt":"2015-06-05T20:05:29Z",
"modifiedAt":"2015-06-05T20:05:43Z"
}
]
}
What version of CF? Looks similar to this issue: ColdFusion 9.0.1 - 3574332 CHTTP returns filecontent as java.io.ByteArrayOutputStream when mimetype is application/json. The workaround is to either:
Set the CFHTTP attribute getasbinary="never" OR
Convert the returned fileContent object into a string using:
<cfset rawJSONString = httpResp.fileContent.toString()>
I am posting data to an Action, get json data as a result, and then, process the result in javascript.
It's work well on dev server, but when i publish it on Azure WebApp, when I post my data, it display the json on the screen.
The begining of my Form to post data :
#using (Ajax.BeginForm("UpdateAccount", "Loging",
new AjaxOptions
{
HttpMethod = "POST",
OnSuccess = "processAccountUpdateResult"
}))
{
Note: processAccountUpdateResult is the javascript function which process the json result.
Here is my action :
[HttpPost]
public ActionResult UpdateAccount(string newName)
{
return Json(new { result = "ok" }, "text/html", JsonRequestBehavior.AllowGet);
}
Note : I added the "text/html" mime type after I read this
But no luck.
I tried to add the json mimetype in the web.config, but still no luck :
<configuration>
<system.webServer>
<staticContent>
<mimeMap fileExtension=".json" mimeType="application/json"/>
</staticContent>
</system.webServer>
</configuration>
Thanks
To make it working, we need to add this to the web.config :
<add key="UnobtrusiveJavaScriptEnabled" value="true" />