Chapter 2

ASP.NET

.NET Goes Online

Introduction

While web browsers request resources (including HTTP, CSS, and JavaScript) files over HTTP, the other end of this connection, and what supplies those files, is a web server. Unlike web clients, which are limited by what technologies a browser understands (namely HTML, CSS, and JS), a web server can be written in any programming language. In this chapter, we will explore writing web servers in C#, using aspects of the ASP.NET framework.

Key Terms

Some key terms to learn in this chapter are:

  • Web Server
  • ASP.NET
  • Dynamic Web Pages
  • Templates
  • Razor Pages

Key Skills

The key skills you will be developing in this chapter are:

  • Creating a web server to serve a web application using ASP.NET
  • The ability to author Razor Pages combining HTML with embedded C# code

Static Webservers

The earliest web servers simply served files held in a directory. If you think back to your web development assignment from CIS 115, this is exactly what you did - you created some HTML, CSS, and JS files and placed them in the public_html directory in your user directory on the CS Linux server. Anything placed in this folder is automatically served by an instance of the Apache web server running on the Linux server, at the address https://people.cs.ksu.edu/~[eid]/ where [eid] is your K-State eid.

Apache is one of the oldest and most popular open-source web servers in the world. Microsoft introduced their own web server, Internet Information Services (IIS) around the same time. Unlike Apache, which can be installed on most operating systems, IIS only runs on the Windows Server OS.

While Apache installations typically serve static files from either a html or public_html directory, IIS serves files from a wwwroot directory.

As the web grew in popularity, there was tremendous demand to supplement static pages with pages created on the fly in response to requests - allowing pages to be customized to a user, or displaying the most up-to-date information from a database. In other words, dynamic pages. We’ll take a look at these next.

Dynamic Pages

Modern websites are more often full-fledged applications than collections of static files. But these applications remain built upon the foundations of the core web technologies of HTML, CSS, and JavaScript. In fact, the client-side application is typically built of exactly these three kinds of files! So how can we create a dynamic web application?

One of the earliest approaches was to write a program to dynamically create the HTML file that was being served. Consider this method:

public string GeneratePage()
{
    StringBuilder sb = new StringBuilder();
    sb.Append("<!DOCTYPE html>");
    sb.Append("<html>");
    sb.Append("<head>");
    sb.Append("<title>My Dynamic Page</title>");
    sb.Append("</head>");
    sb.Append("<body>");
    sb.Append("<h1>Hello, world!</h1>");
    sb.Append("<p>Time on the server is ");
    sb.Append(DateTime.Now);
    sb.Append("</p>");
    sb.Append("</body>");
    sb.Append("</html>");
    return sb.ToString();
}

It generates the HTML of a page showing the current date and time. Remember too that HTTP responses are simply text, so we can generate a response as a string as well:

public string GenerateResponse()
{
    string page = GeneratePage();
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("HTTP/1.1 200");
    sb.AppendLine("Content-Type: text/html; charset=utf-8");
    sb.AppendLine("ContentLength:" + page.Length);
    sb.AppendLine("");
    sb.Append(page);
    return sb.ToString();
}

The resulting string could then be streamed back to the requesting web browser. This is the basic technique used in all server-side web frameworks: they dynamically assemble the response to a request by assembling strings into an HTML page. Where the differ is what language they use to do so, and how much of the process they’ve abstracted.

This approach was adopted by Microsoft and implemented as Active Server Pages (ASP). By placing files with the .asp extension among those served by an IIS server, C# or Visual Basic code written on that page would be executed, and the resulting string would be served as a file. This would happen on each request - so a request for http://somesite.com/somepage.asp would execute the code in the somepage.asp file, and the resulting text would be served.

You might have looked at the above examples and shuddered. After all, who wants to assemble text like that? And when you assemble HTML using raw string concatenation, you don’t have the benefit of syntax highlighting, code completion, or any of the other modern development tools we’ve grown to rely on. Thankfully, most web development frameworks provide some abstraction around this process, and by and large have adopted some form of template syntax to make the process of writing a page easier.

Template Rendering

It was not long before new technologies sprang up to replace the ad-hoc string concatenation approach to creating dynamic pages. These template approaches allow you to write a page using primarily HTML, but embed snippets of another language to execute and concatenate into the final page. This is very similar to the template strings we have used in C#, i.e.:

string time = $"The time is {DateTime.Now}";

