Wednesday, May 5, 2010

Calling web service using javascript. Part -3

In the part 1 of this blog we saw how to invoke web service methods using pure javascript and in part 2 we made use of web service behavior to invoke web methods. The previous two blogs were not dependent on server technologies used by the user but this blog will have that constraint because we will have a look at how to invoke web methods using “ScriptManager”. “ScriptManager” is an ASP.NET server side control which is key to AJAX functionality in ASP.NET. Without any further delay let’ see our first web method invocation using “ScriptManager”. For this blog we will making use of the same web service as we have used in our previous two blogs. Below is the code for the first web service method we are going to invoke.

namespace ScriptManagerService
{
    //The attribute which makes the web service callable from script.
    [System.Web.Script.Services.ScriptService]
    public class TestService : System.Web.Services.WebService
    {
        [WebMethod]
        public string HelloWorld()
        {
            return "Hello World";
        }
    }
}

The above web service code is pretty straight forward. We have a simple “HelloWorld” method which returns a string. What makes this web service special is the “[System.Web.Script.Services.ScriptService]” attribute added on top of the “TestService” web service class. The attribute makes the web service callable from JavaScript. Also when the proxy classes are generated the attribute generates JavaScript object corresponding to the web service class. Once the web service is created now we need to create our web page to invoke the web method. Create a new aspx page and add a “ScriptManager” control to it. Sample aspx page with the “ScriptManager” tag is added below.

<body>
<script language="javascript" type="text/javascript">
function invokeSimpleWebMethod()
{
    ScriptManagerService.TestService.HelloWorld(handleResult);    
} 
 
function handleResult(result)
{
    alert(result);
}
</script>
   <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="scrMgr" runat="server">
            <Services>
                <asp:ServiceReference Path="TestService.asmx" />                
            </Services>
        </asp:ScriptManager>
        <asp:Button ID="btnHW" runat="server" Text="Hello World" OnClientClick="invokeSimpleWebMethod();" />
    </div>
    </form> 
</body>

In the above code we can see that we have a “ScriptManager” server control added. And inside the “Services” tag we have added reference to our local web service using the “ServiceReference” tag. The path attribute of the “ServiceReference” tag has the url of the web service which needs to be invoked. You can add multiple web services by adding additional “ServiceReference” tags. What ASP.NET does here is that it generates JavaScript proxy classes for each of the web services mentioned in the “ServiceReference” tag. All the auto generated JavaScript proxy classes derive from “Sys.Net.WebServiceProxy”.

Also we have an ASP.NET Button server control which calls a “invokeSimpleWebMethod” javascript method on its “OnClientClick” event. In the “invokeSimpleWebMethod” javascript method we have made use of the namespace (in which the web service is defined) followed by the web service name and finally the method name which needs to be invoked. Behind the scene ASP.NET has done the extra work of registering the namespace and creating the proxy classes and also adding the methods defined inside the web service into a proxy JavaScript class. If you notice the “HelloWorld” method takes an argument. The argument is nothing but the name of the JavaScript function which needs to be invoked when the web service method has been successfully executed and results are returned to the browser. You can also pass the name of the function which needs to be invoked when the web service method invocation request fails. We will see the same shortly. The “handleResult” JavaScript method gets called when the asynchronous request is successfully returned. The method gracefully displays the result in an alert message box.

That’ about how to use the “ScriptManager” server control to invoke a web service methods i.e. namespace followed by class name followed by the method name. One thing to note is that “ScriptManager” control can only be used to call web services in the same domain. Now lets see how to pass parameters to web methods.

Passing parameters to web method

We will use the following web method to see how we can pass parameters by using “ScriptManager”.

[WebMethod]
public string CombineName(string firstName, string midleName, string lastName)
{
      return string.Concat(string.Concat(firstName, " - ", midleName), " - ", lastName);
}

The above method is pretty straight and it is the same method which I have used in my previous blog. The method takes three string arguments and concatenates them and returns them as a single string. Now lets see how we can pass the three parameters from JavaScript.

Below is the JavaScript method which invokes the web method with the three string parameters.

function webMethodWithParams()
{    
    ScriptManagerService.TestService.CombineName("Sandeep", " ", "P.R", handleResult, handleError);
}

