Avatar

Blog (pg. 19)

  • Published on
    In ASP.NET controls you can use the tilde '~' character to signify the virtual application root of the website, for example when sourcing an image for an ImageButton. Code:
      <asp:ImageButton ID="ImageButton1" runat="server" AlternateText="Example Button" ImageUrl="~/images/button.png" />
    This is fine for most cases. However, when using URL rewriting (via HttpHandlers) it can become a problem, since ASP.NET will calculate the path to the resource based on the location of the physical page it is rendering (not the page requesed in the browser [RawUrl]). If the resource is in a directory below the page directory, then it will render a relative path to the HTML, (and not an absolute path from the app root). When your browser sees a relative path in the HTML it appends that to the current working directory to retreive the resource, which for a page being served up via a rewrite, is probably the wrong directory. For example, imagine you have the following directory structure: /MyButtonPage.aspx /images/button.png /subdir/page-served-by-httphandler-mapping-to-mybuttonpage.aspx If you view '/MyButtonPage.aspx' (containing the code from above) the output path to the image would be 'images/button.png'. Your browser will therefore retreive '/images/button.png' which is correct. However, if you view '/subdir/page-served-by-httphandler-mapping-to-mybuttonpage.aspx' (which rewrites internally the '/MyButtonPage.aspx') then ASP.NET will again render the image path as 'images/button.png' (since it is relative to the 'MyButtonPage.aspx' which was rendered internally). Your browser will now try to retrieve, '/subdir/images/button.png' which does not exist. A workaround for this problem is to not use the tilde mechanism and instead reference the absolute path to the resource in the code. e.g.
      <asp:ImageButton ID="ImageButton1" runat="server" AlternateText="Example Button" ImageUrl="/images/button.png" />
    However, this will reference from the website root, not the application root, so be careful if your site runs as a virtual application within another site.
  • Published on
    When using URL rewriting to make nice URLs without querystrings, it is useful to include a meaningful name for the page, i.e. the page title, in the URL itself. This helps usability in distinguishing pages and also aids search engines in spidering different content that may be generated by a single page on your server. For some reason, the HttpUtility.UrlEncode and HttpUtility.HtmlEncode just dont cut it for making good URLs that are readable, optimized and work on the browser/server. I have written my own class for cleaning/encoding text into a form that can be used as part of a URL that will be used in a rewriting engine. Code:
    Imports System.Text.RegularExpressions
    
    Public Class UrlEncoder
        Public Shared Function EncodeRewriteUrl(ByVal inputString As String) As String
            Dim url As String = Regex.Replace(System.Web.HttpUtility.HtmlDecode(inputString.Replace(" ", "-")), "[^a-zA-Z0-9\-]", "")
            While url.Contains("--")
                url = url.Replace("--", "-")
            End While
            Return url
        End Function
    End Class
    The idea here is that you pass in the text you want to use as your URL, and you receive a hyphenated URL containing only URL safe characters ready for use with your rewriting engine. Example: if.. pageId=1 pageTitle="A page about 'dogs' and 'cats' (also other things)" and.. pageUrl=UrlEncoder.EncodeRewriteUrl(pageTitle & "-" & pageId & ".aspx") then.. The page URL would be: A-page-about-dogs-and-cats-also-other-things-1.aspx
  • Published on
    After recently starting this blog using my Blogger account and its FTP publishing feature I wanted a more seamless integration with my existing site for the index page of the blog. In other words, I wanted to show the latest blogs as part of my existing site using the masterpage I have in ASP.NET. To achive this goal, I have set Blogger to publish to /blog on my domain, with a proprietary filename and .html extension. I have then put a Default.aspx page in the /blog directory, which uses the site master page. This default page basically opens the .html file server side and writes the stripped out relavent HTML content to a placeholder in the ASP.NET page. I have included some of the Blogger specific meta/link tags in the .aspx page head, to enable the RSS/Atom feeds from the page and to enable the edit post controls etc. to work. As its ASP.NET you can also add extra features to the page. I have added a drop down list of the categories (Blogger labels) to use as a quick link to each category. Blogger.com creates a .html page for each of your labels containing links to the relevant posts, these files are stored under the 'labels' folder, which can be simply enumerated on the server. Code:
    if not IsPostback then
    
       try
    
        Dim file As String = "blog-content.html"
    
        Response.Cache.SetLastModified(IO.File.GetLastWriteTime(Server.MapPath(file)))
    
        dim content as string=IO.File.ReadAllText(Server.MapPath(file))
        content = content.Substring(content.IndexOf("<body>") + "<body>".Length, content.LastIndexOf("</body>") - content.IndexOf("<body>") - "<body>".Length)
        content = content.Remove(content.IndexOf("<iframe"), content.IndexOf("</iframe>") + "</iframe>".Length - content.IndexOf("<iframe"))
    
        lblContent.Text=content
    
        'load the labels
        dim labelFiles() as string=IO.Directory.GetFiles(Server.MapPath("labels"))
    
        ddlCats.Items.Clear
        ddlCats.Items.Add(New ListItem("--Jump To Category--",""))  
    
        for each filename as string in labelFiles
    
         dim url as string=filename.substring(filename.lastindexof("\")+1,filename.length-filename.lastindexof("\")-1)
    
         ddlCats.Items.Add(New ListItem(url.replace(".html",""),url))
    
        next
    
       catch ex as exception
    
        lblContent.Text="Error: " & ex.Message
    
       end try
    My site only uses this method for the index page, but you could extend this idea even further to display all blog pages within your custom ASP.NET / masterpage site, by putting the /blog directory under a custom HttpHandler (if your server allows this) and rewriting the URL to your ASP.NET page. Also I would suggest using regular expressions for filtering the HTML if your server supports it. -- Edit: 25/11/2008 I have now improved the integration by using the HTML generated by my masterpage as the template on Blogger.com, so all the pages look like you are on my website. This means I could refine the above process of finding the correct content for each placeholder, (with the addition of a seperate placeholder for the sidebar) as the divs that contain the data have specific class names. I have also created two more aspx pages, "index by date" and "index by category" - the code behind for these simply enumerate the "archives"/"labels" folders on the server, (the same as my drop down box above). Additionally, these pages open each .html file to parse the content for all of the post titles and permalinks to create the list of links for each date/label. -- Edit: 18/10/2010 Since my blog no longer uses Blogger due to FTP publishing being cancelled this code is now redundant.
  • Published on
    In ASP.NET, setting the default button of a textbox is usually a simple task as you simply wrap the button and textbox in an asp:panel and set the 'defaultbutton' attribute on the panel. Sometimes however, it is not possible to put the button and textbox in a panel together, for example if the textbox is part of a databound template (e.g. gridview, repeater etc.) If you look at the HTML code that the asp:panel method generates, it basically attaches a javascript event handler to the 'onkeypress' of the textbox. I have wrapped this functionality, so that you can programmatically set the default button of any textbox control on your page, to any button on your page. Code:
    Public Shared Sub SetDefaultButton(ByVal textbox As System.Web.UI.WebControls.TextBox, ByVal button As System.Web.UI.WebControls.IButtonControl)
     If TypeOf button Is System.Web.UI.WebControls.WebControl Then
     textbox.Attributes("onkeypress") = "javascript:if (event.keyCode == 13){ document.getElementById('" & CType(button, System.Web.UI.WebControls.WebControl).ClientID & "').click();event.cancelBubble = true;if (event.stopPropagation) event.stopPropagation();return false;}"
     End If
    
    End Sub
  • Published on
    I recently wrote a wrapper class implementing the .NET System.Security.Cryptography.TripleDESCryptoServiceProvider to quickly allow you to encrypt a plaintext string and return the Base64 encoded string of the encrypted bytes created by the TripleDES cipher (and decrypt that string back to plaintext!). This provides a very simple way to encrypt and decrypt your data without the need to store it as a binary image. The VB.NET code is shown below:
    Public Class TripleDES
            Private _k, _iv As Byte()
    
            Public Sub New(ByVal key As Byte(), ByVal iv As Byte())
                _k = key
                _iv = iv
            End Sub
    
            Public Property Key() As Byte()
                Get
                    Return _k
                End Get
                Set(ByVal value As Byte())
                    _k = value
                End Set
            End Property
    
            Public Property IV() As Byte()
                Get
                    Return _iv
                End Get
                Set(ByVal value As Byte())
                    _iv = value
                End Set
            End Property
    
            Public Function Encrypt(ByVal plaintext As String) As String
                Dim cipher As New System.Security.Cryptography.TripleDESCryptoServiceProvider()
                cipher.BlockSize = 64
    
                Dim cipherText As String
                Using cipherTextStream As New IO.MemoryStream()
                    Using cryptoStream As New System.Security.Cryptography.CryptoStream(cipherTextStream, cipher.CreateEncryptor(_k, _iv), System.Security.Cryptography.CryptoStreamMode.Write)
                        Dim bytes As Byte() = Text.Encoding.UTF8.GetBytes(plaintext)
                        cryptoStream.Write(bytes, 0, bytes.Length)
                        cryptoStream.FlushFinalBlock()
    
                        cipherText = Convert.ToBase64String(cipherTextStream.ToArray())
                    End Using
                End Using
    
                Return cipherText
    
            End Function
    
            Public Function Decrypt(ByVal ciphertext As String) As String
                Dim cipher As New System.Security.Cryptography.TripleDESCryptoServiceProvider()
                cipher.BlockSize = 64
    
                Dim plaintext As String
                Using plaintextStream As New IO.MemoryStream()
                    Using cryptoStream As New System.Security.Cryptography.CryptoStream(plaintextStream, cipher.CreateDecryptor(_k, _iv), System.Security.Cryptography.CryptoStreamMode.Write)
                        Dim bytes As Byte() = Convert.FromBase64String(ciphertext)
                        cryptoStream.Write(bytes, 0, bytes.Length)
                        cryptoStream.FlushFinalBlock()
    
                        plaintext = Text.Encoding.UTF8.GetString(plaintextStream.ToArray())
                    End Using
                End Using
    
                Return plaintext
    
            End Function
        End Class
    To use it, simply instanciate it with your Key and Initialization Vector. Make sure your K and IV are the same when decrypting data you encrypted with them!