Skip navigation

This Just In

Add Most Recent Blog Posts to a Web Site

asp:Q&A

LANGUAGES: C# | VB.NET

ASP.NET VERSIONS: 1.x

 

This Just In

Add Most Recent Blog Posts to a Web Site

 

By Josef Finsel

 

Q. I am working on a Web site for a client who has a blog; she wants the most recent posts from her blog to show up on her Web site. Unfortunately, her blog host generates an atom.xml file rather than an RSS one, so I am relying on Feedburner.com to create the RSS translation for use on the Web page. The problem is that Feedburner doesn t know the atom file has been updated as soon as a new post has been created, which can lead to stale data. There is a service called ping-o-matic that can send Feedburner a message to update the feed for a specific blog, but I don t want to make my client go to that site every time they update their blog. Rather, I want to have the ping happen whenever someone hits the Web page. While that will not update the page for that visitor, it will update it for the next one. But I want this to be transparent to the visitor. How can I do this?

 

A. The trick to solving this problem is to have the server send the ping rather than the client. This is easy enough to do, especially for a site that uses QueryStrings (like the ping site does) because that only requires opening up the connection and everything required is in the URL. To provide a more universal appeal, however, we re going to write a class that will run on the server that will handle both POST and GET parameters.

 

Let s begin with the easiest possible scenario: having the server make a connection to a Web page. Add a class file with a reference to the System.Net namespace. All that s needed now is to create a WebClient and open a URL. Well, as I found out the hard way, it helps to wrap this in a Try..Catch statement because a 404 error will bubble up through your Web page. However, once we have the class defined, we can instantiate it in our PageLoad and have it open a Web page:

 

Dim c1 As ExternalWebCall = New ExternalWebCall

