I'm trying to connect an API endpoint in Azure (api management) to a backend service. However, the set-body policy isn't recognizing my JSON body and thus isn't transforming it for the backend call.
I've tried all iterations i can think of for the "Liquid" and "None" templates. The microsoft documentation is useless as even the "liquid" template is capitalized in the doc while it NEEDS to be lowercase. Even the Deep Dive article that everyone points to is misleading and/or out of date.
i was once able to get the {{context.Request.OriginalUrl}} reference to work using liquid template, but i can't seem to get the {{body.json}} reference to work
Here's the policy i have in the section (purely to test - this has no use for what i'm doing):
<set-body template="liquid">
Calling User Agent: {{context.Request.OriginalUrl}}
</set-body>
And here's an example of what i have to try to read the json body (passing through via POST):
<set-body template="liquid">{{body}}</set-body>
I've tried several iterations and inputs like below:
<set-body template="liquid">{{body.json}}</set-body>
while passing through a body like this:
{"json":"this is an example body"}
No matter what I do, this is what I see in the trace after testing the call:
set-body (0.069 ms)
{
"input": null,
"output": ""
}
i'm obviously open to using the "none" template, but i run into the same problems. The documentation is wrong - if i copy/paste the example:
<set-body>#(context.Body.As<String>())</set-body>
I get errors like:
One or more fields contain incorrect values:
Error in element 'set-body' on line 32, column 10: 'IProxyRequestContext' does not contain a definition for 'Body' and no extension method 'Body' accepting a first argument of type 'IProxyRequestContext' could be found (are you missing a using directive or an assembly reference?)
and when i do get it to not error, it returns the same "output":"" output.
For being able to access the body as an object in the liquid template, you will have to set the Content-Type header to application/json as mentioned in the docs.
If your requests are already sending this header, then it should work without setting it too.
A policy like this in the inbound section will guarantee it works as expected
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body template="liquid">{{body.json}}"}</set-body>
As for accessing it via the context variable, you have to access it as context.Request.Body.As<string>() as mentioned in the docs, so something like this
<set-body>#(context.Request.Body.As<string>())</set-body>
The official reference for set-body doesn't seem to have the problems you've mentioned.
Was there any other doc you are referring to? If its on learn.microsoft.com, you could open an issue at the end of each doc.
Related
I'm trying to update the secret config of vault. I try this using the node-fetch library. To avoid repetition, I've wrapped the types of requests (LIST, GET, POST). These wrappers work with the pki so it is safe to assume that it has nothing to do with the headers of the requests. In my POST wrapper, I pass a JSON Object as a parameter. Following the documentation of vault, this should look something like this:
{
'max_versions': 3,
'cas_required': false,
'delete_version_after': '0s'
}
Vault gives me an error saying invalid character 'o' looking for beginning of value. I can't find the mistake I made since I am passing a JSON Object.
Could someone point me into the right direction of what I am doing wrong?
I want to make a RESTful API in my CakePHP application however, the only way it describes is using extensions (a.k.a file extensions) https://book.cakephp.org/3.0/en/development/routing.html#creating-restful-routes but this isn't feasible for me considering that I actually have JSON files that I do not wish to get confused with CakePHP, not only that but adding .json or whatever to the end of a path is likely to be missed and omitting it does not change that it will actually go there causing an error to show.
Is there a way to create RESTful routes without using extensions?
Extensions are optional
Extensions are by no means required for RESTful routes to work. Extensions are part of how the request handler component configures the rendering and response process, the routes themselves will work just fine without specifying extensions.
It looks like the docs are kind of outdated, the sentence describing the code example doesn't make any sense:
The first line sets up a number of default routes for easy REST access where method specifies the desired result format (e.g. xml, json, rss). These routes are HTTP Request Method sensitive.
I guess this belongs to an older code example. You may want to report this over at GitHub.
Use the Accept header
That being said, the request handler component also evaluates the Accept header, so you could make sending application/json the requirement for your API.
Also if you don't want to accept non-JSON requests at all, then you should check Request::is() and throw an exception accordingly.
if (!$this->request->is('json')) {
throw new \Cake\Network\Exception\BadRequestException():
}
Hardcode the components behavior
Furthermore it's possible to overwrite the extension that the request handler determines, and make the component think this is a JSON request:
$this->RequestHandler->ext = 'json';
It should be noted that this won't affect methods like RequestHandler::prefers()!
And finally you can also use the RequestHandler::renderAs() method to tell the request handler how to render and respond:
$this->RequestHandler->renderAs($this, 'json');
This however would need to be done in the Controller.beforeRender event in order to override the components behavior in case it identifies a request of a type that it is normally ment to handle.
See also
Cookbook > Controllers > Request & Response Objects > Checking Request Conditions
Cookbook > Controllers > Components > Request Handling > Responding To Requests
API > \Cake\Controller\Component\RequestHandlerComponent::$ext
I am trying to publish a json message using the Amazonsns connector in WSO2 ESB 4.9. I am able to successfully publish a simple string message however when I set the messagestructure to json in order to send different messages to different platforms and attempting to send json as the value of message it will not work. I am using a simple transaction that looks almost exactly like the documentation sample. My Transaction:
content-type: application/json;charset=UTF-8
{
"region":"us-west-2",
"accessKeyId":"MyAccessKey",
"secretAccessKey":"MySecretAccessKey",
"version":"",
"messageStructure":"json",
"subject":"Test",
"message": {"default":"mess","email":"message"},
"targetArn":"arn:aws:sns:us-west-2:977102061874:endpoint/APNS_SANDBOX/mobile_iOS_Sandbox/34ed4324e6-1119-67sd-b7dd-f413c88e4e25",
"topicArn":""
}
My result is an unexpected error sending message out.
Caused by: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,73]Message: Illegal character: <d>
My service is also like the example:
<amazonsns.init>
<region>{$ctx:region}</region>
<accessKeyId>{$ctx:accessKeyId}</accessKeyId>
<secretAccessKey>{$ctx:secretAccessKey}</secretAccessKey>
<version>{$ctx:version}</version>
</amazonsns.init>
<amazonsns.publish>
<message>{$ctx:message}</message>
<subject>{$ctx:subject}</subject>
<messageStructure>{$ctx:messageStructure}</messageStructure>
<topicArn>{$ctx:topicArn}</topicArn>
<targetArn>{$ctx:targetArn}</targetArn>
</amazonsns.publish>
I did not expect much success but just started trying different things....I attempted xml encoding the json message and get the same message on '&'. Using wire logs I found the content type of the successful messages going to Amazon were application/x-www-form-urlencoded so I also tried url encoding and get same message on the '%'.
I'm running out of ideas. I double checked the connector documentation to make sure I did not miss anything. It does say you can use a JSON transaction for Publish and gives a sample transaction however it does not discuss anything special/different that you would need to set up in order to make this work. Is there something I am missing?
UPDATE: Can see it is using the Builder for application/x-www-form-urlencoded which is stumbling on the json. Have tried setting messageType and ContentType properties to application/json. JSON Builder is enabled. It just seems the connector wants to send it out as application/x-www-form-urlencoded, is there something I am not setting properly to tell the connector this is JSON?
UPDATE: The publish template that comes with the connector is setting the messagetype to applicaiton/x-www-form-urlencoded which is overriding any of the settings I am making in my proxy service. Going to look at changing the template in the connector to use a different messagetype and/or allow me to set it in the proxy dynamically then will be trying again.
Joe
We have created a public JIRA for this issue. Please follow that.
You need to enable message builders for the content types you are sending. make sure following is done.
Enable relevent Message builder for each content-type.
< messageBuilder contentType="application/json"
class="org.apache.synapse.commons.json.JsonStreamBuilder"/>
Make sure Content-Type header property is parsing with the message.
Make sure your Json is wellformed.
Out of curiosity, are there any popular standards for returning server side exceptions (and corresponding details) in an API that returns JSON? The exceptions could be anything from uncaught exceptions in the code or thrown exceptions put in place by the developer.
Currently I'm setting the HTTP Status Code according to their Wiki definitions'
My JSON response would look something like this:
HTTP Code: 401
{'error': 'Authentication required.', 'message': 'You must be authenticated to perform that action.'}
You can take a look at JSON-RPC (http://en.wikipedia.org/wiki/JSON-RPC).
In case of success, the response is like this:
{
"result": ...(returned data)...
...
}
and in case of error, the response is like that:
{
"error": ...(error details)...
...
}
When the error occured, "error" property must be present and "result" property must not be returned. In case there was no error, "error" property must not be set. This way you can distinguish errors from successful responses based solely on the content of the response, without the need to check the headers (like response codes).
The standard structure of native ECMAScript error objects is:
{
name: ...,
message: ...
}
where "name" is the type of the error (the name of the corresponding constructor, like "Error", "SyntaxError", etc.). In your example, you could use the name "AuthentificationError", for instance.
Yes you can do the same.
As we know that Web servers allow us to create custom error pages for HTTP errors, in those custom error pages you have to serve the JSON reply
Example:
Add the following to your WEB-INF/web.xml:
<error-page>
<error-code>401</error-code>
<location>/autherror.jsp</location>
</error-page>
In the autherror.jsp file code the required logic to accomplish your needed JSON reply.
Try being a bit more verbose regarding the error you are sending. This structure has been suggested in some places to give sufficient information both for users and developer using your API
"error":{
"code":"410",
"long":"410001",
"message":"cannot connect to DB",
"developer":"Connection to the DB was unsuccessful due to ...",
"documentation": "mysite.com/help/errors/..."
}
Return the correct HTTP code for error. It is bad practice to return code 200 with error.
Extend the code with your own error IDs
Provide short message that roughly outlines the cause of the error. Consider adding more detailed message for the developers. Have in mind that API error messages are 99.9% of the time read by developers so make them happy by providing technical details (within reason) so that they can resolve ASAP.
In case you keep public documentation about your api and error codes, simply add the url.
I am trying to understand how POST and/or GET methods work in terms of the actual browser.
I am attempting to contact an API which requires API key, method you wish to use on their side, and an IP address at the minimum.
My original idea was to do something like this:
I feel like I'm on the right track, it does something and gets an error as opposed to telling me the page does not exist. I'm expecting either JSON or XML in response as the API supports both but instead I get this error:
This page contains the following errors:
error on line 1 at column 1: Document is empty
Below is a rendering of the page up to the first error.
Upon studying the documentation of the API more, I found something saying that methods are called using HTML form application/x-www-form-urlencoded and the resuource models are given as form elements.
I tried researching what that means to see what the problem was and found this site http://www.w3.org/TR/xforms11/ but I'm still unclear.
Ideas?
It seems to mean that the application is expecting a POST method but you're doing a request with a GET method (when you use the querystrings).
Since you can't just do browser requests using POST using the address bar, you may need to:
Construct a simple JS function that does a xmlhttprequest request using that method instead, and running it from the console;
Create a simple HTML page that automates the above process, allowing you do make POST calls;
Using CURL instead, which is a great tool for testing those kinds of requests.