In the above JavaScript function we are invoking the “CombineName” method the same way we invoked the “HelloWorld” web method. But the difference here is that instead of the callback function as the first argument we are passing the arguments first, followed by the success callback function name and then the failure callback function. The funda behind is simple, if your web method accepted three parameters pass them first in the same order as defined in the web method followed by the callback methods. Similarly if the web method takes different number of parameters then make sure that, that many number of parameters are passed first followed by the success callback function. Success callback and error callback functions are optional. Our “handleError” javascript function looks something like this.
function handleError(error)
{
    alert(error.get_message());
}

The above “handleError” javascript function is pretty straight forward. It takes an error object as one of the argument. The error object passed by the system has various methods and properties to retrieve the details of the exception happened on the server side. Some of the methods and properties are as follows.

  1. get_exceptionType method or _exceptionType field/property can be used to get the type of exception raised by the server like “System.Exception”.
  2. get_message method or _message field/property can be used to get the error message returned by the server.
  3. _stackTrace field/property or get_stackTrace method can be used to get the stack trace of the error from the server.
  4. get_statusCode method or _statusCode field/property can be used to retrieve the HTML status code returned from the server.

The error object which is passed to the “handleError” JavaScript function is of type “Sys.Net.WebServiceError”. “Sys.Net.WebServiceError” is a simple class with the above explained methods or fields/properties.

Now the next step is to pass complex objects from JavaScript to a web method. Lets see how that can be accomplished.

Passing complex objects

Let see the code of the web method which accepts a complex object and processes it.

[WebMethod]
public string GetCar(Car car)
{
    return "Model: " + car.Model + ", Color: " + car.Color; //", Cubic capacity: ";//+ car.CC + 
}

The above web method doesn’t have rocket science in it, it just takes a complex object of type “Car” and concatenates the properties and returns it as a string. Now lets the “Car” class. Its same as that we have used in our previous examples.

public class Car
{
    public string Model
    {   get; set; }          
 
    public string Color
    { get; set; }
 
    public Engine CarEngine
    { get; set; }
}
 
public class Engine
{
    public int CC
    { get; set; }
    public int Cylinders
    { get; set; }
}

The “Car” class has three properties namely “Model”, “Color” and “CarEngine”. “Model” and “Color” are of type string and “CarEngine” is again a complex object of type “Engine”. The “Engine” class has two properties of type int namely “CC” and “Cylinders”. The above “Car” class is the one which we will be passing to the web method from within JavaScript using ScripManager. The code is pasted below.

function sendComplexObject()
{
    //Create a new Object and assign the same properties as those in the Car class.
    var car = new Object();
    car.Model = "Ferrari California"
    car.Color = "Ferrari Red"
    car.CarEngine = new Object()
    car.CarEngine.CC = 4500
    car.CarEngine.Cylinders = 12
    //Execute the web method.
    ScriptManagerService.TestService.GetCar(car, handleResult, handleError);
}

In the “sendComplexObject” method we are first creating a “Car” object by creating an object of type “Object”. Then to that object we are adding all the properties that the “Car” class has. Since JavaScript is a dynamic language you can add all the properties by just giving the name and the value. As the “Car” class has another complex object, Engine, as one of its property named “CarEngine” we are also creating the complex engine object also the same way. Once we have created the object we are finally executing the web method in the final line of code. Isn’t simple to pass complex objects from JavaScript to server. To help us to do write such a simple code the ScriptManager does the extra work converting the JavaScript objects to server objects.

Below is the JSON way of creating the complex Car object.

function sendComplexObject()
{
    //Create an object of type "ScriptManagerService.Car" and pass all the properties 
    //as a javascript string array.
    var car = new ScriptManagerService.Car({"Model" : "Ferrari California", "Color" : "Ferrari Red", "CarEngine" : {"CC" : "100", "Cylinders" : "5"}})
    //Invoke the web method.
    ScriptManagerService.TestService.GetCar(car, handleResult, handleError);
}

In the above code we are trying to create a “ScriptManagerService.Car” object. This is possible because the ScriptManager generates the Proxy classes automatically for us. Now lets see handling a complex type returned from the server. It also is quite simple.

Handling a complex object returned by the server

Lets see the web service which returns the complex object.