c1.CallExternalWebPage(

  http://localhost/200505/RequestFormSample.aspx )

 

With that much functionality in the class, the person who asked the question has a workable solution. But there is so much more this class could do, and thus it evolved into the full class shown in Figure 1 (this code and a C# version are available for download; see end of article for details). The first thing you ll notice is that I am using a StringDictionary to store the parameters. This private parameter has a public AddParameters method that does nothing but add a new name/value pair to the dictionary. Because this class will be re-instantiated every time it is used, I m not going to bother adding any means of removing or retrieving the items.

 

Imports System.Net

Imports System.Collections

Public Class ExternalWebCall

 Private ParameterDictionary As Collections.Specialized.StringDictionary _

 = New Collections.Specialized.StringDictionary

 Public Enum GetPostType

    None = 0

    FormGetType = 1

    FormPostType = 2

 End Enum

 Public Sub CallExternalWebPage(ByVal URL As String, ByVal GetPost _

 As GetPostType)

   Dim iConn As WebClient = New WebClient

   Dim OpenURL As String

   Try

      Select Case GetPost

         Case GetPostType.None

           iConn.OpenRead(URL)

         Case GetPostType.FormPostType

           OpenURL = URL & BuildPOSTParameterString()

           iConn.OpenRead(OpenURL)

         Case GetPostType.FormGetType

           iConn.Headers.Add("Content-Type", _

              "application/x-www-form-urlencoded")

              iConn.UploadValues(URL, BuildGETParameterString())

      End Select

   Catch ex As Exception

   End Try

 End Sub

 Public Sub AddParameters(ByVal Key As String, ByVal Value As String)

  ParameterDictionary.Add(Key, Value)

 End Sub

 Private Function BuildPOSTParameterString() As String

  Dim FirstEntry As Boolean = True

  Dim retVal As String = ""

  Dim de As DictionaryEntry

  For Each de In ParameterDictionary

      If FirstEntry Then

          retVal = retVal & "?"

          FirstEntry = False

      Else

          retVal = retVal & "&"

      End If

      retVal = retVal & de.Key & "=" & de.Value

  Next de

  Return retVal

 End Function

 Private Function BuildGETParameterString() As _

   Collections.Specialized.NameValueCollection

   Dim retVal As Collections.Specialized.NameValueCollection = _

    New Collections.Specialized.NameValueCollection

   Dim de As DictionaryEntry

   For Each de In ParameterDictionary

      retVal.Add(de.Key, de.Value)

   Next de

   Return retVal

 End Function

End Class

Figure 1: A class file to use the server to send data to other servers.

 

Now that we have a method for adding parameters, we need to add a mechanism for dealing with the differences between forms using GET and POST (QueryStrings, Params, and Forms Oh My! for more information on the differences between the two). POST parameters are the easier of the two since they are included in the URL as name-value pairs separated with & . A good example of this is:

 

http://localhost/2005/RequestFormSample.aspx?One=Yes&Two=No

 

For this, all we need to do is to walk the dictionary and retrieve the name-value pair and format it, remembering to use ? to signify the beginning of parameters. This is handled by BuildPOSTParameterString and adding that to the URL and opening the page.

 

GET is a little trickier. When sending data using GET, the data needs to be encapsulated in the HTTP stream as a true NameValueCollection. This is easily handled by a variation on the BuildPOSTParameterString and is found in the BuildGETParameterString. The tricky part is found in what we need to do to open a Web page expecting name-value pairs. Rather than using the OpenRead method, we need to use the UploadValues method and we need to inform the Web server that we are sending parameters, so we add a header defining application/x-www-form-urlencoded data.

 

Add a quick enumerator so that we don t need to remember what 0, 1, and 2 mean and we re done with building the class module. But we need to test it. In my initial testing, I was using IIS on a Windows 2003 server and the QueryString showed up in the log file, so it was easy to determine the code was working. When I switched over to my development machine, which runs Windows XP, I discovered that the IIS logging was a lot less detailed. But because I couldn t test the GET parameters in the log file, I added two more files. WebForm1.aspx (I like Microsoft s naming convention so much I decided to keep it) is a self-posting form that allows you to specify a URL (up to three parameters), which type of data (GET/POST/None) is the form expecting, and then submit the data to that form using the ExternalWebCall class. The second page, RequestFormSample.aspx, is the form that we are going to be pinging. It s set up to take any form data and dump the parameters out to a text file (see Figure 2).

 

Private Sub Page_Load(ByVal sender As System.Object, _

  ByVal e As System.EventArgs) Handles MyBase.Load

 If Request.QueryString.Count > 0 Then

   Dim strFilePath As String = Server.MapPath(".") & "\QOutput.txt"

   Dim sw As StreamWriter = File.CreateText(strFilePath)

   Dim iCount As Int16

   sw.WriteLine("This is a test!")

   For iCount = 0 To Request.QueryString.Count - 1

       sw.WriteLine(Request.QueryString(iCount))

   Next

   sw.Flush()

   sw.Close()

 End If

 If Request.Form.Count > 0 Then

    Dim strFilePath As String = Server.MapPath(".") & "\Output.txt"

    Dim sw As StreamWriter = File.CreateText(strFilePath)

    Dim iCount As Int16

    sw.WriteLine("This is a test!")

    For iCount = 0 To Request.Form.Count - 1

         sw.WriteLine(Request.Form.Item(iCount))

    Next

    sw.Flush()

    sw.Close()

 End If

End Sub

Figure 2: A generic post to file to demonstrate the class file is working.

 

The first thing RequestFormSample.aspx does is to determine if there are any parameters being sent. If so, it opens a text file within the Web directory and writes out the contents of the parameters.

 

Although this adequately solves the problem, there are still a couple of things to consider before implementing this solution. One is that if the Web page instantiating the class is heavily hit, it will be pinging a great deal, causing unnecessary network traffic. Even the prolific blogger Robert Scoble doesn t need to have his RSS feed updated that often. Not only that, there is a small amount of time taken up in instantiating the class and connecting, in addition to the memory used in the instantiation. To protect resources, it would be a good idea to add a session variable that checks the last time the class was instantiated and only update it after a suitable time interval has elapsed. On the other end of the spectrum, if the Web page is not hit for several days, the blog may be far ahead of where the RSS feed is and, while the person hitting the page will refresh it for the next user, it could appear dated for that user. In that case, I recommend creating a service to run on the server that would ping every X minutes or a job that gets scheduled to run. Unfortunately, the Web page is being hosted on a commercial server where that can t be implemented, so this class will have to suffice.

 

That wraps up this column. Remember, this is your column; it runs on the questions you ask, so feel free to send your ASP.NET questions to [email protected] so I can help you get the answers you need.

 

The sample code accompanying this article is available for download.

 

Josef Finsel is a software consultant with Strategic Data Solutions (http://www.sds-consulting.com). He has published a number of VB and SQL Server-related articles and is currently working with VS 2005. He s also author of The Handbook for Reluctant Database Administrators (Apress, 2001).

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish