protips

Logo

This site lists the protips that we shared with students during our courses

View the Project on GitHub appliedtechnology/protips

How HTTP works and why you cant keep state in a field on controller class

When we create a C# MVC controller it might feel very reasonable to do something like this:

public class HomeController : Controller
{
  public User _user = null;
  public HomeController() { }

  public IActionResult Index(string id)
  {
    _user = getUserFromDatabase(id);
    return View(_user);
  }

  public IActionResult Details()
  {
    return View(_user);
  }
}

Let’s go through the code:

Why is this? Well, it has to do with the nature of web applications, and ultimately how HTTP works. Let’s clear it up a bit.

Request and response - that is it

A normal web request happens over HTTP that has a very simple flow:

  1. A client requests a resource (data or a page) from a server
  2. The server responds with some a representation of that data

That. Is. It. Nothing else can happen in HTTP (for the most cases that we need to think about at least).

So in the case when the client is a browser, we type in an URL in the address bar and wait. What happens is that the browser, behind the scenes, issues a HTTP GET request, passing the URL from the address-bar as the URL to GET data from. The server on that address (https://www.marcusoft.net for example) returns some data, typically a web page.

Connection between the client and the server is now shut down.

The user might then client then clicks a link in the page that was sent back from the server. This is will issue another HTTP GET request. And the server responds with a new page.

The key thing to note here is that every request is as if it is the first time the client and server is communicating with each other.

Why is that a good idea? Well, the server does not only serve one client. In fact there might be many many clients (users with browsers, other programs etc) that calls into the server. It is therefor much simpler to view each client as a unique, new client that the server never seen before, than to keep track of all the clients that has ever called the server before.

This is what is referred to as the STATELESS nature of HTTP. The server doesn’t keep any information, any state, about the client between request. Every request is like the first time we communicate.

The need for HTTP Headers

The fact that HTTP is stateless is also why we need headers in HTTP request. Because with headers we can pass meta-information, about and with each request (from the client to the server) and response (from the server to the client).

We could, for example, add some information that tells the server that this client has been authenticated in the last request. This is typically just an encrypted string, called a token.

We could also pass some data for settings the user has created. Or meta-information about the users activities and information.

The important part here is that we need to pass this information with every request. Remember that the server and client doesn’t know anything about each other between requests. HTTP is stateless. Hence the state needs to be passed back and forth. This done through HTTP Headers.

Didn’t you write anything about the field in a C# controller

Now, that was HTTP - but our original question was;

What happened with the data that I had in the _user-field? Why did that disappear?

Well, since HTTP is stateless it also means that our controllers doesn’t keep state around between request. It is if someone would do new HomeController every time a new request is being handled.

In fact, that is probably EXACTLY what happens. And it is ASP.NET Core MVC (the framework) that does that for us. .I wrote probably because I presume that they make something more optimized than that, but for all intents and purposes we can view it as being new HomeController on every request.

That means that the _user field will be set to null on each request.

But wait a minute here… what about HttpContext.Session

You might have seen code like this:

public IActionResult Index()
{
  var session = HttpContext.Session.Id;
  return View();
}

That Session.Id there is actually stored between requests. You could even store data in it and then retrieve it on the next request

// set data on one request
HttpContext.Session.Set("MyUser", _user);

// next request, in another action method
// get the data back from session
var user = HttpContext.Session.Get("MyUser");

The key to understand how this work is the HttpContext… The Session hangs off the HttpContext and this tells us that the Session works using HTTP somehow. So probably (again - this is most likely done in a much smarter way); MVC fakes that the server holds the Session in-memory between request.

But it doesn’t… It’s just looks like it was in-memory, but it’s actually faked using HTTP Headers.

(In reality, you can change how the session is stored, but let’s not mind that now)

Summary

You can’t store data on a field in a controller HomeController._user, because HTTP is stateless. Hence the controller will be recreated on each request and all data stored in the controller will be lost between requests.