Invoking server side code using AJAX and .ashx handlers for Virtual Earth (Part 1)

Overview

The purpose of this article is to help beginners who may have experience using the Virtual Earth API but have no knowledge of how to move from using client side JavaScript to invoking server side backend using AJAX and .ashx handlers.  Essentially, the AJAX portion is the use of the XMLHttpRequest object which can communicate to the .ashx handler that in turn invokes the server side logic.  This can be applied to many situations such as plotting data from a SQL database, or leveraging the Mappoint Web Service to execute additional functionality not capable by the current version of Virtual Earth.  In this article we will create a basic web page with an AJAX/.ashx handler setup and then apply this to polling SQL results from a database.

The XMLHttpRequest Object

Start by creating a new web site called HTMLPost (file -> new -> website -> asp.net web site click “OK”) and add a new JavaScript file called AJAX.js (right click project -> add -> new item -> Jscript file -> click “Add”) to your web site.

Picture 1

Figure 1 - Creating a Web Site

ServerSide1.jpgPicture 10

Figure 2 - Adding a JavaScript file

Inside our AJAX.js file, we will need to initialize our XMLHttpRequest object.  When the request to the .ashx handler has finished completed, a function XMLHttpRequestCompleted will be invoked.  Add the following code:


function InitXmlHttp() {
    // Attempt to initialize xmlhttp object
    try
    {
        xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch (e)
    {
        // Try to use different activex object
        try
        {
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
        catch (E)
        {
            xmlhttp = false;
        }
    }
    
    // If not initialized, create XMLHttpRequest object
    if (!xmlhttp && typeof XMLHttpRequest!='undefined')
      {     
            xmlhttp = new XMLHttpRequest();
      }
      // Define function call for when Request obj state has changed
      xmlhttp.onreadystatechange=XMLHttpRequestCompleted;
}

Listing 1 – Initializing the XMLHttpRequest object

Also, we will invoke an .ashx file called FindNearby.ashx and set the asynchronous Boolean to true (to prevent hanging if an error occurs in our .ashx handler.


function InvokeASHX()
{
    InitXmlHttp();
    xmlhttp.onreadystatechange= XMLHttpRequestCompleted;
    xmlhttp.open("GET", "" + "Handler.ashx", true );
    xmlhttp.send(null);
}

Listing 2 – Invoking the .ashx handler

Finally, we will add a callback function XMLHttpRequestCompleted which will be invoked when our XMLHTTPRequest object has finished communicating with the .ashx handler.  Inside our callback function we will execute a JavaScript alert to show our handler response.


function XMLHttpRequestCompleted()
{
        if (xmlhttp.readyState==4)
    {
        try
        {
            alert(xmlhttp.responseText);
        }
        catch (e)
        {
        }
    }
}

Listing 3 – Handling the response from the .ashx handler

The .ashx Handler

Now we will create our .ashx handler which will handle take the AJAX request to execute our server side logic.  Right click your web project -> Add New Items -> Generic Handler -> Click “Add”

ashx.jpg

Figure 3 – Adding the .ashx handler

The file generated is basically a code behind file that implements the interface “IHttpHandler”.  Inside the ProcessRequest method we will handle our AJAX request (passed as an instance of HttpContext known as “context” by default).


<%@ WebHandler Language="C#" Class="Handler" %>

using System;
using System.Web;

public class Handler : IHttpHandler {
    
    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "text/plain";
        context.Response.Write("Hello World");
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }

}

Listing 4 – The .ashx handler generated code

Finally, include a reference to the AJAX.js file in the default.aspx page and call the InvokeASHX method.


<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="HtmlPost.WebForm1" %>

<!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>
 <script type="text/javascript" src="/Portals/0/AJAX.js"></script>
</head>
<body onload="InvokeAshx();">
    <form id="form1" runat="server">
    <div>
    
    </div>
    </form>
</body>
</html>

Listing 5 –Invoking AJAX.js on the default.aspx page

  

Combining Listing 1-5, you should now have a web site that outputs “Hello World” to the screen.

ServerSide2.jpg

Figure 4 - Invoking the .ashx handler from client side

  

Hello World Virtual Earth

To apply everything so far to Virtual Earth, begin by modifying the default.aspx file to include a Virtual Earth map.  We will call the InvokeASHX() method inline code for illustrative purposes:


<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="HtmlPost.WebForm1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
   <head>
      <title></title>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
      <script type="text/javascript" src="/Portals/0/AJAX.js"></script>      
      <script type="text/javascript" src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6"></script>
      <script type="text/javascript">
      var map = null;
            
      function GetMap()
      {
         map = new VEMap('myMap');
         map.LoadMap();
         InvokeASHX();
      }   
      </script>
   </head>
   <body onload="GetMap();">
      <div id='myMap' style="position:relative; width:400px; height:400px;"></div>
   </body>
</html>

Listing 6 – Setting up Virtual Earth

Inside our Handler.ashx file, adjust the ProcessRequest method to the following:


public void ProcessRequest(HttpContext context)
{
    context.Response.ContentType = "text/plain";
    context.Response.Write("var shape = new VEShape(VEShapeType.Pushpin, map.GetCenter());");
    context.Response.Write("shape.SetTitle(\'Hello World!\');");
    context.Response.Write("map.AddShape(shape);");        
}

Listing 7 – Sending up Virtual Earth requests from the .ashx handler

Finally in the AJAX.js file, we will evaluate the response from the handler rather than raising an alert:


function XMLHttpRequestCompleted()
{
        if (xmlhttp.readyState==4)
    {
        try
        {
            eval(xmlhttp.responseText);
        }
        catch (e)
        {
        }
    }
}

Listing 8 – Setting up AJAX.js to handle Virtual Earth requests

Picture 13

Figure 5 - Resulting VE page

Below this article is a sample web site you can download and then compile that demonstrates this simple Virtual Earth application.

Invoking a SQL Proximity Search inside the .ashx handler

Inside the ProcessRequest method of the .ashx handler, we can execute our server side logic and then pass up the content in an array of characters using the context.Response.Write method which will be evaluated by our client side JavaScript.  One practical example is to plot nearby locations from a SQL database onto our VE map.  The .ashx code below will execute a proximity search using SQL as outlined at http://msdn2.microsoft.com/en-us/library/ms980211.aspx and generate the JavaScript to add shapes onto a VE map (assuming that the database has been created as per the above MSDN article and that pushpin information is being appropriately returned to the drLocations SqlDataReader).


public void ProcessRequest(HttpContext context)
{
string connectionString = appSettings.Get("ConnectionString");
StringBuilder response = new StringBuilder();
connection = new SqlConnection(connectionString);
try
{
    connection.Open();
    SqlCommand cmdLocations = new SqlCommand("sp_FindNearby", connection);
    cmdLocations.CommandType = CommandType.StoredProcedure;
    cmdLocations.Parameters.Add("@Centerlat", SqlDbType.Float);
    cmdLocations.Parameters["@CenterLat"].Value = 45.21;
    cmdLocations.Parameters.Add("@CenterLon", SqlDbType.Float);
    cmdLocations.Parameters["@CenterLon"].Value = -71.5;
    cmdLocations.Parameters.Add("@SearchDistance", SqlDbType.Float);
    //Execute a FindNearby of 200 kilometers to the users entered location
    cmdLocations.Parameters["@SearchDistance"].Value = 200;
    //Using Earth Radius in Kilometers
    cmdLocations.Parameters.Add("@EarthRadius", SqlDbType.Float);
    cmdLocations.Parameters["@EarthRadius"].Value = 6378.1;
    cmdLocations.Parameters.Add("@StoreType", SqlDbType.VarChar);
    cmdLocations.Parameters["@StoreType"].Value = context.Request.Params.Get(0);
    SqlDataReader drLocations;
    drLocations = cmdLocations.ExecuteReader();
    while (drLocations.Read())
    {
        response.Append(String.Format("datashape = new VEShape(VEShapeType.Pushpin, new VELatLong({0},{1}));", drLocations[1],drLocations[2]));
        response.Append(String.Format("datashape.SetTitle('{0}');",drLocations[3]));               
        response.Append(String.Format("datashape.SetDescription('{0}');",drLocations[4]));                    
        response.Append(String.Format("map.AddShape(datashape);"));
    }
    context.Response.Write(response.ToString());
}			
catch (Exception ex)
{
    context.Response.Write("alert(\"" + ex.ToString() + "\")");
}
connection.Close();
}

Listing 9 – Executing a Proximity Search and passing the results back to Virtual Earth

This will look for the nearest locations 200 kilometers from the (45.21,-71.5) co-ordinate inside the table defined by the sp_FindNearby stored procedure.  We can make the following modifications to our JavaScript code to pass parameters to our handler and make our solution more robust:

- Add parameters to our xmlhttp.open method such that it looks like


xmlhttp.open("GET", "" + "Handler.ashx" + "?Latitude=40.21&Longitude=-71.5", true );

Listing 10 – Passing parameters to the .ashx handler

- And then get the parameters by using the context.Request.Params.Get method


cmdLocations.Parameters["@CenterLat"].Value = context.Request.Params.Get(0);
cmdLocations.Parameters["@CenterLon"].Value = context.Request.Params.Get(1);

Listing 11 – Processing the parameters through the .ashx handler

After applying these changes the proximity search should now be executing from a co-ordinate of (40.21,-71.5).

Conclusion

Adding an intermediate layer to your VE applications is essential in order to establish a bridge between client and server side communication.  In part 2, we will be looking at how to apply some useful functions from the Mappoint Web Service, Microsoft’s older mapping product onto a VE map.

Article contributed by Derek Chan (www.infusion.com). Have you got something to contribute?

Code Download
Copyright 2009. Sponsored by nsquared.   |  Terms Of Use  |  Privacy Statement
Content on this site is generated from the developer community and shared freely for your enjoyment and benefit. This site is run independently of Microsoft and does not express Microsoft's views in any way.