[WebMethod]
public Car GetCarObject()
{
    return new Car
    {
        Model = "Ferrari",
        Color = "Ferrari Red",
        CarEngine = new Engine { CC = 2500, Cylinders = 8 },
        test = "test"
    };
}

The above web method does the hard work of creating a car object and returning the same. We are going to invoke the above web method using the ScriptManager and use the very complex object in JavaScript. The JavaScript code is pasted below.

function receiveUseComplexObject()
{
    //Invoking the web method that returns complex object.
    ScriptManagerService.TestService.GetCarObject(handleResult, handleError);
}
 
//Modified handleResult function
function handleResult(result)
{
    //Using the complex object returned by the web method in JavaScript.
    var carDef = "Model: " + result.Model + ", Color: " + result.Color + ", Engine-CC: " + result.CarEngine.CC;
    carDef += ", Engine-Cylinders: " + result.CarEngine.Cylinders;
    alert(carDef);
}

In the “receiveUseComplexObject” we are not doing any drastic than invoking the “GetCarObject” web method. The main work is done by the modified “handleResult” function. The web method returns a “Car” object, the parameter of the “handleResult” also is the JavaScript proxy “Car” object representing the server object. The ScriptManager does all the work of converting the returned object as a JSON “Car” object. This is why we are able to use the properties of the “Car” object as we would do in a normal OOPS programming language.

Calling methods in a page

Using “ScriptManager” one can access method of a web page as well. One catch here is that you can execute only static page methods. To call a page method from JavaScript by making use of “ScriptManger” you need to add the “WebMethod” attribute to the method you want to call. Lets see “.cs” file of a web page.

public partial class TestPage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {    }
 
    [System.Web.Services.WebMethod]
    public static string HelloWorld()
    {
        return "Hello world";
    }        
}

As mentioned above, we have added the “WebMethod” attribute to a static method and that’ it you need to do to make a page method callable by “ScriptManager”. In “ScriptManager” you need to set “EnablePageMethods” property to “true”. What this property does is it generates a “PageMethods” JavaScript class which has the necessary code to call all the static page methods which are decorated by the “WebMethod” attribute. Lets see the ASPX page and the JavaScript.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebSerCalServiceMngr.aspx.cs" Inherits="WebServiceUsingJS.WebSerCalServiceMngr" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
<script language="javascript" type="text/javascript">
function callPageMethods()
{
    PageMethods.HelloWorld(handleResult);
}
</script>
 
<form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="scrMgr" runat="server" EnablePageMethods="true">   
        </asp:ScriptManager>
<asp:Button ID="Button3" runat="server" Text="Call page method." OnClientClick="callPageMethods();" />
    </div>
    </form>
</body>
</html>

In the above code we have set “EnablePageMethods” property to true and on the click of a button we are calling “callPageMethods()” JavaScript function. Inside the “callPageMethods” JavaScript function we are making use of “PageMethods” proxy class to execute our page method. So with “ScriptManager” doing all the extra work at the back, as a developer we are having great time.


Try to know more


Sandeep.

7 comments:

  1. http://localhost/TestWeb/TestService.asmx

    Parser Error Message: The page must have a
    <%@ webservice class="MyNamespace.MyClass" ... %> directive.

    ReplyDelete
  2. TestService.asmx file :

    namespace ScriptManagerService
    {
    [System.Web.Script.Services.ScriptService]
    public class TestService : System.Web.Services.WebService
    {
    [WebMethod]
    public string HelloWorld()
    {
    return "Hello World";
    }
    }
    }

    ReplyDelete
  3. http://pantestmb.blogspot.com/2011/02/unknown-server-tag-aspscriptmanager.html

    I've solved my problem :
    Must have a config file ...

    ReplyDelete
  4. im getting 'PageMethods' is undefined
    using in Kelitte Dashboard usercontrol

    ReplyDelete
  5. Great job!
    Thanks for sharing.

    ReplyDelete
  6. This is the most important advantage of Web Alternatives. Web Alternatives generally work outside of personal systems, providing designers a non-proprietary path to their solutions.

    ReplyDelete
  7. It was very nice article and it is very useful to
    Web Method learners.We also provide Web Method online training

    ReplyDelete

Please provide your valuable comments.