Modifying Partial Postbacks

The purpose of this post is to explain the difficulties we encountered when modifying the HTML stream being sent back to the client on a partial postback using C# .NET with ASP.

Background

The product I was working on when this occurred was a website based on ASP .NET, the task was to replace all text from the website with references and place the raw text in the database so it could be replaced during run-time.  We decided to override Page::RenderChildren as this replaced all the references in the controls from others pages (for example .ascx pages) before inserting them into the parent page.  There are many advantages and disadvantages to doing this, but the focus of this post is to discuss the issues surrounding the actual implementation.

The Issue

Although this worked fine in a full postback, any partial postback made with these references failed.  It appears in ASP the structure of the HTML stream for a partial postback is different to that of a full postback.

|58|hiddenField|ctl00_holderTableTitle_Close_Review_ClientState|{\"ActiveTabIndex\":0,\"TabState\":[true,true,true,true,true]}|

Above is an extract from a partial postback with properties separated by ‘|’.  The two properties of importance are the first and the last: the last being the actual data or HTML to be ran, while the first is the character count for that data.  The problem occurs when a piece of HTML such as below is parsed for references:

<asp:Label runat="server" ID="lblLabel" Text="$$STRING4$$"></asp:Label>

The text in this case is a reference to the database, which when replaced will likely be more and or less than eleven characters long. This would leave the first property in our partial postback invalid, causing the postback to fail on the client.

Resolution

The solution chosen for this was two fold: first isolate when the postback was a partial postback.

string output = mRenderedHTML.ToString();
if (output.Substring(0, output.IndexOf('\n') + 1).Contains("updatePanel"))
{
    writer.Write(ReplaceResourcesUpdatePanel(mRenderedHTML, Path.GetFileName(path)));
}
else
{
    writer.Write(ReplaceResources(mRenderedHTML, Path.GetFileName(path)));
}

Second iterate through the stream and rebuild it as necessary.

private string ReplaceResourcesUpdatePanel(StringBuilder page, string page_name)
{
    // Get the raw text from the page
    string html = page.ToString();

    // Get the keywords
    Dictionary.PageDictionary dictionary = Global.GetPageDictionary(page_name);
    Keywords.Keyword[] keywords = Global.GetKeywords();

    // Iterate through the html
    if (dictionary != null)
    {
        StringBuilder builder = new StringBuilder();
        int start = 0;

        while (start < html.Length)
        {
            // Find the next start
            int end = html.IndexOf('|', start);
            int count = int.Parse(html.Substring(start, end - start));

            // Find the text
            int text = end + 1;
            for (int i = 0; i < 2; i++)
            {
                text = html.IndexOf('|', text) + 1;
            }

            // Replace the text
            string replace = html.Substring(text, count);
            if (replace.Contains("$$STRING"))
            {
                foreach (string keyword in dictionary.Items.Keys)
                {
                    replace = replace.Replace(string.Format("$${0}$$", keyword), ReplaceKeywords(dictionary.Items[keyword], keywords));
                }
            }

            // Rebuild the string
            int length = replace.Length;
            html = html.Substring(0, text) + replace + html.Substring(text + count);
            html = html.Substring(0, start) + length + html.Substring(end);

            // Increment the start
            int difference = length.ToString().Length - count.ToString().Length;
            start = text + length + difference + 1;
        }
    }

    // Return the new HTML
    return html;
}

The above is the optimised result of iterating through the HTML stream, replacing the references and changing the character count. The highlighted lines are where the stream has it’s properties replaced, where line forty-one replaces the actual data and line forty-two replaces the character count.


There are a lot of different ways of achieving the same outcome here and I strongly encourage exploring those avenues, however if modifying the postbacks like this is your only option then I hope this post has been of some use to you.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s