I’m deep in development, but this was a pain in the ass to figure out so I thought I would throw this out there for those of you who for some reason need to call a Microsoft Atlas enabled web service.

I added the attribute [System.Web.Script.Services.ScriptService] to the web service, which I’m using to easily convert the return values of the web service to JSON.

Normally I’d call the WebMethod’s in my service using something along the following lines…

Justise.Project.WebSericeClassName.MethodName()

Using prototype, I need to use the Ajax class with the Request method to call the same service. Getting JSON back was the pain in the butt. Since using GET and POST require entirely different call configurations, I’m going to use different sections to detail each.

POST

Some of the important things in the following source code.

  • Content-Type is set to “application/json”
  • using postBody instead of parameters, and then you need postbody to be a string of the JSON format. Thus the purpose of fnJSONSerialize
  • xContextCache is a class I wrote using a static property on eProjectServiceObj (The object containing this Login call) I’ll include it at the end of this post.
var sID = eProjectServiceObj.xContextCache.mAdd(successCallback, callContext);

var xRequest = new Ajax.Request(this.Url + "/Login",
{
"contentType" : "application/json",
"method"    : "POST",
"onSuccess" : eProjectServiceObj.xContextCache.mGetEventHandler(sID),
"onFailure" : callFailure,
"postBody"  : fnJSONSerialize({"username": userName, "password" : password})
});

function fnJSONSerialize(whxObj) {
var xPairs = new Array();
for(var sProp in whxObj) {
xPairs.push(["'", sProp, "':'", whxObj[sProp], "'"].join(""));
}
return "{" + xPairs.join(",") + "}";
}

GET

  • Here you want to use parameters, but you want to quote your values, so I’m using ‘ ” ‘ added to each value.
  • The ContentType property in the options was ignored, so I had to use the “requestHeaders” collection.
  • You must add a [ScriptMethod(UseHttpGet = true)] to the method you intent to call, otherwise GET is disabled.
var sID = eProjectServiceObj.xContextCache.mAdd(successCallback, callContext);

var xRequest = new Ajax.Request(this.Url + "/Login",
{
"method"    : "GET",
"onSuccess" : eProjectServiceObj.xContextCache.mGetEventHandler(sID),
"onFailure" : callFailure,
"parameters"  : {"username": '"' + userName + '"', "password" : '"' + password + '"'},
"requestHeaders" : {"Content-type" : "application/json" }
});

Context Cache

I used this class to keep the ability to pass a context to the callback function.

function ContextCache() {
//--- Methods
this.mAdd   = fnAdd;
this.mExec  = fnExec;
this.mGetEventHandler = fnGetEventHandler;
this.mGetUnique = fnGetUnique;

//--- Properties
this.xContexts = new Hash();

function fnAdd(callbackFunction, callContext) {
var sID = this.mGetUnique();
this.xContexts[sID] = [callbackFunction, callContext];
return sID;
}

function fnGetEventHandler(handlerID) {
var sFunc = "eProjectServiceObj.xContextCache.mExec(arguments[0], '" + handlerID + "')";
return new Function(sFunc);
}

function fnExec(resultXmlHttp, handlerID) {
if(!this.xContexts[handlerID])return;
eval("var xResult = " + resultXmlHttp.responseText);

this.xContexts[handlerID][0](xResult, this.xContexts[handlerID][1]);
}

function fnGetUnique() {
var sUnique = new Date().getTime().toString();
if(this.xContexts[sUnique] == null)
return sUnique;

while(this.xContexts[sUnique]!=null) {
sUnique = sUnique + "_";
}
return sUnique;
}
}
4 comments