Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

HomeLinks: What it is and how to implement it

2.33/5 (3 votes)
19 Jun 2007CPOL7 min read 1   108  
How to implement a mechanism to provide links to your web shared local files using XML, AJAX, and Web Services.

Introduction

There is no easier way to share files online than to upload them in your web space and provide others with the right links. Document files, spreadsheets, images, and MP3s are all small in size, and can be easily uploaded. But, what if you want to share a big bunch of files, MPG video files, for example? If you are unwilling to FTP 100MB to your web host, then you still have an alternative: host them yourself and provide links to the Web Server running at your computer. All you need is a static IP and the IIS (or any other web server) installed and running. There is one last caveat in this scenario. No matter how long your home computer can stay online, it will eventually be shutdown, which leads to broken links. Whether you turn off your computer for 5 minutes or 5 hours, broken links are always a nuisance. This is where HomeLinks come as a means of protection against broken links and annoyed web users! Plus, HomeLinks is a showcase for asynchronous, client-side JavaScript calls to XML Web Services!

Background

Before diving into the code, there are a few setup requirements to be met:

  1. You must have a Web Server running in your computer. The local folder where the homelinks.xml file and the other files you want to share online reside must be made publicly available through your web server. You can either create a virtual directory, or place the folder under the Web Server's root. In either case, the following test URL must be accessible:
    http://127.0.0.1/(VIRTUAL) DIRECTORY NAME
  2. Your Web Server must be reachable from outside your LAN. If your computer is behind a router/firewall, then you have to configure it in order to allow access to port 80 and forward incoming HTTP calls to the Web Server. Make sure you open port 80 in your (Windows) firewall settings too!
  3. You better have a static IP even though it is not mandatory. You can always use your current IP, dynamically assigned to you by your ISP, to run the sample application demonstrated here. Or, you can assign your dynamic IP to an automatically updated domain name so people can always access your Web Server. This can be done using free services such as DynDNS that allow you to select your domain name of preference, associate it with your dynamic IP, and have it updated automatically whenever your ISP changes your IP. In our case, the Web Server listens to the following dynamically assigned domain name no matter what my IP is:
    http://dpant.homeip.net

    and people can access our sample "rally_mavro_rodo" virtual directory using the URL:

    http://dpant.homeip.net/rally_mavro_rodo
  4. Apart from the homelinks.xml file, all other source files included in the source Zip file must be placed under a HomeLinks ASP.NET Web Application in your remote web host. If you have IIS installed locally, you can of course run the Web Application in your computer, but make sure you include the right URLs inside the homelinks.xml file (e.g., pseudo-static domain names won't work; use your local IP instead). Since the Web Application facilitates a Web Service, make sure you place the following lines in the web.config file:
    XML
    <system.web>
    ...
    <webServices>
    <protocols>
    <add name="HttpGet"/>
    <add name="HttpPost"/>
    </protocols>
    </webServices>
    </system.web>

Using the code

In Figure 1, you can see the HomeLinks architecture. HomeLinks is made up of the following components:

  • An XML Web Service (HomeLinksService.asmx).
  • The homelinks.xml file.
  • The service consumer (HomeLinks.htm or ShowHomeLinks.htm in the source Zip file).

Screenshot - HomeLinks_architecture.gif

Figure 1: The HomeLinks architecture

homelinks.xml

This file feeds the XML Web Service with the links that will be displayed to the end user. In other words, the HLink nodes are translated into HTML hyperlinks by the Web Service. The homelinks.xml file resides in the web-shared folder in your computer, so you can easily decide on which files will be available online by adding or removing HLink nodes:

XML
<?xml version="1.0" encoding="utf-8" ?>
    <HomeLinks>
       <HLink href="http://dpant.homeip.net/rally_mavro_rodo/MOV02573.MPG"
              target="_blank">Blaxokerasia 1 (2,4Mb)</HLink>
       <HLink href="http://dpant.homeip.net/rally_mavro_rodo/MOV02578.MPG"
              target="_blank">Blaxokerasia 2 (1,6Mb)</HLink>
       <HLink href="http://dpant.homeip.net/rally_mavro_rodo/MOV02579A.WMV"
              target="_blank">Blaxokerasia 3 (1.9Mb)</HLink>
       ...
    <HomeLinks>

The XML Web Service

In order to use the Web Service, we call the HomeLinks method which accepts one parameter: the URI where the homelinks.xml file is located. In our example, we provide the http://dpant.homeip.net/rally_mavro_rodo URL. The Web Service tries to access the homelinks.xml file, uses the MSXML parser to read the HLink nodes, and creates and returns an XML-formatted document of HTML A links. For example, the first HLink node above results in this HTML link:

<a href="http://dpant.homeip.net/rally_mavro_rodo/MOV02573.MPG" 
   target="_blank">Blaxokerasia 1 (2,4Mb)</a>

If the homelinks.xml file is unreachable (your computer is shutdown, offline, or your Web Server is stopped), the Web Service simply returns an error message (NO_HOMELINKS) stating the fact. In this way, we avoid presenting the users with broken links. The XML Web Service is written in C#. The HomeLinks method uses MSXML on the server-side by importing the System.Xml .NET namespace. The Load method of the XmlDocument object is used to access the XML document, the documentElement property represents the root element of the XML file loaded in memory, and the SelectNodes method allows us to apply XPath Expressions to XML data:

C#
public class HomeLinksService : System.Web.Services.WebService { 
    const string NO_HOMELINKS = 
          "The {0} URI is currently unreachable (probably offline)";
    const string BR = "<br/>";
    const string A = "<a href='{0}' target='{1}'>{2}</a>";
    [WebMethod]
    public string HomeLinks(string URI) {
        XmlDocument hlDoc = new XmlDocument();
        try 
        {
            string links = String.Empty;
            hlDoc.Load(FixURI(URI) + "homelinks.xml");
            XmlNodeList hlinks = hlDoc.DocumentElement.SelectNodes("HLink");
            for (int i = 0; i < hlinks.Count; i++)
            {
                links += String.Format(A, hlinks[i].Attributes["href"].Value, 
                  ><>hlinks[i].Attributes["target"].Value, hlinks[i].InnerText) + BR;
            }
            return links;
        }
        catch (Exception) {
            return String.Format(NO_HOMELINKS, URI);
        }
    }
}

The service consumer

Figure 1 shows two possible Web Service consumers. The HomeLinks.htm (or HomeLinks.htm in the source Zip file) file can be either part of the web application (Consumer A) or can be located anywhere else on the Internet (Consumer B). Unfortunately, Consumer B is restrained by security issues of the XmlHttpRequest object, and will only work with Internet Explorer 6. Either way, we access the Web Service using the XmlHttpRequest object through JavaScript. This is an asynchronous, client-side, GET HTTP call to a Web Server resource. This can be anything from a simple text or HTML file to an ASPX/PHP/Java/CGI script. The onreadystatechange callback function is called when a result is returned by the server. Web Services always return results in XML format. Pay attention to the readyState and status properties of the XmlHttpRequest object. They indicate whether the asynchronous call was successful or not. Last but not least, please note that the XmlHttpRequest object instance creation is browser-dependent. Here is the full script code:

JavaScript
<script type="text/javascript">
    var req = false;
    function getHomeLinks() {
        // Obtain the XMLHttpRequest object for ...

        if (window.ActiveXObject)
            // ... IE

            req = new ActiveXObject("Msxml2.XMLHTTP");
        else if (window.XMLHttpRequest)
            // ... or Mozilla

            req = new XMLHttpRequest();

        if (req) {
            // Call the XML Web Service and pass the URI location of the

            // homelinks.xml file

            req.open("GET", "HomeLinksService.asmx/HomeLinks?" + 
                     "URI=http://dpant.homeip.net/rally_mavro_rodo", true);
            req.onreadystatechange = function() {
                // Callback inline function

                if (req.readyState == 4) {
                    if (req.status == 200)
                        // Simply display the HTML hyperlinks inside the divHL DIV

                        document.getElementById("divHL").innerHTML = 
                                 req.responseXML.documentElement.firstChild.nodeValue;
                    else
                        // The AJAX response indicates
                        // error - display verbose error message

                        document.getElementById("divHL").innerHTML = "readyState = " + 
                                 req.readyState + ", status = " + 
                                 req.status + " (" + req.statusText + ")"; 
                    // either case, no more waiting

                    document.getElementById("divWait").style.visibility = "hidden";
                }
                else {
                    // display wait popup while request is in progress

                    document.getElementById("divWait").style.visibility = "visible";
                }
            }
            // initiate the AJAX request

            req.send(null);
        }
        else {
            // XHR object not supported - abort

            document.write("Unable to create the XMLHttpRequest object.");
        }
    }
</script>

and here is the call within HomeLinks.htm (or ShowHomeLinks.htm in the source Zip file):

HTML
<body onload="getHomeLinks(); return true;">
<div>
    <div id="divHL"></div>
    <div id="divWait" 
       style="left: 50%; top: 50%; margin-top: 222px; 
              margin-left: -113px; position: absolute; visibility: hidden; 
              vertical-align: middle; width: 226px;
              color: #3366cc; height: 111px; background-color: #ffff99;
              text-align: center; border-right: #3333cc 1px solid; 
              border-top: #3333cc 1px solid; border-left: #3333cc 1px solid; 
              border-bottom: #3333cc 1px solid; top: 0px;">
        <br /><br /> HomeLinks loading, Please wait ...
    </div>
</div>
</body>

Sample Application Results

In our example, the hyperlinks point to a bunch of MPG video files (with funny, Greek names) taken from a Rally race event. You can check them out in my blog post. Of course, the HomeLinks will be available only if my computer is up and running ;)