Which concatenates the invoking the DateTime.Now property’s ToString() method into the string time. While the C# template string above uses curly braces to call out the script snippets, most HTML template libraries initially used some variation of angle brackets + additional characters. As browsers interpret anything within angle brackets (<>) as HTML tags, these would not be rendered if the template was accidentally served as HTML wihout executing and concatenating scripts. Two early examples are:

  • <?php echo "This is a PHP example" ?>
  • <% Response.Write("This is a classic ASP example) %>

And abbreviated versions:

  • <?= "This is the short form for PHP" ?>
  • <%= "This is the short form for classic ASP" %>

Template rendering proved such a popular and powerful tool that rendering libraries were written for most programming languages, and could be used for more than just HTML files - really any kind of text file can be rendered with a template. Thus, you can find template rendering libraries for JavaScript, Python, Ruby, and pretty much any language you care to (and they aren’t that hard to write either).

Microsoft’s classic ASP implementation was limited to the Visual Basic programming language. As the C# language gained in popularity, they replaced classic ASP with ASP.NET web pages. Like classic ASP, each page file (named with a .aspx extension) generates a corresponding HTML page. The script could be either Visual Basic or C#, and a new syntax using the at symbol (@) to proceed the code snippets was adopted. Thus the page:

<html>
    <body>
        <h1>Hello Web Pages</h1>
        <p>The time is @DateTime.Now</p>
    </body>
</html>

Would render the current time. You can run (and modify) this example on the w3schools.com.

This template syntax is the Razor syntax, and used throughout Microsoft’s ASP.NET platform. Additionally it can be used outside of ASP.NET with the open-source RazorEngine.

Classic PHP, Classic ASP, and ASP.NET web pages all use a single-page model, where the client (the browser) requests a specific file, and as that file is interpreted, the dynamic page is generated. This approach worked well in the early days of the world-wide-web, where web sites were essentially a collection of pages. However, as the web grew increasingly interactive, many web sites grew into full-fledged web applications, full-blown programs that did lend themselves to a page-baed structure. This new need resulted in new technologies to fill the void - web frameworks. We’ll talk about these next.

Web Frameworks

As web sites became web applications, developers began looking to use ideas and techniques drawn from traditional software development. These included architectural patterns like Model-View-Controller (MVC) and Pipeline that simply were not possible with the server page model. The result was the development of a host of web frameworks across multiple programming languages, including:

  • Ruby on Rails, which uses the Ruby programming language and adopts a MVC architecture
  • Laravel, which uses the PHP programming language and adopts a MVC architecture
  • Django, which uses the Python programming language and adopts a MVC architecture
  • Express, which uses the Node implementation of the JavaScript programming language and adopts the Pipeline architecture
  • Revel, which uses the Go programming language and adopts a Pipeline architecture
  • Cowboy, which uses the erlang programming language and adopts a Pipeline architecture
  • Phoenix, which uses the elixir programming language, and adopts a Pipeline architecture

ASP.NET Frameworks

This is only a sampling of the many frameworks and languages used in the modern web. Microsoft adapted to the new approach by creating their own frameworks within the ASP.NET family:

  • ASP.NET MVC uses C# (or Visual Basic) for a language and adopts a MVC architecture
  • ASP.NET Razor Pages, which also uses C# (or Visual Basic) for its language, and adopts a Pipeline architecture
  • ASP.NET API is a web framework focused on creating RESTful web APIs (i.e. a web application that serves data instead of HTML)

IIS and ASP.NET Core

While ASP.NET applications are traditionally hosted on IIS running on the Windows Server operating system, the introduction of .NET Core made it possible to run .NET programs on Linux machines. As Linux operating systems are typically free and dominate the web server market (W3Cook^[w3cook] reports 98.1% of web servers worldwide run on a Linux OS).

$[w3cook]: W3Cook OS Summary

Microsoft has accordingly migrated its ASP.NET family to a new implementation can run on .NET Core or IIS: ASP.NET Core. When you build a ASP.NET Core application, you can choose your deployment target: IIS, .NET Core, or even Microsoft’s cloud service, Azure. The same application can run on any of these platforms.

Razor Pages

ASP.NET Core adds a project type to Visual Studio’s new project wizard, ASP.NET Core web application which uses Razor Pages. The Razor Page approach represents a hybrid approach between a MVC and Pipeline architecture and leverages some of the ideas of component-based design that we saw with WPF applications.

The program entry point is Program.cs, which creates the web server our application will run on. In it, we initialize and configure the server based on the Startup.cs class, which details what aspects of the ASP.NET program we want to use. The wizard does the initial configuration for us, and for now we’ll leave the defaults:

  • Adding the Razor Pages service (which allows us to use Razor Pages)
  • Enabling HTTPS redirection (which instructs browsers making HTTP requests against our server to make HTTPS requests instead)
  • Enabling the use of static files, which means files in the wwwroot folder will be served as they are, in as efficient a manner of possible
  • Mapping requests to razor pages (this makes a request against a route like /index map to the Pages/Index.cshtml razor page)

Under this architecture, any file we want to serve as-is (i.e. our CSS and JavaScript files), we’ll place in wwwroot folder. Any route we want to serve dynamically, we’ll create a corresponding Razor page for in the Pages folder.

Razor Page Syntax

Let’s look at an example Razor page, index.cshtml, and then break down its components:

@page 
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}
<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

