Simple Talk is now part of the Redgate Community hub - find out why

Token Replacement in ASP.NET

Damon Armstrong describes an elegant way of performing dynamic string replacement in ASP.NET applications - one that will work in any situation imaginable!

As a web developer, you will encounter situations that call for an effective token replacement scheme. Token replacement is really just a technologically-savvy way of saying string replacement, and involves substituting a “real” value for a “token” value in a string. This article presents a powerful approach to token replacement in ASP.NET 2.0.

The goal is to create a centralized token replacement mechanism that works in ASP.NET controls, HTML controls, static HTML markup, and in text placed on the page from the code-behind. In other words, in any situation imaginable.

If you want to skip the background info and get right into the replacement mechanism, then jump to the section titled Acquiring All ASP.NET Page Content. Otherwise, let’s get some background on the various token replacement options in ASP.NET.

Basic token replacement concepts

The ultimate goal of token replacement is to make the value of a string dynamic. You begin with a static string containing tokens, and then update those tokens with replacement values to produce your dynamic string. For example, you may have the following string in an application that emails users a new password if theirs has been lost:

In this scenario, the application has two values to communicate to the user: their name (to personalize the email) and their new password (because they need it to login). Let’s say we wish to assign the value “Matt” to the [$NAME$] token and “5ZQS76Bv” to the [$PASSWORD$] token. The resulting text would look like this:

One question that might arise is, why not just use concatenation to build the string? Concatenation is certainly faster, but it comes down to a question of maintainability. Concatenation can only occur in code, so you would have to hard-code the majority of the string, like this:

What happens if you wanted to update the email content? You would have to update your code, recompile it, and redeploy it. Plus, a complex HTML email built directly in C# source is not the easiest thing to debug and maintain.

The token replacement approach allows you to store the content in a separate file, read it into your application, and replace the tokens in code. This separates the content from the application and allows you to make updates in a less convoluted environment and without having to recompile or redeploy the application.

Token / String Replacement in ASP.NET

There are several ways to replace strings in ASP.NET 2.0, but I’m only going to touch on the three that I consider the most popular:

  • Inline Script
  • The String.Format method
  • The String.Replace and StringBuilder.Replace instance methods

Each of these are outlined in more detail below.

Inline Script

Although ASP.NET has moved to a code-behind model, you can still write inline script just as you could in original ASP. Inline script still has its uses and can be extremely helpful for string replacement scenarios. If you think about it, inline script is essentially a really powerful string replacement mechanism that finds your script in a string (i.e. the page markup), runs that script, and then replaces the script with the value it produces. Here’s our original example modified for inline script:

One issue with inline script is that you cannot always use it in conjunction with ASP.NET controls. For example, let’s say that you wanted a standard ASP.NET button on the page to say “Matt, Click Here to Email Your Password.” You cannot do the following:

Nor can you use the mechanism from inside your code-behind. So, it’s useful for making a single page, but not for our global string-replacement needs.

The String.Format Method

The String.Format method accepts a string containing tokens followed by a list of replacement values for the tokens found in the string. The tokens in the string are numbers surrounded by curly brackets, where the number corresponds to the index of the replacement value passed into the method.

The first token, {0}, corresponds to the first replacement value “Matt”, the second token, {1}, corresponds to “Male”, and so on. You can have an infinite number of tokens and replacement values, but you have to have at least as many replacement values as you have tokens. In other words, when the function encounters a number in curly brackets, {N}, there had better be a corresponding replacement value for that token or else the function throws an exception.

Tokens do not need to appear in order, you can use a token more than once in the string, and you can have more replacement values than tokens:

In this example, the token, {2}, appears twice, the tokens are not in order, and the “X”, “Y”, “Z”, and “!” replacement values are never used.

You can easily put tokens for the string.Format method in ASP.NET controls, HTML controls, static HTML markup, and code in the code-behind, so it is a candidate for use in our global string replacement mechanism. My biggest issue is that the tokens are non-intuitive. When you see a token like [$NAME$], you have some idea what it represents, whereas the token, {7}, communicates very little.

The String.Replace and StringBuilder.Replace Methods

Lastly, we have the Replace methods found on the String and StringBuilder instances. Both of these methods operate using the same basic logic. You provide a string containing a token, identify the token, supply a replacement value for the token, and the method replaces any instances of the token with the replacement value.

We’ll begin by looking at the Replace method on a String instance. Since the Replace method is only available on a String instance and not from the String class itself, you start by creating a String instance. Then you call the Replace method from the instance by passing in a token and replacement value, and the method returns a string containing the replacements. Your original string, however, remains unchanged. If you want to update your string with the replacement, you have to assign the result of the Replace function back to your string, as demonstrated in the following example:

You call the Replace method on a StringBuilder instance in the exact same way as a string: by passing in a token and replacement value. However, the Replace method on a StringBuilder instance operates directly on that StringBuilder instance’s value, so you do not need to assign the result of the Replace function back to your StringBuilder to pick up the changes:

Tokens like [$NAME$] are fairly intuitive, can be placed in ASP.NET controls, HTML controls, static HTML markup, and code in your code behind. So it’s the option we’re going to run with for building the global string replacement mechanism for our ASP.NET application.

Acquiring All ASP.NET Page Content

Our biggest objective in building a global string replacement mechanism is that it needs to work everywhere. If you put a token directly in your HTML, it needs to be replaced. If you assign a token to an ASP.NET control from a code-behind, then it needs to be replaced. If you put a token in a database and a control pulls that value from the database and displays it, then it needs to be replaced. So… how do you go about doing that?