Here are two screenshots of our sample application:

Screenshot - HomeLinks_Sample_Success.png

Screenshot 1: Successful execution

Screenshot - HomeLinks_Sample_Error.png

Screenshot 2: HomeLinks as a means to prevent broken links

Points of interest

How to debug

It is most certain that it will take you quite a while to debug this project. Web Services and the XmlHttpRequest object can prove to be a real big pain especially with all the hideous error messages and no debugging tools. There are also security and configuration issues that will block your way to success. Here, I list the most common pitfalls you will encounter:

XmlHttpRequest returns Internal Server Error (status = 500)

  • Make sure you have added the following configuration lines to the web.config file of your ASP.NET Web Application:
    XML
    <system.web>
       <webServices>
           <protocols>
                <add name="HttpGet"/>
                <add name="HttpPost"/>
           </protocols>
       </webServices>
    </system.web>
  • Make sure you use the GET HTTP method to call the Web Service and pass the correct parameter(s).
  • Make sure you display the readyState, status, and statusText values in a DIV or TEXTAREA object. Do not use document.write() for this purpose.

XmlHttpRequest returns Not Found (status = 404)

  • Make sure you use the correct URL to the .asmx file.
  • The Web Service method calls are case sensitive. In our example, type HomeLinks, and not homeLinks or homelinks.

JavaScript error: uncaught exception: Permission denied to call method XmlHttpRequest.open

  • Make sure you place the consumer file in the same domain as the Web Service. This is a security constraint imposed by browsers. Internet Explorer will warn you, but will finally make the call unlike Mozilla Firefox.

JavaScript returns nothing

  • Make sure you display the readyState, status, and statusText values. Do not use document.write for this purpose as this is an asynchronous call and the latest results will override the previous ones. Use DIV or TEXTAREA objects instead.
  • Make sure you display the XML response inside a DIV or TEXTAREA object. Do not use document.write for this purpose.
  • Make sure you handle the XML document correctly.
  • Use the Firefox Error Console to check for coding errors.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)