cshttp Tutorial - Day 2: Helper Methods, Navigation Links & Shared Templates

Back to cshttp Tutorial Main Menu

Day 2: Helper Methods, Navigation Links & Shared Templates

In Day 1, we built a multi-page website that responds to /, /home, /about, /contact, and returns a custom 404 Not Found page for unknown URLs.

Today, we will:

  1. Refactor our switch statement into clean Helper Methods (HandleHome(), HandleAbout(), etc.) so OnRequest() stays short and easy to read.
  2. Add clickable HTML Navigation Links (<a href='...'>) so visitors can click between pages instead of typing URLs in the address bar.
  3. Create a Shared Master Template (RenderPage) so we don't have to repeat the navigation menu on every single page.
  4. Add Dynamic Server Content using a live server clock (DateTime.Now).

1. Starting from Our Day 1 Code

At the end of Day 1, our OnRequest() method in Program.cs looked like this:

static void OnRequest()
{
	string path = Request.Path.ToLower();
	string html = "";

	switch (path)
	{
		case "/":
		case "/home":
			html = @"
<html>
	<head>
		<title>Hello World</title>
	</head>
	<body>
		<h1>Welcome cshttp!</h1>
		<p>Hello World!</p>
	</body>
</html>
";
			break;
		case "/about":
			html = @"
<html>
	<head>
		<title>About Us</title>
	</head>
	<body>
		<h1>About Us</h1>
		<p>It's Me!</p>
	</body>
</html>
";
			break;
		case "/contact":
			html = @"
<html>
	<head>
		<title>Contact</title>
	</head>
	<body>
		<h1>Contact</h1>
		<p>Email us at [email protected]</p>
	</body>
</html>
";
			break;
		default:
			Response.StatusCode = 404;
			html = @"
<html>
	<head>
		<title>Not Found</title>
	</head>
	<body>
		<h1>404 Not Found</h1>
		<p>The page that you are looking for does not exist.</p>
	</body>
</html>
";
			break;
	}

	Response.Write(html);
}

Notice a growing problem: as we add more pages and more HTML, putting all the HTML strings directly inside switch (path) makes OnRequest() very long and crowded.


2. Refactoring Each Page into Its Own Helper Method

To keep OnRequest() clean and easy to read, we can move the HTML for each page into its own dedicated helper method:

  • HandleHome()
  • HandleAbout()
  • HandleContact()
  • HandleNotFound()

First, let's see how we extract the Home page into HandleHome():

static void HandleHome()
{
	string html = @"
<html>
	<head>
		<title>Home</title>
	</head>
	<body>
		<h1>Welcome cshttp!</h1>
		<p>Hello World!</p>
	</body>
</html>
";
	Response.Write(html);
}

Once each page has its own method, look how clean OnRequest() becomes! It acts like a traffic director (a router) that simply calls the right method for each path:

static void OnRequest()
{
	string path = Request.Path.ToLower();

	switch (path)
	{
		case "/":
		case "/home":
			HandleHome();
			break;
		case "/about":
			HandleAbout();
			break;
		case "/contact":
			HandleContact();
			break;
		default:
			HandleNotFound();
			break;
	}
}

3. Adding Clickable Navigation Links (<a href='...'>)

In Day 1, we had to manually type /about or /contact into the browser's address bar to switch pages. Real websites have a navigation menu with clickable links (<a href='...'>) at the top of the page:

<a href='/'>Home</a> |
<a href='/about'>About</a> |
<a href='/contact'>Contact</a>

C# & HTML Tip — Using Single Quotes '...' for HTML Attributes: In HTML, attribute values can use either double quotes (href="/") or single quotes (href='/'). Because C# strings already use double quotes (@"...") to mark the start and end of the string, using double quotes inside HTML would require escaping them (href=""/""). By using single quotes ('...') for HTML attributes inside C# strings, our HTML stays super clean and requires zero escaping!

Let's add this navigation menu inside HandleHome() and HandleAbout():

static void HandleHome()
{
	string html = @"
<html>
	<head>
		<title>Home</title>
	</head>
	<body>
		<a href='/'>Home</a> |
		<a href='/about'>About</a> |
		<a href='/contact'>Contact</a>
		<h1>Welcome cshttp!</h1>
		<p>Hello World!</p>
	</body>
</html>
";
	Response.Write(html);
}

