Is there a way to consume a web service using JavaScript? I'm Looking for a built-in way to do it, using a JavaScript framework is not an option.
You can consume a web service using JavaScript natively using the XmlHttpRequest object. However instantiating this object varies between browsers. For example Firefox and IE 7+ let you instantiate it as a native JavaScript object but IE6 requires you to instantiate it as an ActiveX control.
Because of this I'd recommend using an abstraction library such as jQuery. If that's not an option then abstract the creation into a factory method and check for the browser version.
To use this to make a web service call you simply instantiate the object and then call it's open() method. I recommend this is done async to keep the UI responsive. When invoked async you will get callbacks to your specified async method that will indicate the status of the request. When the status is 4 (loaded) you can take the response data and then process it.
How you process the data will depend on what it is, if it's JSON then you can run it through JavaScript's eval() method but that does have some security implications. If it's XML you can use the XML DOM to process it.
See Wikipedia for more info on the XMLHttpRequest object.
You can create an XMLHttpRequest if the service is hosted within your domain. If not, you will have cross-domain issues.
You can use the XMLHttpRequest object, but since you don't want to use any JavaScript frameworks, you will have to marshal and unmarshal the SOAP envelopes yourself.
Also check XML HTTP Request for a nice info page about using the XmlHttpRequest object.
There is a small library written in javascript that can be used as a XML-SOAP client.
I don't know if it works on all browsers but it might help you out. You can find it here
This worked. It's old (checking for Netscape), was written before all the Ajax tools came out. You do have to handle different browsers -- basically, IE does it one way, and everybody else does it the other way.
// javascript global variables
var soapHeader = '<?xml version=\"1.0\"?>'
+ '<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"'
+ ' SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"'
+ ' xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\"'
+ ' xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\"'
+ '>'
+ '<SOAP-ENV:Header/>'
+ '<SOAP-ENV:Body>';
var soapFooter = '</SOAP-ENV:Body>'
+ '</SOAP-ENV:Envelope>';
var destinationURI = '/webservices/websalm';
var actionURI = '';
function callWebService(nsCallback,ieCallback,parms) {
try
{
// Create XmlHttpRequest obj for current browser = Netscape or IE
if (navigator.userAgent.indexOf('Netscape') != -1)
{
SOAPObject = new XMLHttpRequest();
SOAPObject.onload = nsCallback;
} else { //IE
SOAPObject = new ActiveXObject('Microsoft.XMLHTTP');
SOAPObject.onreadystatechange = ieCallback;
}
SOAPObject.open('POST', destinationURI, true);
// Set 2 Request headers, based on browser
if (actionURI == '') {
SOAPObject.setRequestHeader('SOAPAction', '\"\"');
} else { SOAPObject.setRequestHeader('SOAPAction', actionURI);
}
SOAPObject.setRequestHeader('Content-Type', 'text/xml');
// Compose the Request body from input parameter + global variables
var requestBody = soapHeader + parms + soapFooter
// Send, based on browser
if (navigator.userAgent.indexOf('Netscape') != -1)
{
SOAPObject.send(new DOMParser().parseFromString(requestBody,'text/xml'));
} else {
SOAPObject.send(requestBody);
}
} catch (E)
{
alert('callWebService exception: ' + E);
}
}
Related
I need to create a JS-Library which can run workflow using new WebApi for Dynamics CRM 2016:
https://msdn.microsoft.com/de-de/library/mt607689.aspx
I need to start workflow from my Code. (workflow should be “real-time”) and not asynchronously . I will build my function-call into Ribbon on form.
If anyone can help me I would be more then thankful, since I searched all internet and could not found how to solve this, except from above link where I found this method
https://msdn.microsoft.com/de-de/library/mt622404.aspx
but I'm not sure how to use this method? Once agin it has to be “real-time”
I found solutions such as:
-https: //processjs.codeplex.com/
but this does not work for me since it run workflow asynchronously. It has to be using Web API from link provided above. I think that this Web API works only for Microsoft Dynamics 2016
Now that we have actions there really isn't a need to start a workflow from javascript anymore. I used to do so using a javascript library that used the SOAP api but the web api actions are much easier to use. And an action is created in the same way as a workflow. To create an action go to create a workflow but instead of choosing workflow from the dropdown select action. You will end up with a form like this.
Remember the unique name and the entity which you will run it against. In this example I'll be using this workflow pictured which runs against a contact record.
From javascript I can now issue a POST to
https://<your-crm-server>/api/data/v8.0/contacts(<contact-id>)/Microsoft.Dynamics.CRM.wa_GetContactSyncStatus
Again this is an action targeting contacts and running the wa_GetContactSyncStatus action, change the values to what you need them to be. Also as a side note this is against a 2016 server anything later will have a different api version for you to use. Consult the developer resources page in your crm instance to figure out what your url for the web api is.
The action will run asynchronously and as long as your javascript request is set to be synchronous as well your request will return when the action is complete.
As another side note if you have your action call another workflow that isn't synchronous it will quite probably return before your asynchronous background workflow does.
I do this quite often: make the process an Action, they are designed specifically for this purpose (click a ribbon button and invoke what essentially is a workflow through WebAP; they also become custom messages for plugin registration, which is nice in some scenarios).
To have synchronous invocations all you need to do is to make the XmlHttpRequest synchronous by tweaking the open statement:
// 'xhr' is the XMLHttpRequest
xhr.open(http_method, request_url, false); <-- third parameter 'false' means sync request
I never use libraries to invoke the webapi so unfortunately I can't suggest any library-specific piece of code, but I would assume any decent library allows you to make XHR requests synchronous.
(Mandatory warning: sync requests are suboptimal and browsers do complain about them, I expect Chrome in particular to start breaking sync code at some point in the future).
Soap Request in JS :
function RunWorkflow(in_entitiId,in_workflowId,in_url) {
var _return = window.confirm('Do you want to execute workflow ?');
if (_return) {
var url = in_url;
var entityId =in_entitiId ;
var workflowId = in_workflowId;
var OrgServicePath = "/XRMServices/2011/Organization.svc/web";
url = url + OrgServicePath;
var request;
request = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
"<s:Body>" +
"<Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<request i:type=\"b:ExecuteWorkflowRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">" +
"<a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">" +
"<a:KeyValuePairOfstringanyType>" +
"<c:key>EntityId</c:key>" +
"<c:value i:type=\"d:guid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/\">" + entityId + "</c:value>" +
"</a:KeyValuePairOfstringanyType>" +
"<a:KeyValuePairOfstringanyType>" +
"<c:key>WorkflowId</c:key>" +
"<c:value i:type=\"d:guid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/\">" + workflowId + "</c:value>" +
"</a:KeyValuePairOfstringanyType>" +
"</a:Parameters>" +
"<a:RequestId i:nil=\"true\" />" +
"<a:RequestName>ExecuteWorkflow</a:RequestName>" +
"</request>" +
"</Execute>" +
"</s:Body>" +
"</s:Envelope>";
var req = new XMLHttpRequest();
req.open("POST", url, true)
// Responses will return XML. It isn't possible to return JSON.
req.setRequestHeader("Accept", "application/xml, text/xml, */*");
req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
req.onerror = displayError;
req.onreadystatechange = function () { assignResponse(req); };
req.send(request);
}
function displayError(e) {
alert(this.status);
}
}
function assignResponse(req) {
if (req.readyState == 4) {
if (req.status == 200) {
alert('successfully executed the workflow');
}
}
}
Example:
RunWorkflow(Xrm.Page.data.entity.getId(),"21E95262-5A36-46CA-B5B5-3F5AA539A9AF","https://org.dynamics.com");
I was wondering if it was possible to intercept and control/redirect DNS requests made by Firefox?
The intention is to set an independent DNS server in Firefox (not the system's DNS server)
No, not really. The DNS resolver is made available via the nsIDNSService interface. That interface is not fully scriptable, so you cannot just replace the built-in implementation with your own Javascript implementation.
But could you perhaps just override the DNS server?
The built-in implementation goes from nsDNSService to nsHostResolver to PR_GetAddrByName (nspr) and ends up in getaddrinfo/gethostbyname. And that uses whatever the the system (or the library implementing it) has configured.
Any other alternatives?
Not really. You could install a proxy and let it resolve domain names (requires some kind of proxy server of course). But that is a very much a hack and nothing I'd recommend (and what if the user already has a real, non-resolving proxy configured; would need to handle that as well).
You can detect the "problem loading page" and then probably use redirectTo method on it.
Basically they all load about:neterror url with a bunch of info after it. IE:
about:neterror?e=dnsNotFound&u=http%3A//www.cu.reporterror%28%27afew/&c=UTF-8&d=Firefox%20can%27t%20find%20the%20server%20at%20www.cu.reporterror%28%27afew.
about:neterror?e=malformedURI&u=about%3Abalk&c=&d=The%20URL%20is%20not%20valid%20and%20cannot%
But this info is held in the docuri. So you have to do that. Here's example code that will detect problem loading pages:
var listenToPageLoad_IfProblemLoadingPage = function(event) {
var win = event.originalTarget.defaultView;
var docuri = window.gBrowser.webNavigation.document.documentURI; //this is bad practice, it returns the documentUri of the currently focused tab, need to make it get the linkedBrowser for the tab by going through the event. so use like event.originalTarget.linkedBrowser.webNavigation.document.documentURI <<i didnt test this linkedBrowser theory but its gotta be something like that
var location = win.location + ''; //I add a " + ''" at the end so it makes it a string so we can use string functions like location.indexOf etc
if (win.frameElement) {
// Frame within a tab was loaded. win should be the top window of
// the frameset. If you don't want do anything when frames/iframes
// are loaded in this web page, uncomment the following line:
// return;
// Find the root document:
//win = win.top;
if (docuri.indexOf('about:neterror') == 0) {
Components.utils.reportError('IN FRAME - PROBLEM LOADING PAGE LOADED docuri = "' + docuri + '"');
}
} else {
if (docuri.indexOf('about:neterror') == 0) {
Components.utils.reportError('IN TAB - PROBLEM LOADING PAGE LOADED docuri = "' + docuri + '"');
}
}
}
window.gBrowser.addEventListener('DOMContentLoaded', listenToPageLoad_IfProblemLoadingPage, true);
I'm using an AJAX function to transfer data to a PHP file. The data that I'm passing to the AJAX function is 17000 characters long. This is generally too long to transfer using the GET method, however one would think that the POST method would allow for such large variables to be be passed on.
Here's the AJAX function I'm using:
function ajaxFunction(id, datatypeString, pathToFileString, variable){
var myRequestObject = null;
document.getElementById(id).innerHTML = "<span>Started...</span>";
if (window.XMLHttpRequest)
{
myRequestObject = new XMLHttpRequest();
}
else if (window.ActiveXObject)
{
try
{
myRequestObject = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
try
{
myRequestObject = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {}
}
}
myRequestObject.onreadystatechange = function()
{
document.getElementById(id).innerHTML = "<span>Wait server...</span>";
if(myRequestObject.readyState == 4)
{
if(myRequestObject.status == 200)
{
// process a document here
document.getElementById(id).innerHTML = "<span>Processing file...</span>"
if(datatypeString == "txt"){
//Injects code from a text file
document.getElementById(id).innerHTML = myRequestObject.responseText;
}
else if(datatypeString == "xml"){
//Injects code from an XML file
document.getElementById(id).innerHTML = myRequestObject.responseXML.documentElement.document.getElementsByTagName('title')[0].childNodes[0].nodeValue; // Inject the content into the div with the relevant id
}
else{
document.getElementById(id).innerHTML = "<span>Datatype exception occured</span>";
}
}
else
{
document.getElementById(id).innerHTML = "<span>Error: returned status code " + myRequestObject.status + " " + myRequestObject.statusText + "</span>";
}
}
};
myRequestObject.open("POST", pathToFileString+variable, true);
myRequestObject.send(null);
}
And this is the function call to that AJAX function:
ajaxFunction("myDiv", "txt", "processdata.php", "?data="+reallyLargeJavascriptVariable);
Also this is the error that I'm getting when the AJAX function is called:
Error: returned status code 414 Request-URI Too Large
I've looked around on Stackoverflow and other websites for a solution to this problem. However most answers come down to: "Use the POST method instead of the GET method to transfer the data."
However as you can see in the AJAX function, I'm already using the POST method.
So I'm not sure what's going on here and what to change in my code to solve this issue. I simply want to be able to pass very large variables to my function, but with this function that doesn't seem possible.
Given the error, the limitations of the URI seem to be causing the problem. However, I'm using the POST method and not the GET method, so why is the variable still passed via the URI? Since I am not using the GET method, but rather the POST method like many people suggested in other threads about this problem, I'm not sure why the URI is involved here and is seemingly causing a problem.
Apparently the URI is putting a limit on the size of the variable that I can transfer, however I'm using the POST method, so why is this error occurring and how can I adjust my AJAX function to make it work with the large variables that I want to transfer using AJAX?
When you're doing a POST you need to pass the POST data on the .send (you're currently passing null). You need to set a few header details, as well.
myRequestObject.open("POST", pathToFileString, true);
myRequestObject.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
myRequestObject.setRequestHeader("Content-length", variable.length);
myRequestObject.send(variable);
If you're currently passing a question mark in the start of variable or end of the path go ahead and remove it.
I'm trying to implement sms functionality in Dynamics CRM 2011. I've created a custom activity for this and added a button to the form of an SMS. When hitting the button, a sms should be send.
I need to make an http request for this and pass a few parameters. Here's the code triggered:
function send() {
var mygetrequest = new ajaxRequest()
mygetrequest.onreadystatechange = function () {
if (mygetrequest.readyState == 4) {
if (mygetrequest.status == 200 || window.location.href.indexOf("http") == -1) {
//document.getElementById("result").innerHTML = mygetrequest.responseText
alert(mygetrequest.responseText);
}
else {
alert("An error has occured making the request")
}
}
}
var nichandle = "MT-1234";
var hash = "md5";
var passphrase = "[encryptedpassphrase]";
var number = "32497123456";
var content = "testing sms service";
mygetrequest.open("GET", "http://api.smsaction.be/push/?nichandle=" + nichandle + "&hash=" + hash + "&passphrase=" + passphrase + "&number=" + number + "&content=" + content, true)
mygetrequest.send(null)
}
function ajaxRequest() {
var activexmodes = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"] //activeX versions to check for in IE
if (window.ActiveXObject) { //Test for support for ActiveXObject in IE first (as XMLHttpRequest in IE7 is broken)
for (var i = 0; i < activexmodes.length; i++) {
try {
return new ActiveXObject(activexmodes[i])
}
catch (e) {
//suppress error
}
}
}
else if (window.XMLHttpRequest) // if Mozilla, Safari etc
return new XMLHttpRequest()
else
return false
}
I get the "access is denied error" on line:
mygetrequest.open("GET", "http://api.smsaction.be/push/?nichandle=" ......
Any help is appreciated.
The retrieving site has to approve cross domain AJAX requests. Usually, this is not the case.
You should contact smsaction.be or check their FAQ to see if they have any implementation in place.
Usually JSONP is used for cross domain requests, and this has to be implemented on both ends.
A good way to overcome this, is using your own site as a proxy. Do the AJAX requests to an script on your side, and let it do the call. In example PHP you can use cURL
I suppose the SMS-service is in different domain. If so, you cannot make AJAX-call to it, because it violates same origin policy. Basically you have two choices:
Do the SMS-sending on server-side
Use JSONP
Also, is it really so that the passphrase and other secrets are visible in HTML? What prevents people from stealing it and using it for their own purposes?
Your AJAX requests by default will fail because of Same Origin Policy.
http://en.wikipedia.org/wiki/Same_origin_policy
Modern techniques allow CORS ( see artilce by Nicholas ) http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/
jQuery's Ajax allow CORS.
Another way to do it is to get the contents and dynamically generate a script element and do an insertBefore on head.firstchild ( refer jQuery 1.6.4 source line no : 7833 )
Google analytics code does some thing similar as well. you might want to take a look at that too.
Cheers..
Sree
For your example, when requesting from different domain error is:
XMLHttpRequest cannot load http://api.smsaction.be/push/?nichandle=??????&hash=?????&passphrase=[???????????]&number=????????????&content=???????????????. Origin http://server is not allowed by Access-Control-Allow-Origin.
For cross domains XMLHttp requests destination server must send Access-Control-Allow-Origin response header.
MDN: https://developer.mozilla.org/en/http_access_control
I have an issue, mainly with IE.
I need to be able to handle n queries one after another. But If I simply call my function below in a for loop IE does some strange things (like loading only so many of the calls).
If I use an alert box it proves that the function gets all of the calls, and surprisingly IT WORKS!
My guess is that IE needs more time than other browsers, and the alert box does just that.
Here is my code:
var Ajax = function(all) {
this.xhr = new XMLHTTPREQUEST(); // Function returns xhr object/ activeX
this.uri = function(queries) { // Takes an object and formats query string
var qs = "", i = 0, len = size(queries);
for (value in queries) {
qs += value + "=" + queries[value];
if (++i <= len) { qs += "&"; }
}
return qs;
};
xhr.onreadystatechange = function() { // called when content is ready
if (this.readyState === 4) {
if (this.status === 200) {
all.success(this.responseText, all.params);
}
this.abort();
}
};
this.post = function() { // POST
xhr.open("POST", all.where, true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send(uri(all.queries));
};
this.get = function() { // GET
xhr.open("GET", all.where + "?" + uri(all.queries), true);
xhr.send();
};
if (this instanceof Ajax) {
return this.Ajax;
} else {
return new Ajax(all);
}
};
This function works perfectly for a single request, but how can I get it to work when called so many times within a loop?
I think the problem might be related to the 2 concurrent connections limit that most web browsers implement.
It looks like the latency of your web service to respond is making your AJAX requests overlap, which in turn is exceeding the 2 concurrent connections limit.
You may want to check out these articles regarding this limitation:
The Dreaded 2 Connection Limit
The Two HTTP Connection Limit Issue
Circumventing browser connection limits for fun and profit
This limit is also suggested in the HTTP spec: section 8.14 last paragraph, which is probably the main reason why most browsers impose it.
To work around this problem, you may want to consider the option of relaunching your AJAX request ONLY after a successful response from the previous AJAX call. This will prevent the overlap from happening. Consider the following example:
function autoUpdate () {
var ajaxConnection = new Ext.data.Connection();
ajaxConnection.request({
method: 'GET',
url: '/web-service/',
success: function (response) {
// Add your logic here for a successful AJAX response.
// ...
// ...
// Relaunch the autoUpdate() function in 100ms. (Could be less or more)
setTimeout(autoUpdate, 100);
}
}
}
This example uses ExtJS, but you could very easily use just XMLHttpRequest.
Given that the limit to a single domain is 2 concurrent connections in most browsers, it doesn't confer any speed advantage launching more than 2 concurrent requests. Launch 2 requests, and dequeue and launch another each time one completes.
I'd suggest throttling your requests so you only have a few (4?) outstanding at any given time. You're probably seeing the result of multiple requests being queued and timing out before your code can handle them all. Just a gess though. We have an ajax library that has built-in throttling and queues the requests so we only have 4 outstanding at any one time and don't see any problems. We routinely q lots per page.
Your code looks like it's put together using the constructor pattern. Are you invoking it with the new operator like var foo = new Ajax(...) in your calling code? Or are you just calling it directly like var foo = Ajax(...) ?
If the latter, you're likely overwriting state on your later calls. It looks like it's designed to be called to create an object, on which the get/post methods are called. This could be your problem if you're "calling it within a loop" as you say.