2

Background: I have to append to query string values from the initial call to the website, unique for each user, to every internal link on every subsequent page call for the rest of their visit to the site to preserve tracking information for Google Analytics. I store these values in the Session object in the Global.asax during Session_Start. I've made a custom Page object that overrides CreateHtmlTextWriter to supply a custom HtmlTextWriter object that has overriden all the public AddAttribute and WriteAttribute methods that check the name/key to see if it is "href" and if so, append the additional Query String parameters to the value object during the Render process. This works for links that were generated via an ASP.NET control, i.e. Hyperlink like:

<asp:HyperLink BackColor="Red" ID="link" runat="server" NavigateUrl="http://www.google.com/" Text="Google" />

but doesn't work if a just wrote:

<a href="http://www.yahoo.com/">Yahoo</a>

directly in the aspx page. What part of HtmlTextWriter does HTML from the aspx page render through?

I'm including my code files for the proof of concept I'm trying to get working (doesn't try to idenitfy whether the link is internal or external, that will be for later)[This is built using Webforms, .NET Framework 3.5 and C#]:

Default.aspx:

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

<!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></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:HyperLink BackColor="Red" ID="link" runat="server" NavigateUrl="http://www.google.com/" Text="Google" />
        <br />
        <a href="http://www.yahoo.com/">Yahoo</a>
    </div>
    </form>
</body>
</html>

Default.aspx.cs (includes both the custom Page class and the custom HtmlTextWriter class):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;

namespace TestRenderOverride
{
    public partial class _Default : CustomPage
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            HyperLink hl = new HyperLink();
        }

    }

    public class CustomPage : System.Web.UI.Page
    {
        protected override HtmlTextWriter CreateHtmlTextWriter(TextWriter tw)
        {
            return new CustomHtmlTextWriter(HttpContext.Current, tw);
        }
    }
    public class CustomHtmlTextWriter : HtmlTextWriter
    {
        private string UTM_A;
        private string UTM_B;

        public CustomHtmlTextWriter(HttpContext context, TextWriter writer) : base(writer)
        {
            if (context.Session["Protected_UTM-A"] != null)
            {
                UTM_A = context.Session["Protected_UTM-A"].ToString();
            }
            if (context.Session["Protected_UTM-B"] != null)
            {
                UTM_B = context.Session["Protected_UTM-B"].ToString();
            }
        }
        public CustomHtmlTextWriter(HttpRequest request, TextWriter writer, string tabString)
            : base(writer, tabString)
        {

        }

        private string AppendCustomQueryString(string value)
        {
            if (HasQueryStringValues())
            {
                if (value.Contains('?'))
                {
                    return value + "&" + BuildQueryStringAddition();
                }
                else
                {
                    return value + "?" + BuildQueryStringAddition();
                }
            }
            else
            {
                return value;
            }
        }

        private string BuildQueryStringAddition()
        {
            string returnValue = String.Empty;
            if (!String.IsNullOrEmpty(UTM_A))
            {
                returnValue = "UTM_A=" + UTM_A;
            }
            if (!String.IsNullOrEmpty(UTM_B))
            {
                if (!String.IsNullOrEmpty(returnValue))
                {
                    returnValue += "&UTM_B=" + UTM_B;
                }
                else
                {
                    returnValue = "UTM_B=" + UTM_B;
                }
            }

            return returnValue;
        }

        private bool HasQueryStringValues()
        {
            if (!String.IsNullOrEmpty(UTM_A) || !String.IsNullOrEmpty(UTM_B))
            {
                return true;
            }

            return false;
        }

        public override void AddAttribute(HtmlTextWriterAttribute key, string value)
        {
            if (key == HtmlTextWriterAttribute.Href)
            {
                value = AppendCustomQueryString(value);
            }

            base.AddAttribute(key, value);
        }
        public override void AddAttribute(string name, string value)
        {
            if (name.ToLowerInvariant() == "href")
            {
                value = AppendCustomQueryString(value);
            }

            base.AddAttribute(name, value);
        }
        public override void AddAttribute(HtmlTextWriterAttribute key, string value, bool fEncode)
        {
            if (key == HtmlTextWriterAttribute.Href)
            {
                value = AppendCustomQueryString(value);
            }

            base.AddAttribute(key, value, fEncode);
        }
        public override void AddAttribute(string name, string value, bool fEndode)
        {
            if (name.ToLowerInvariant() == "href")
            {
                value = AppendCustomQueryString(value);
            }

            base.AddAttribute(name, value, fEndode);
        }
        protected override void AddAttribute(string name, string value, HtmlTextWriterAttribute key)
        {
            if ((name.ToLowerInvariant() == "href") || (key == HtmlTextWriterAttribute.Href))
            {
                value = AppendCustomQueryString(value);
            }

            base.AddAttribute(name, value, key);
        }


        public override void WriteAttribute(string name, string value)
        {
            if (name.ToLowerInvariant() == "href")
            {
                value = AppendCustomQueryString(value);
            }

            base.WriteAttribute(name, value);
        }

        public override void WriteAttribute(string name, string value, bool fEncode)
        {
            if (name.ToLowerInvariant() == "href")
            {
                value = AppendCustomQueryString(value);
            }

            base.WriteAttribute(name, value, fEncode);
        }

        public override void WriteStyleAttribute(string name, string value)
        {
            if (name.ToLowerInvariant() == "href")
            {
                value = AppendCustomQueryString(value);
            }

            base.WriteAttribute(name, value);
        }

        public override void WriteStyleAttribute(string name, string value, bool fEncode)
        {
            if (name.ToLowerInvariant() == "href")
            {
                value = AppendCustomQueryString(value);
            }

            base.WriteAttribute(name, value, fEncode);
        }

    }
}