When it comes right down to it, all of the architecture and code for ASP.NET revolves around building a giant string to send to the browser. ASP.NET controls, HTML, code, values from a database, they all end up as part of a string containing the source for a page. All we have to do is intercept that string before ASP.NET sends it to the browser and run our replacements. And it really doesn’t take all that much code.

Since we want this mechanism to be available to all of the pages in an application, we’ll create a new class named TokenReplacementPage that derives from System.Web.UI.Page. Any pages requiring token replacement functionality just need to derive from TokenReplacementPage instead of System.Web.UI.Page. Following is the code for the TokenReplacementPage class:

Code Listing 1 – TokenReplacementPage class

First, note that the TokenReplacementPage class is an abstract class that derives from System.Web.UI.Page. This means that it has all the standard Page functionality as well as token replacement features. All you have to do to confer token replacement functionality to a page is inherit from the TokenReplacementPage class instead of the Page class.

There are three methods inside the TokenReplacementPage class:

  • The Render method – to write the HTML page source to a StringBuilder
  • The RunGlobalReplacements method – to perform global token replacements on the source
  • RunPageReplacements – to allow for page-specific token replacements

Writing the Page Source to StringBuilder

The overridden Render method has a single HtmlTextWriter parameter named writer. An HtmlTextWriter allows you to write HTML to a stream. In this case, the underlying stream is sent to the browser, so the writer parameter is your conduit for outputting HTML to the people viewing your page. Normally, the Render method iterates through all of the controls on the page and passes the writer parameter to the Render method on each individual control. As each control executes its Render method, it writes the HTML for that section of the page to the browser.

Since we want to capture the entire page source before it gets to the browser, we need to do a little work to re-route the page source into a stream that we can access. To start, we create a “stream” that we can work with: a StringBuilder instance named pageSource. Next, we create a new StringWriter named sw and pass pageSource into its constructor. This initializes sw with pageSource as its underlying stream. Anything written to sw is output to pageSource. Next, we create a new HtmlTextWriter named htmlWriter and pass sw into its constructor. This initializes htmlWriter with sw as its underlying TextWriter. Thus, anything written to htmlWriter is written to sw, which is then written to pageSource. Finally, we pass htmlWriter to the Base.Render method and allow the page to render as it normally would. After Base.Render finishes, the entire page source is available for modification in pageSource.

After acquiring the page source, the overridden Render method passes pageSource to two methods that are responsible for actually making the substitutions: RunPageReplacements and RunGlobalReplacements. We will discuss these in more detail shortly.

Once the replacements have been made, the only thing left to do is send the updated content to the browser. We do that by using pageSource to write in the last line of code in the overridden Render method.

Next, let’s take a look at making the actual replacements.

Global Replacements

You make global replacements, that is the replacements you want to make on every page that inherits from the TokenReplacementPage class, in the RunGlobalReplacements method. All you have to do to make a replacement is call the Replace method on the pageSource parameter and pass in the token and the replacement value:

The Replace method then searches through the string in pageSource and replaces any instances of the token with the replacement value. Remember to make your token something that is unlikely to normally appear in the page. For example, [$NAME$] is a much better choice than just NAME because it’s very unlikely a normal sentence would contain a word with brackets and dollar signs around it. You don’t want to accidentally mistake a normal word in a sentence for a token.

Page Specific Replacements

There may be times when you want to run page-specific replacements. Notice that the RunPageReplacements method in the TokenReplacementPage class is marked as virtual and contains no code. This allows you to override the RunPageReplacements method on the page in which you want to make page-specific token replacements. The replacements are made in the exact same fashion as described in the Global Replacements section, but they are only applied to that specific page:

Code Listing 2 – Overridden RunPageReplacements Example

Checking out the demo application

Download the demo application (from the Code Download link in the box to the right of the article title) and extract it to a location of your choosing on your hard drive. Start Visual Studio, and open the web site from wherever it is that you chose to save it. There are only four files (not including code-behinds) in the entire demo:

File Name

Purpose

App_Code\TokenReplacementPage.cs

Contains the TokenReplacementPage class that provides pages with token replacement functionality

Default.aspx

Demonstrates global token replacement functionality

PageSpecificReplacementsPage.aspx

Demonstrates global and page-specific token replacement functionality

Web.config

Website configuration

Take a look at the markup in the two ASP.NET pages and notice the various tokens that appear throughout. Also take a look at each page’s code behind files because you will see a token set in code to demonstrate that you can put a token anywhere and, as long as it is output to the page source, it is replaced by the token replacement mechanism. The code-behind for PageSpecificReplacementsPage.aspx also contains the RunPageReplacement override from Code Listing 2.

When you run the demo application, you will see that the tokens are replaced with their respective values when the page appears in your browser. Make sure to observe the difference between the [$PAGESPECIFICTOKEN$] behavior between Default.aspx and PageSpecificReplacementsPage.aspx. Feel free to add new tokens, replacement values, and pages to the demo application to get a feel for how it all works.

Conclusion

When you need a token replacement mechanism, you should be well equipped with this solution. It gives you the ability to replace tokens regardless of whether they appear in ASP.NET controls, HTML controls, static HTML markup, code, or even a content management database. As long as you can get the token to render on the page, it can be replaced.

How you log in to Simple Talk has changed

We now use Redgate ID (RGID). If you already have an RGID, we’ll try to match it to your account. If not, we’ll create one for you and connect it.

This won’t sign you up to anything or add you to any mailing lists. You can see our full privacy policy here.

Continue

Simple Talk now uses Redgate ID

If you already have a Redgate ID (RGID), sign in using your existing RGID credentials. If not, you can create one on the next screen.

This won’t sign you up to anything or add you to any mailing lists. You can see our full privacy policy here.

Continue