Web developer from Sydney Australia. Currently using asp.net, mvc where possible.

Monday, November 26, 2007

Forget Page Methods move on to Script Services

This entry is to guide you through the transition from the page method to the script service. Don't worry it won't hurt a bit...

Full Sample Code

Using page methods is a great idea for getting more performance out of your ajax apps but if they have a few draw backs

  1. They don’t promote reusability, user controls methods would but sadly they missed the boat.

  2. Getting them to actually work on a hosted server is somewhat of a mistory (see: http://www.west-wind.com/WebLog/posts/152493.aspx)

The best thing about page methods is that the converted me to using Web Services or Script Services. Script services are regular web services with a few extra attributes. The good thing is that you can have a javascript proxy built for you just like a page method. Better still, the web service is actually usable from anywhere...

The hello world of the script service.

Fire up visual studio and start a new Web Project (or web site if you must) .

Add a new Web Service

The Hello world template should be already present, as below .... (I changed the method name to ‘Hello’ so it’s different from the class name)

/// <summary>
/// Summary description for HelloWorld
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
public class HelloWorld : System.Web.Services.WebService
{

[WebMethod]
public string Hello()
{
return "Hello World";
}
}

The add a reference to the System.Web.Extensions library and add the script service attribute to the class as below.
using System;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.ComponentModel;

using System.Web.Script.Services;

namespace ScriptServiceExample
{
/// <summary>
/// Summary description for HelloWorld
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[ScriptService]
public class HelloWorld : System.Web.Services.WebService
{

[WebMethod]
public string Hello()
{
return "Hello World";
}
}
}
That’s it. Done. We have converted out web service into a script service. Now we will move onto the default.aspx file and consume our script service.

Firstly check you web.confg has the necessary handlers defined.

<httphandlers>
<remove verb="*" path="*.asmx">
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false">
<add verb="*" path="*.asmx" type="System.Web.Script.Services.ScriptHandlerFactory" validate="false">
</add>

</add></remove></httphandlers>
Now, Add a ScriptManger to the page and define the script service as follows.
<asp:scriptmanager id="ScriptManager1" runat="server">
<services>
<asp:servicereference path="/HelloWorld.asmx">
</asp:servicereference>
</services>

</asp:scriptmanager>
The web page will compile and run at this stage but viewing a blank page less than impressive.

If you got firebug or other means you can run the page and check that the following javascript is actually generated and linked to the page..

Type.registerNamespace('ScriptServiceExample');
ScriptServiceExample.HelloWorld=function() {
ScriptServiceExample.HelloWorld.initializeBase(this);
this._timeout = 0;
this._userContext = null;
this._succeeded = null;
this._failed = null;
}
ScriptServiceExample.HelloWorld.prototype={
Hello:function(succeededCallback, failedCallback, userContext) {
return this._invoke(ScriptServiceExample.HelloWorld.get_path(), 'Hello',false,{},succeededCallback,failedCallback,userContext); }}
ScriptServiceExample.HelloWorld.registerClass('ScriptServiceExample.HelloWorld',Sys.Net.WebServiceProxy);
ScriptServiceExample.HelloWorld._staticInstance = new ScriptServiceExample.HelloWorld();
ScriptServiceExample.HelloWorld.set_path = function(value) {
var e = Function._validateParams(arguments, [{name: 'path', type: String}]); if (e) throw e; ScriptServiceExample.HelloWorld._staticInstance._path = value; }
ScriptServiceExample.HelloWorld.get_path = function() { return ScriptServiceExample.HelloWorld._staticInstance._path; }
ScriptServiceExample.HelloWorld.set_timeout = function(value) { var e = Function._validateParams(arguments, [{name: 'timeout', type: Number}]); if (e) throw e; if (value < _timeout =" value;" get_timeout =" function()" set_defaultusercontext =" function(value)" _usercontext =" value;" get_defaultusercontext =" function()" set_defaultsucceededcallback =" function(value)" e =" Function._validateParams(arguments," _succeeded =" value;" get_defaultsucceededcallback =" function()" set_defaultfailedcallback =" function(value)" e =" Function._validateParams(arguments," _failed =" value;" get_defaultfailedcallback =" function()" hello=" function(onSuccess,onFailed,userContext)" href="http://www.blogger.com/post-edit.g?blogID=3340352991227979961&postID=790319424207465397">Ping the server
This will not work yet because the function is defined as “ScriptServiceExample.HelloWorld.Hello” but just to encapsulate this call I am going to add an helper javascript file called HelloWorldHelper.js

The final html looks like this..

<form id="form1" runat="server">


<div>
<asp:scriptmanager id="ScriptManager1" runat="server">
<services>
<asp:servicereference path="/HelloWorld.asmx">
</asp:servicereference>
</services>

<script src="helloWorldHelper.js" type="text/javascript"></script>
<a href="javascript: Hello()">Ping the server</a>

</asp:scriptmanager><div id="processing" style="display: none;">
fetching
</div>
<div id="results">
</div>

</div>
</form>
NB: You should move the script include into the head of your document

The javascript file looks like this..

function Hello()
{
$get('processing').style.display = 'block';
ScriptServiceExample.HelloWorld.Hello(SuccessCallBack);
}

function SuccessCallBack(result)
{
$get('results').innerHTML = result;
$get('processing').style.display = 'none';
}

That’s it, consuming your script service couldn’t be easier.

Sending complex results to your web page is as easy as returning an object from your web service. The object will automatically be serilised using JSON (although you can specify XML if you want).

Quick code for returning an object.

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[ScriptService]
public class HelloWorld : System.Web.Services.WebService
{

[WebMethod]
public HelloWorldResponse Hello()
{
HelloWorldResponse r = new HelloWorldResponse();
r.Message = "Hello World";
r.Time = DateTime.Now;
return r;
}
}

[Serializable]
public class HelloWorldResponse
{
public string Message;
public DateTime Time;
}

Comsuming the object, client side

function Hello()
{
$get('processing').style.display = 'block';
ScriptServiceExample.HelloWorld.Hello(SuccessCallBack);
}

function SuccessCallBack(result)
{
var resultObject = eval(result)
$get('results').innerHTML = "The Message is:" + resultObject.Message + " The message was created on " + resultObject.Time;
$get('processing').style.display = 'none';
}

Visit the offical site for more info

4 comments:

Anonymous said...

Interesting to know.

Ramesh Janjyam said...

my onsuccess method is not called.. Could you please help me out.

hobbes_child said...

I've downloaded your sample code and if I use the Visual Studio Development Server, it works fine, but if I use IIS then I get a JS error on clicking the link:
'ScriptServiceExample' is undefined.

Can you please advise? I need it to work in IIS. Many thanks.

Unknown said...

keep up the good work! this blog is really interesting and gives good details.

Thanks...

Mechanical Seals