Global.asax:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;

namespace TestRenderOverride
{
    public class Global : System.Web.HttpApplication
    {

        protected void Application_Start(object sender, EventArgs e)
        {

        }

        protected void Session_Start(object sender, EventArgs e)
        {
            if (Request.QueryString["UTM_A"] != null)
            {
                Session["Protected_UTM-A"] = Request.QueryString["UTM_A"].ToString();
            }
            if (Request.QueryString["UTM_B"] != null)
            {
                Session["Protected_UTM-B"] = Request.QueryString["UTM_B"].ToString();
            }
        }

        protected void Application_BeginRequest(object sender, EventArgs e)
        {

        }

        protected void Application_AuthenticateRequest(object sender, EventArgs e)
        {

        }

        protected void Application_Error(object sender, EventArgs e)
        {

        }

        protected void Session_End(object sender, EventArgs e)
        {

        }

        protected void Application_End(object sender, EventArgs e)
        {

        }
    }
}

Here is the source that is rendered when you run the code and set Start Page of the web project to "Specific Page" with the value of "Default.aspx?UTM_A=Georgia&UTM_B=Maryland":

<!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><title>

</title></head>
<body>
    <form name="form1" method="post" action="Default.aspx?UTM_A=Georgia&amp;UTM_B=Maryland" id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNzU0OTY1Mjk1ZGRbJc0Q8nWuQUZrihiKtbAzs/+VPA==" />
</div>

    <div>
        <a id="link" href="http://www.google.com/?UTM_A=Georgia&amp;UTM_B=Maryland" style="background-color:Red;">Google</a>
        <br />
        <a href="http://www.yahoo.com/">Yahoo</a>
    </div>
    </form>
</body>
</html>
John Saunders
  • 160,644
  • 26
  • 247
  • 397
ben f.
  • 750
  • 6
  • 14

3 Answers3

1

I haven't touched web forms in a long time, but I think you need to update your anchor like:

<a href="http://www.yahoo.com/" runat="server">Yahoo</a>
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
  • That does send the tag through the currnet processing steps (a couple light modifications to stop it from adding the values multiple times) and I get my end result (via a not too painful migration process). I'm still curious, does ASP.NET just render all that HTML as a giant literal or something, becasue it still has to get into the Render method to get to the output. – ben f. Oct 26 '11 at 00:06
  • It's been so long. Either you are correct and/or there are two types of tags (from what I can remember) in asp.net webforms. One is a web form control tag, the other is an html tag. Html tags are rendered differently (maybe ultimately as a literal, I can't remember). – Erik Philips Oct 26 '11 at 00:13
0

A couple other options would be to

  1. Override the page's render method, have a regex string to find href tags and update them there.

  2. Do this on the client side. On ready, iterate through all A tags via jquery, and update the href attribute from there. You could do something simple like putting the user data in a hidden variable so you can use it when updating the href value. I think the advantage here is you are pushing the processing on to the client side and the implementation in jquery would be much simpilar.

dkpatt
  • 580
  • 5
  • 12
0

To do it in C# you'd have to manipulate the html after asp.net was done writing it, in the PreRender event. Seems heavy handed to do it that way though.

You could use JQuery to go through all the links and change them. Here is a little code that will do it. I'm sure there is a better way, and some caveats. I'd recommend using the plugin if you go with jquery.

Change URL parameters (stack overflow)

<script type="text/javascript">
    $(document).ready(function () {
        $('a[href]').each(function (index) {
            var hr = this.href;
            var toAppend = 'val=100&simple=true';
            hr = hr + ((hr.indexOf('?') > -1) ? '&' : '?');
            hr = hr + toAppend;
            this.href = hr;
        });
    });
</script>
Community
  • 1
  • 1
Andrew
  • 8,322
  • 2
  • 47
  • 70