05-02-2016 12:07 PM - edited 03-01-2019 04:28 AM
I am trying to access the always-on APIC-EM sandbox API using my webserver and javascript. I have already done the python based learning labs and didn't have much issue with it.
Scenario:
I have a locally hosted nodejs server. I am using xmlHttpRequest (the nodejs version xhr actually) and I can run a GET command no problem. However, since V1 and the new ticketing system, I need to be able to POST the username/password so that I can get the ticket and run the GET commands that I want (network device discovery etc).
Problem:
APIC-EM doesn't seem to recognize the header content type of 'application/json' when I POST (it does with GET, no problem). It returns an error of: "error code": "unknown", "message": "Content-type: 'text/plain;charset=UTF-8' not supported"
Here is my code:
var json_text = '{ "username": "admin","password": "C!sc0123" }';
var ticket_json = JSON.parse(json_text);
var apicem_ip = "sandboxapic.cisco.com:9443";
var apic_url = 'https://'+apicem_ip+'/api/v1/ticket';
//- var xmlHTTP = new XMLHttpRequest();
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var xhr = new XMLHttpRequest();
xhr.open('POST',apic_url,false);
xhr.responseType = 'JSON';
xhr.setRequestHeader = ('content-type', 'application/json');
xhr.send(JSON.stringify(ticket_json));
app.locals.apic_nd = xhr.responseText;
Lines 6 and 12 are nodejs specific as XMLHttpRequest doesn't exist natively in nodejs. I don't think that is a problem because I can successfully use GET and receive a response from the sandbox controller. Line 12 is just setting a global variable to be called in my HTML file (which is where I view the responses and errors from the sandbox controller).
Things I've tried, I tried to send just a plain JSON object on line 11 (without the stringify function which converts it into a string), but that doesn't work in Java Script and I believe that that is not how the python API works either (it is a string as well). I also changed line 10 to 'text/json' as well, no luck.
Why am I trying to do this?
I already have a way to view the controller data via a shell using python, I would like to call the APIs and see the same data via a browser. I figured JavaScript would be the easiest way but ran into the above issue.
Any help would be appreciated.
05-02-2016 12:17 PM
I think what might be happening is XMLHttpRequest is looking for a "Content-type" header with an uppercase 'C'. Since it doesn't see one, it goes with its default. Can you try setting the header Content-type and see if that helps? While this shouldn't matter, there have been bugs with this in the past with XMLHttpRequest modules.
05-02-2016 12:29 PM
If that doesn't work, the other thing it might be is complaining on the Accept header. Try also adding:
xhr.setRequestHeader('Accept','application/json');
05-02-2016 01:58 PM
I tried both your options and still got the same error message.
Thanks for your input, any other ideas?
05-02-2016 02:07 PM
Can you get a sniffer capture of the request to see what headers and body the client is sending?
05-02-2016 02:36 PM
I tried yours and got
Then I modified my script to be the same as your then got
var url = 'https:/sandboxapic.cisco.com:9443/api/v1/ticket';
// provide username and password as body of request
var j_data = {
"username":"admin",
"password":"C!sc0123"};
var json_text = '{"username":"admin","password":"C!sc0123"}';
var ticket_json = JSON.parse(json_text);
var xmlHttp = new XMLHttpRequest();
alert("test1");
xmlHttp.open("POST", url, false);
xmlHttp.setRequestHeader("Content-type", "application/json");
alert("test2");
xmlHttp.responseType = "JSON";
alert("test3");
// xmlHttp.send(JSON.stringify(j_data));
xmlHttp.send(JSON.stringify(ticket_json));
alert("test4");
alert(xmlHttp.response);
var data = JSON.parse(xmlHttp.response)
alert(data.version)
alert(data.response.serviceTicket);
}
I suspect there may have hidden char in you script or I missed something.
BUT those are minor compare to what you can see from the last image that we are facing CORS issue. How can you resole the CORS issue that's the challenge.
05-02-2016 03:11 PM
xhr.setRequestHeader = ('content-type', 'application/json'); ==> got error.
vs
xhr.setRequestHeader('content-type', 'application/json');
Aslo we can just use
var json_text = { "username": "admin","password": "C!sc0123" };
......
......
xhr.send(JSON.stringify(json_text));
05-02-2016 03:15 PM
Heh, I didn't catch that. Yeah, it's a method. It doesn't return a settable property.
05-02-2016 05:25 PM
Hmm, nice catch! I wonder why it worked with the GET requests. I changed the code but still a no go. Looking at the xhr module I am using, there is a known bug for synchronous requests that mess up header data. I changed the code to async which is supposed to fix it but still got the same error. I think the problem might be with the module. There is another one I can try and will post back tomorrow or tonight. Thanks so far!
05-03-2016 01:16 PM
I got it working with a different module, not sure why I was using the old one...guess I am new to nodejs and restAPIs. Here is the code (this website won't let me format code in replies...)
Thanks for y'alls help!!
var apicem_ip = "sandboxapic.cisco.com:9443";
var apic_url = 'https://'+apicem_ip+'/api/v1/ticket';
var request = require('request');
var options = {
url: 'https://'+apicem_ip+'/api/v1/ticket',
method: "POST",
headers: {
'Content-type': 'application/json'
},
//console.log("test1");
body: '{ "username": "admin", "password": "C!sc0123"}'
//console.log("test2");
};
function callback(error, response, body) {
console.log("callback function");
//if (!error && response.statusCode == 200) {
if (!error) {
var info = (JSON.parse(body));
console.log(info);
console.log("status 200");
}
else {
//console.log(response.statusCode);
//console.log(JSON.parse(body));
console.log(JSON.parse(body));
}
}
request.post(options, callback);
05-03-2016 01:24 PM
Glad to hear that, so you did not get any CORS issue ?
05-03-2016 01:30 PM
Not sure what CORS is, but I was able to get a ticket number... so now it is on to the next where I use the ticket number as a header in my GET requests...like in the python modules.
05-03-2016 01:36 PM
I wonder if you can share your whole javascript ? You use node.js right ?
05-03-2016 01:44 PM
Yes. Node.js with express.js as the webfrontend. I am also using jade as the HTML template option. I am all new to this, I just downloaded a template website (google hackathon-startup).
The code I posted above is the only real addition I've made to app.js. However, I don't think that is where the code should live. I am trying to figure out now how to incorporate the js code either directly into HTML via jade... or... directly into the controller js res handler. Again, new to node in general so still trying to figure it out.
07-16-2016 09:19 AM
Hi Guys
I am trying this code JS as well
=================================
<html>
<head>
<title>
Hello.com
</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
</head>
<script>
var url = 'https://sandboxapic.cisco.com/api/v1/ticket';
// provide username and password as body of request
var json_text = {"username" : "devnetuser","password" : "Cisco123!"};
var x = JSON.stringify(json_text);
//var ticket_json = JSON.parse(json_text);
var xmlHttp = new XMLHttpRequest();
xmlHttp.withCredentials = true;
alert("test1");
xmlHttp.open("POST", url, true);
xmlHttp.setRequestHeader('X-PINGOTHER.com', 'pingpong');
xmlHttp.setRequestHeader("content-type", "application/json");
alert("test2");
xmlHttp.responseType = "json";
alert("test3");
xmlHttp.send(x);
//xmlHttp.send(json_text);
alert("test4");
var data = JSON.parse(xmlHttp.response);
//alert(data.version);
// alert(data.response.serviceTicket);
</script>
</html>
=====================================
I think I am having an issue with CORs. cant figure out the problem
Here's what i get when I run chrome CTRL+Shift +J (devtools)
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide