Contents
Introduction
In a previous post I’ve shown how to use WCF, jQuery and jTemplates to retrieve information from the server and lay it out on the page using jTemplates’ templating engine.
The code example in that article used the Microsoft Ajax client library and the proxy it creates at run time to ease the javascript code needed to call the methods of the webservice I created.
In this article I would like to show you an easy way of creating a similar proxy to the Microsoft one in pure javascript and the help of jQuery’s AJAX capabilities. This makes a lot of sense if you do not have the option to use the MS library on the client or simply because you don’t want to.
I will be relying mostly on the code examples and concepts I described in the previous article so I’d recommend reading it first before continuing to read this one.
You can let me know if you find this article useful or not by using the ratings and comments below.
Server Side
The good news: no need to change anything on the server side, the web service can continue to work as it always did. All we’re doing in this exercise is to replace the client side logic and so we will not touch the server code.
Client Side
Using the same website I’ve shown how to create in the previous article we will create the new proxy for our WCF webservice.
In the System folder create a javascript file and call it ServiceProxy.js, To the file add the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
ServiceProxy = function() //constructor for the proxy
{
this._baseURL = "Services/TutorialService.svc/";
};
ServiceProxy.prototype =
{
_defaultErrorHandler: function(xhr, status, error)
{
alert(xhr.statusText);
},
_doAjax: function(method, data, fnSuccess, fnError)
{
if (!data) data = {};
if (!fnError) fnError = this._defaultErrorHandler;
$.ajax({
type: "GET",
url: this._baseURL + method,
data: data,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: fnSuccess,
error: fnError,
dataFilter: function(data)
{
var response;
if (typeof (JSON) !== "undefined" && typeof (JSON.parse) === "function")
response = JSON.parse(data);
else
response = val("(" + data + ")");
if (response.hasOwnProperty("d"))
return response.d;
else
return response;
}
});
}
};
|
Lets examine the code:
The first section is the constructor of the proxy object, in there we define the base URL of the service we will be calling.
The second method is _defaultErrorHandler, this is a default error handler, its strictly a developer friendly method, allowing the calling method to leave the pointer to an error handling method empty. Of course the implementation of this default error handler should be changed in a real life scenario as its not wise to show any raw error information to a website’s visitor.
The third and most important method is _doAjax, this is where the “magic” happens which takes care of the back and forward communication with the server.
The first two lines take care of the defaults, if there is no data passed to the webservice we assign an empty object and if there is no pointer supplied to an error handler we assign our default method.
Next is the where we use jQuery’s incredibly convenient Ajax method, lets examine this call, jQuery’s ajax method accepts an object that sets all the needed information:
‘type’: The HTTP verb: could be GET, POST, etc. In this example we use GET since we’re only retrieving from the server.
For a more robust scenario we would probably want to turn this into a parameter of the method.
‘url’: The URL of the webservice method, in this case the base URL defined in the constructor and the name of the method.
‘data’: The parameters passed to the service method, constructed as a JSON object.
‘contentType’: the content type of the data being sent to the server. In our case we’re communicating with the webservice using JSON and therefore we use: “application/json; charset=utf-8”
‘success’: A pointer to the method to be called when the asynchronous call has finished successfully.
‘error’: A pointer to the method to be called when the asynchronous call finished with an error.
‘dataFilter’: I’ve taken the code block in this example from Dave Ward’s Encosia blog. I will add the links to a couple of his articles at the end of this post.
The dataFilter is used whenever there is a successful response from the server and allows us to modify the response in any way we like before its passed to the success method.
In this example there are two uses to the filter: The first one is to turn the response into a JSON object from the string result coming from the server. It will first check whether the current browser supports JSON parsing natively (Firefox 3.5, IE8 for example). If it does it will use the native JSON object and if not will use the less optimized and less secure eval() function.
The second part is to check whether the response is enclosed within a ‘d’ property, this ‘d’ property is added automatically by Microsoft’s WCF service and since we want to abstract this fact from the caller we only return the relevant response if this is the case.
Now that we have everything we need for our base proxy functionality all we need to do is add the methods that match the exposed methods on the webservice. To the prototype of the ServiceProxy object add these two methods:
1 2 3 4 5 6 7 8 9 10 11 |
getArticles: function(success, error)
{
this._doAjax("GetArticles", null, success, error);
},
getArticle: function(link, success, error)
{
var data = {link: link};
this._doAjax("GetArticle", data, success, error)
},
|
These two methods call our _doAjax, passing the necessary information such as the method name and the parameters if needed to the call.
In fact the methods mirror the methods available on the webservice thus completing the proxy.
Now the only thing to do is create a HTML file (or any other file, could even be JSP) that will replace the aspx page we created in the previous article.
You can find all the source code in the example site ive made available for download, in this post I will only show the changes needed.
– First, create a default.html in the root of the site.
– In the head element add these links:
<script src=”System/jquery.js” type=”text/javascript”></script>
<script src=”System/jquery-jtemplates.js” type=”text/javascript”></script>
<script src=”System/ServiceProxy.js” type=”text/javascript”></script>
<link href=”System/Styles.css” rel=”stylesheet” type=”text/css” />
– Copy over the entire javascript element.
– Add the following code as the first line of the script to declare the proxy:
<script type="text/javascript">
var proxy = new ServiceProxy();
– In the document ready function replace the call to the GetArticles method with this one:
1 2 3 4 |
$(document).ready(function() //executes this code when page loading is done
{
proxy.getArticles(articlesRetrieved);
});
|
– In the loadArticle function replace the call to getArticle with this one:
1 2 3 4 5 6 7 |
function loadArticle(link)
{
$("#LoadingImg").removeClass("Hidden");
$("#SingleArticle").html("");
proxy.getArticle(link, articleRetrieved, serviceDefaultErrorHandler);
}
|
That’s all! Now you have a working page doing exactly the same as it did before only using our own javascript proxy without any dependencies on the Microsoft proxy or the MS Ajax library.
The end result is as expected identical to the one before:
Source Code
Can be downloaded here.
Resources
- Simplify calling ASP.NET AJAX services from jQuery
- Improving jQuery’s JSON performance and security
- Never worry about ASP.NET AJAX’s .d again
- WCF and JSON Services
nice! what about calling WCF Authentication Services?
Great articles.
One discovery… jQuery 1.5 and above breaks it.
I’m not a hardcore javascript developer, but the easiest workaround is to remove the dataFilter property in _doAjax.
This unfortunately means the ‘d’ property added by the WCF service to all json responses is still present. The workaround to that is to change all ‘results’ to ‘results.d’ in index.html, but it would be nicer if the ‘d’ could be removed inside _doAjax as in the original code.
Followup…
since the jquery ajax method now automatically converts responses to json, and the fact that dataFilter runs before this magic happens, the most sensible solution to me (feel free to disagree) is to manipulate the raw response before jQuery does the conversion to json. So for my own project, I’ve reinstated the dataFilter property with simple text manipulation to remove the ‘d’ property added by WCF.
dataFilter: function (data) {
//remove the ‘d’ property inserted by all WCF services (if it exists)
return data.replace(/^\{“d”:(.*)\}$/, “$1”);
}
Hi Greg,
Its a great point you bring up, ive been meaning to update the article or write a new one but havent gotten around to doing it yet.
In any case, since im very much adverse to string manipulation, however simple they may be, what i did in my usage of the proxy is wrap the Success handler with my own so it looks like this:
$.ajax({
…
success: function(data, status, xhr)
{
var newData = (data.hasOwnProperty(“d”) ? data.d : data)
fnSuccess(newData, status, xhr);
},
…
});
Thanks!
Yoav
Where is the service?, I can’t find any file called : “Services/XXXX.svc/”
I can find the Service References\WCFService\Reference.cs
and the WCFService.asmx .
Can you please help me what I should write in the _baseURL ?
I did:
this._baseURL = “Service/WcaService.asmx/”;
and add my function
GetPictures: function (link, success, error) {
var data = { link: link };
this._doAjax(“GetPictures”, data, success, error)
},
I call :
var proxy = new ServiceProxy();
prox.GetAlbums(0, OnGetPicturesSucceed, OnDefaultErrorHandler);
nothing happen!
Hi Yosi,
Im not sure to which asmx youre referring to… the web service exposed in the download i provided in this article (http://www.mediafire.com/file/jutyzjrzm5m/ServiceProxy%20-%20Turotial%20Site.zip) has an svc file called: TutorialService.svc
you can find it in the zip file: JTemplatesWCF – Tutorial Site\Services folder.
the bsae url property should point to the SVC file:
this._baseURL = “Services/TutorialService.svc/”;
you should find everything there 🙂
I successful, but with the following changes:
1. type: “POST”, instead of “GET” -> any comment?
2. success: fnSuccess, -> the “wrap the Success handler with my own” does not works.
Thanks a lot Yoav, I successfully configure, in my case it is this._baseURL = “Service/WcaService.asmx/”;
It works great, except he 2 issues I sent:
1. type: “POST”, instead of “GET” -> any comment?
2. success: fnSuccess, -> the “wrap the Success handler with my own” does not work, I use the change suggested by “Greg”, and it works fine.
Hi Yosi,
Regarding the need to change to POST rather than using GET. thats up to how you create your webservice and its methods. You need to allow GET operations and make sure you mark the method as expecting GET. In WCF thats done using the WebGet attribute in combination with the OperationContract attribute decorating the method of the service class.
The code for this method is a bit out of date and uses an older version of jQuery. things have changed a bit since then with they way jQuery handles AJAX so Greg’s comment is very valid and useful. Ill try and put up a post showing the AJAX with a current jQuery version.
thanks for the feedback.
Yoav.
Thanks a lot Yoav,
Sorry I didn’t say what I think and should say before:
it is a great article very very useful and helpful, especially to new web dev like me.
Thanks a gain , well done.
This post offers clear idea in support of the new visitors of blogging, that genuinely how to do blogging.
Wow! This blog looks just like my old one!
It’s on a entirely different topic but it has pretty much the same page layout and design. Superb choice of colors!
Good post. I’m facing some of these issues as well..
Hi,
Im making use of the same feature. The change is that, I use Post instead of Get and the application i am working on is a portal application.
Everytime i run the app, it gives me a error that it cannot find the resource file i.e. the asmx file which I have added.
FYI, the asmx file is in the root of the project and the aspx file form which I am accessing the webservice is under the project in a folder like “content/abcd/xyz.aspx”.
what shoudl be the URL like?
Hi Jathin,
How are you setting the URL? can you share that piece of code?
Are you using it from javascript?
Basically it should point to “/myservice.asmx” if its indeed at the root of the site.
Yoav.
$.ajax({
type: “POST”,
url: “./myservice.asmx/HelloWorld”,
dataType: “xml”,
data: “sName=” + newName,
success: function (msg) { ajaxFinish(msg); },
error: function (msg) { ajaxError(msg); }
});
This is the code I am using.
URLs tried (ABC is the folder in the server in which the service is present and the web page from which I am accessing is present in ABC/Page/Content/page.aspx)
url: “./myservice.asmx/HelloWorld”
url: “/ABC/myservice.asmx/HelloWorld”
url: “~/ABC/myservice.asmx/HelloWorld”
url: “/portal/ABC/myservice.asmx/HelloWorld
Every iteration it gave the same error “Resource not found” and pointed to the web service for check whether it is spelt correct.
Hi,
You mentioned that the ASMX is at the root of your project. Would it then be accessible simply from: “/myservice.asmx”?
Your facebook like module is not working right, at least it isn’t on my end. Does not appear to work no matter what I do. Wanted to provide you with a like, but I can’t.
I’m sorry.
Amazing tutorial, thank you so much it works perfectly.