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

4 Responses to “Using Prototype to call an Atlas enabled WebService”

  1. Julian Says:

    Hi Kris,

    Thanks for this – i tried your suggestions, but hit an issue.

    Getting this error returned form my “.asmx” web service:

    [InvalidOperationException: Invalid web service call, expected path info of /js/<Method>.]

    System.Web.Script.Services.RestHandler.CreateHandler(HttpContext context) +287

    System.Web.Script.Services.RestHandlerFactory.GetHandler(HttpContext context, String requestType,

    String url, String pathTranslated) +136

    System.Web.Script.Services.ScriptHandlerFactory.GetHandler(HttpContext context, String requestType

    , String url, String pathTranslated) +175

    System.Web.HttpApplication.MapHttpHandler(HttpContext context, String requestType, VirtualPath path

    , String pathTranslated, Boolean useAppConfig) +175

    System.Web.MapHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +120

    System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

    +155

    I have prototype 1.6 running under my /js/ folder… Would that have anything to do with it?

    Cheers,
    Julian.

  2. Julian Says:

    Hi, I worked out that last one (need to append the action!)

    However, i have another trickier issue – and that is if I am to return HTML string as the response text it gets encoded over the wire eg:

    \u003e\u003ch3\u003e

    The microsoft toolkit automatically decodes this, but can’t seem to work it out using prototype.

  3. Kris Gray Says:

    Hi Julian,

    I don’t have the source code for this project anymore, so its hard for me to debug it.

    Though the responseText on the XMLHTTP object should be the same regardless what framework you call it from. Your saying Microsoft Atlas on the client decodes this unescaped text appropriately?

  4. Javascript Templating within ASP.NET « Fluent.Interface Says:

    […] Web Forms post consider’s prototype to be a more well-rounded library, but it requires some tweaks and doesn’t handle character escaping the same way as AJAX […]

Leave a Reply