This site lists the protips that we shared with students during our courses
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:
Index
-action method we get the a user from the database
HomeController
called _user
_user
is also passed to the view (View(_user)
) to show the data in the Razor view.Details
-action method we use that _user
-field again
_user
-field is set to null
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.
A normal web request happens over HTTP that has a very simple flow:
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 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.
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.
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)
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.