What’s this? A post about WebForms?

Is it 2002 all over again? Is GeoCities still going strong? Are we all about to invest endless hours building SOAP services and learning XML?

No, no we’re not.

But, just because it feels like everyone is off building shiny new apps using ASP.NET Core 2.x the truth is there are still plenty of us spending at least some of our days working with “legacy” (and sometimes not so legacy) apps built using WebForms.

And if you’re trying to move to something else at this moment in time you’d be forgiven for feeling overwhelmed.

The web is awash with information covering every topic you could possibly imagine (and then some)…

  • SPAs (Angular, React etc)
  • MVC 5
  • MVC Core
  • Razor Pages
  • .NET Framework
  • .NET Core

Where do you start?

We’ve got to start somewhere so let’s begin with performing a common WebForms task using ASP.NET Core MVC…

If you’re new to MVC you could be forgiven for feeling confused.

There’s a lot of terminology to get to grips with, where Webforms had one or two moving parts MVC has several.

But when you take a step back and explore what WebForms is doing under the hood it becomes easier to translate that to MVC.

Change label text via a Postback

WebForms did its best to act like a Windows app which happened to run in the browser but in reality it uses a little smoke and mirrors to hide what it’s actually up to.

Here’s an example.

Create a page with your button and label, something like this…

<form id="form1" runat="server">
    <div>
        <asp:Button ID="btnChangeLabel" runat="server"
            OnClick="btnChangeLabel_Click"
            Text="Boring, give me another greeting" />

        <asp:Label ID="lblGreeting" runat="server">
            Welcome one and all
        </asp:Label>
    </div>
</form>

Create a handler to handle the button click event, set the new property value for the label and voila, the label is updated when you click the button.

protected void btnChangeLabel_Click(object sender, EventArgs e)
{
    this.lblGreeting.Text = "Hello, this is the changed greeting :-)";
}

Unpicking the abstraction

This might look and feel like a windows app running on the desktop but “under the hood” ASP.NET is simply making GET and POST requests from the browser to the server.

Yep, it’s doing exactly what every other web application ever written does, make requests to the server.

Here’s what’s actually happening.

You request /SimpleLabel in the browser

Your browser makes an HTTP GET request

WebForms Get Request

ASP.NET handles the GET request and routes to the corresponding WebForm (SimpleLabel.aspx)

ASP.NET renders html for the label/button controls and returns it to the browser

The rendered html includes a form which will be submitted when the button is clicked

You click the button

The browser makes an HTTP POST request to the server (submits the form)

The "btnChangeLabel_Click" event handler runs, changing the label's text

ASP.NET re-renders the html (with the new value for the label) and returns it to the browser

You see the new value in the browser

So you see, it’s not actually magic, just an HTTP GET and an HTTP POST.

How does MVC do it?

Now let’s take the exact same requirement and do it with MVC.

The main difference is that you are no longer quite so abstracted away from the HTTP requests and HTML markup that your app is ultimately going to use.

Instead of a WebForm with ASP.NET controls, you need a view with (mostly) standard HTML tags.

Index.cshtml

@model SimpleLabelModel

<form method="post">
    <input type="submit" value="Boring, give me another greeting" />
    <span>@Model.Greeting</span>
</form>

The only non-html part of this markup is the first line and the subsequent reference to @Model.Greeting which is Razor syntax to enable you to take data coming from the server and mix it up with your HTML.

Now we have our button and a greeting, but how do we get to this in a browser?

We can’t, not without a controller to handle requests (from the browser) to our application.

SimpleLabelController.cs

[Route("[controller]")]
public class SimpleLabelController : Controller
{
    [HttpGet("")]
    public IActionResult Get()
    {
        var model = new SimpleLabelModel { Greeting = "Welcome one and all" };
        return View("Index", model);
    }
}

Now we’re getting somewhere, a request to go to /simplelabel in the browser will be routed to our Get action where we return our view (index.cshtml).

We also return an instance of our view’s model (SimpleLabelModel) which (if you’re interested) looks like this.

SimpleLabelModel.cs

public class SimpleLabelModel
{
    public string Greeting { get; set; }
}

Remember this line in the view?

<span>@Model.Greeting</span>

That ensures the value we just set in the Model (“Welcome one and all”) is displayed as a span in the rendered HTML.

How does MVC know which requests go to which controller?

Your MVC app can handle any requests you throw at it but you need to send them somewhere right?

In this example we’re using attribute routing.

[Route("[controller]")]

This attribute on the controller means the first part of the URL will be the name of the controller e.g. https://<your-site-here>/SimpleLabel.

[HttpGet("")]

This attribute on the controller action (the method in that controller) simply ensures that any get requests to https://<your-site-here>/SimpleLabel will end up here.

If we’d done this instead…

[HttpGet("fancy")]

… the url to get to this method would be https://<your-site-here>/SimpleLabel/fancy

Now what about handling that button click?

The button will submit the form, which will post back to /simple-label but wait, we don’t have any controller action to handle it!

SimpleLabelController.cs

[Route("[controller]")]
public class SimpleLabelController : Controller
{
    // GET method omitted for brevity

    [HttpPost("")]
    public IActionResult WelcomeMe()
    {
        var model = new SimpleLabelModel
        {
            Greeting = "Hello, this is the changed greeting :-)"
        };
        return View("Index", model);
    }
}

And there you have it! Click the button, the browser submits the form (via a POST) and the corresponding controller action returns a new instance of the model with our new and improved greeting.

Putting it all together

Here’s the MVC flow then…

You request /SimpleLabel in the browser

Your browser makes an HTTP GET request

WebForms Get Request

ASP.NET handles the GET request and routes to the corresponding Controller Action

ASP.NET renders your html (mixing in values from the model via Razor) and returns it to the browser

The rendered html is the HTML from the view, with the "dynamic data" from the model included.

You click the button

The browser makes an HTTP POST request to the server (submits the form)

The relevant controller action (for the HTTP Post) is called

ASP.NET re-renders the html (with a different model containing a different greeting) and returns it to the browser

You see the new value in the browser

More moving parts

Looking back at this you can see that MVC requires you to handle more moving parts (controllers, views and view models).

But with this increased complexity you also get more freedom to craft your routes, your html, even to start adding javascript to your views where it improves the user experience.

Before you go

Get a handy PDF showing the two flows (WebForms and MVC) next to each other, plus source code for both examples.

Compare WebForms to MVC

Handy PDF + source code showing WebForms vs MVC.

You'll get ASP.NET advice, guidance and useful tips from me once a week (or so). I respect your email privacy, unsubscribe at any time.