static void HandleAbout()
{
	string html = @"
<html>
	<head>
		<title>About Us</title>
	</head>
	<body>
		<a href='/'>Home</a> |
		<a href='/about'>About</a> |
		<a href='/contact'>Contact</a>
		<h1>About Us</h1>
		<p>It's Me!</p>
	</body>
</html>
";
	Response.Write(html);
}

4. Spotting the Next Problem: Repeated Layout Code

Look closely at HandleHome() and HandleAbout() above. Almost 80% of the code is identical!

Every single method (HandleHome, HandleAbout, HandleContact, HandleNotFound) has to repeat the exact same <html>, <head>, <body>, and navigation menu:

<a href='/'>Home</a> |
<a href='/about'>About</a> |
<a href='/contact'>Contact</a>

Imagine if our website had 20 pages, and we wanted to add one new link to the navigation bar—we would have to copy and paste that change into 20 different methods!

To solve this, we can create one master template method (RenderPage) that holds the outer HTML and navigation bar once, and simply plug in the page title and body content for each page.


5. Injecting Variables into Strings ($@"...")

Before we build our template method, we need a way to insert C# variables directly into an HTML string.

In Day 1, we used @"..." (a verbatim string) so our HTML could span across multiple lines:

string html = @"
    <h1>Hello World</h1>
";

In C#, if you put a $ symbol right in front of @"..." (making it $@"..."), it becomes an interpolated string. This lets you place C# variables directly inside the string using curly braces {variableName}!

For example:

string pageTitle = "About Us";
string bodyText = "<p>It's Me!</p>";

string html = $@"
<html>
    <head>
        <title>{pageTitle}</title>
    </head>
    <body>
        {bodyText}
    </body>
</html>";

When C# runs this code, {pageTitle} is automatically replaced with About Us, and {bodyText} is replaced with <p>It's Me!</p>.


6. Creating the RenderPage Template Method

Now, let's create a shared method called RenderPage that accepts two parameters:

  1. string title — the title shown on the browser tab (<title>{title}</title>).
  2. string bodyContent — the unique HTML content for that specific page ({bodyContent}).

Add this method to your Program class:

        static void RenderPage(string title, string bodyContent)
        {
            string html = $@"
<html>
    <head>
        <title>{title}</title>
    </head>
    <body>
        <nav>
            <a href='/'>Home</a> | 
            <a href='/about'>About</a> | 
            <a href='/contact'>Contact</a>
        </nav>
        <hr />
        
        {bodyContent}
    </body>
</html>";

            Response.Write(html);
        }

Notice what RenderPage does:

  • It wraps <nav> (our navigation menu) and <hr /> (a horizontal divider line) at the top of every page.
  • It places {title} inside <title>...</title>.
  • It places {bodyContent} right below the navigation menu.
  • Finally, it calls Response.Write(html); to send the finished page to the browser.

7. Simplifying Our Page Methods with RenderPage

Now that RenderPage handles the outer <html>, <head>, <body>, and <nav> menu, let's see how much cleaner HandleHome() becomes!

Before:

        static void HandleHome()
        {
            string html = @"
    <html>
        <head>
            <title>Home</title>
        </head>
        <body>
            <a href='/'>Home</a> |
            <a href='/about'>About</a> |
            <a href='/contact'>Contact</a>
            <h1>Welcome cshttp!</h1>
            <p>Hello World!</p>
        </body>
    </html>
    ";
            Response.Write(html);
        }

After (Using RenderPage):

        static void HandleHome()
        {
            string content = @"
                <h1>Welcome cshttp!</h1>
                <p>Hello World!</p>";

            RenderPage("Home", content);
        }

HandleHome() only needs to focus on its own unique content, and then pass "Home" and content to RenderPage("Home", content);!