The @page line indicates to the compiler that this file represents a Razor page. This is necessary for the page to be interpreted correctly, and for setting up the mapping from a request for the route /index to be mapped to this page (Index.cshtml).

The @model line indicates the model class to use with this page. Conventionally, the model class has the same name as the Razor page, plus a .cs extension, though we can use a different model file if needed. If we follow the convention, the model file is grouped with the Razor page file, much like the codebehind files in WPF and Forms. The model class provides the data for the web page, in a manner somewhat like the ViewModel classes we worked with in WPF. We’ll talk more about model classes shortly.

The @{} section is a place to define variables. In this case, we add a key/value pair to the ViewData dictionary. This dictionary is available in both the page and the layout, and is an easy way to pass values between them (in this case, we are providing a title to the layout). The layout is discussed below.

Finally, the page content itself is presented in Razor syntax - a mixture of HTML and embedded C# proceeded by the @ symbol. Note that we do not need to provide termination to the C# code - the compiler will automatically determine when we switch from code back to HTML based on the grammar of the C# language.

Layouts

If you remember from our discussions of HTML, a valid HTML page must have a <!DOCTYPE html> element, and <html>, <head>, <title>, and <body> elements. But where are these in our Razor page? It exists in the _Pages/Shared/Layout.cshtml file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - ExampleWebApplication</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">ExampleWebApplication</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2020 - ExampleWebApplication - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @RenderSection("Scripts", required: false)
</body>
</html>

Using a layout file allows us to place boilerplate HTML (code that is repeated on every page of our site) in a single location, and share it amongst all pages in our application. The @RenderBody() line indicates where the content of the Razor page will be rendered.

Note that we also implement a navigation menu in this layout. Instead of giving the links in this navigation page a href element, we use asp-page, which converts into an appropriate href linking to one of our Razor pages on compilation. Thus asp-page="/Index" will point to our Index.cshtml.cs page. The asp-page is an example of a TagHelper, syntax that provides extra details to be processed by the Razor rendering engine.

We can include other sections within the layout with @RenderSection() For example, the @RenderSection("Scripts", required: false) will render a “Scripts” section, if there is one defined in our Razor page. We define such sections with the @section syntax, i.e.:

@section Scripts{
    <script src="my-script.js"></script>
}

Would place the additional <script> element in the rendered Razor page. You can define as many sections as you want.

While the _Pages/Shared/Layout.cshtml file is the default layout, you can define your own layout files. These should also be placed in the Pages/Shared folder, and their name should begin with an underscore. You can then set it to be used as the layout for your page by setting the page’s Layout property:

@{
    Layout = "_YourLayout";
}

Where the string you set the property to is the name of your layout.

Model Classes

The model class serves a similar role to the codebehind classes of your WPF and Windows Forms applications. Any public properties defined in the model class are accessible in the Razor page. I.e. if we defined a property:

public class IndexModel:PageModel {

    public DateTime CurrentTime 
    {
        get 
        {
            return DateTime.Now;
        }
    }

    public IActionResult OnGet()
    {

    }
}

We could use it in the corresponding Razor page:

@page 
@model IndexModel 

<p>The current time is @Model.CurrentTime</p>

In addition, the model class can define a method to be triggered on each HTTP Request, i.e. the OnGet() method will be triggered with each HTTP GET request, and OnPost() will be called with each HTTP POST request. You can define a method for any of the valid HTTP request verbs that will be invoked on each corresponding request.

Summary

In this chapter we explored how server-side web technologies have evolved to create dynamic web sites and web applications. The ASP.NET Core platform is Microsoft’s answer to this evolution. It can run on either Microsoft’s flagship IIS server, or as a stand-alone server on a variety of platforms. It brings together a suite of technologies to build web pages and web applications. We took an in-depth look at one of these - Razor pages, and learned how to build our own Razor pages app.