Let's update all four page methods (HandleHome, HandleAbout, HandleContact, and HandleNotFound) to use RenderPage:

        static void HandleHome()
        {
            string content = @"
                <h1>Welcome cshttp!</h1>
                <p>Hello World!</p>";

            RenderPage("Home", content);
        }

        static void HandleAbout()
        {
            string content = @"
                <h1>About Us</h1>
                <p>It's Me!</p>";

            RenderPage("About Us", content);
        }

        static void HandleContact()
        {
            string content = @"
                <h1>Contact</h1>
                <p>Email us at [email protected]</p>";

            RenderPage("Contact", content);
        }

        static void HandleNotFound()
        {
            Response.StatusCode = 404;
            string content = @"
                <h1>404 Not Found</h1>
                <p>The page that you are looking for does not exist.</p>";

            RenderPage("404 Not Found", content);
        }

8. Adding Dynamic Content: A Live Server Clock

Because all of our pages now share RenderPage(), any change we make inside RenderPage() will automatically appear on every page of our website!

Let's test this by adding a <footer> at the bottom of RenderPage() that displays the current server time using C#'s DateTime.Now:

        static void RenderPage(string title, string bodyContent)
        {
            string html = $@"
<html>
    <head>
        <title>{title}</title>
    </head>
    <body>
        <nav>
            <a href='/'>Home</a> | 
            <a href='/about'>About</a> | 
            <a href='/contact'>Contact</a>
        </nav>
        <hr />
        
        {bodyContent}

        <hr />
        <footer>
            <small>Server Time: {DateTime.Now:yyyy-MM-dd HH:mm:ss}</small>
        </footer>
    </body>
</html>";

            Response.Write(html);
        }

What does {DateTime.Now:yyyy-MM-dd HH:mm:ss} mean?

  • DateTime.Now gets the current date and time from your computer's clock at the exact moment the request arrives.
  • :yyyy-MM-dd HH:mm:ss formats the date cleanly as Year-Month-Day Hour:Minute:Second (for example: 2026-10-27 16:35:10).

9. Complete Code for Day 2

Here is how your complete Program.cs looks when everything is put together:

using System;
using cshttp;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            WebApp.Run(8080, OnRequest);
        }

        static void OnRequest()
        {
            string path = Request.Path.ToLower();

            switch (path)
            {
                case "/":
                case "/home":
                    HandleHome();
                    break;
                case "/about":
                    HandleAbout();
                    break;
                case "/contact":
                    HandleContact();
                    break;
                default:
                    HandleNotFound();
                    break;
            }
        }

        static void RenderPage(string title, string bodyContent)
        {
            string html = $@"
<html>
    <head>
        <title>{title}</title>
    </head>
    <body>
        <nav>
            <a href='/'>Home</a> | 
            <a href='/about'>About</a> | 
            <a href='/contact'>Contact</a>
        </nav>
        <hr />
        
        {bodyContent}

        <hr />
        <footer>
            <small>Server Time: {DateTime.Now:yyyy-MM-dd HH:mm:ss}</small>
        </footer>
    </body>
</html>";

            Response.Write(html);
        }

        static void HandleHome()
        {
            string content = @"
                <h1>Welcome cshttp!</h1>
                <p>Refresh the page and watch the server clock at the bottom change!</p>";

            RenderPage("Home", content);
        }

        static void HandleAbout()
        {
            string content = @"
                <h1>About Us</h1>
                <p>It's Me!</p>";

            RenderPage("About Us", content);
        }

        static void HandleContact()
        {
            string content = @"
                <h1>Contact</h1>
                <p>Email us at [email protected]</p>";

            RenderPage("Contact", content);
        }

        static void HandleNotFound()
        {
            Response.StatusCode = 404;
            string content = @"
                <h1>404 Not Found</h1>
                <p>The page that you are looking for does not exist.</p>";

            RenderPage("404 Not Found", content);
        }
    }
}

10. Test It in Your Browser

  1. Run the app (dotnet run or press Start in Visual Studio).
  2. Visit http://localhost:8080/.
  3. Notice the clickable navigation bar at the top (<hr />) and the Server Time in the footer at the bottom.
  4. Click through About and Contact—every page automatically shares the exact same navigation bar and footer!
  5. Refresh the page several times (F5)—watch the seconds on the Server Time clock tick upward on every refresh, because your C# code generates the HTML live on every request!

In Day 3, we will learn how our web server can accept input from the visitor using URL Query Strings and an interactive HTML form.