MVC Framework
Last updated
Last updated
To better understand MVC Frameworks, I created a simple MVC website using ASP.NET. It features an MSSQL backend, with an administrative interface featuring some CRUD functions.
Model-View-Controller is a framework that separates an application into 3 logical components, which are the Model, View and Controller. Each component is built to handle a specific functionality of the web application.
The reason that this framework was established was due to applications supporting storage of information, displaying information and changing of stored information. These components are highly coupled and interlinked, and it can be difficult to debug should it be all lumped together.
Thus, classifying the code into 3 major components greatly helps developers, allowing individual testing for each component and reducing complexity. It provides a clear separation of business, UI and data input logic, allowing us to build more scalable web or mobile applications.
Here is the rough breakdown of the framework:
View: Displays data, interacts with the user, and pulls data from the model. Dynamically generated from the controller.
Controller: Handles, processes, and parses user input. Acts as an intermediary between the View (UI) and the backend logic (Model).
Model: Stores and maintains data, parses data and handles connections to databases.
The Model component stores and maintains data, and does database connections too. This is the part of the application that keeps the data structures, algorithms, computation engine and the logic that interacts with databases. There can be validation logic that enforces rules for the data (e.g. A string
variable cannot accept an int
type).
For example, suppose an application has a Animal
object. The Model is where I could declare the classes and functions used to store and retrieve data.
The Model would have the name and the public properties defined with the class. Apart form that, it can also contain logic for the database interactions to retrieve the objects like this:
The above is a DbContext
object used ot handle the task of connecting to the database. Using this, one can construct the code for an example query like so in Main
:
Generally, the Controller is the one that contains the code that is in charge of using the data models amd methods defined in the Model component. The displaying of the results is done in the View component.
In summary, the Model component defines all the data types, classes and methods for data within the application.
The Controller is responsible for handling the user's input, processing it and updating the Model objects with the data retrieved. It invokes methods from the Model to do stuff like create new Animal
objects, or perhaps store data within a database. It also does the routing for the web applications, and is typically responsible for sanitising user input.
For example, here's how to define a route that accepts POST requests:
Firstly, an AnimalController
class is defined which inherits from the general Controller
class. These controllers are the things that handle HTTP requests.
Afterwards, I defined an attribute to the addAnimal()
method. I specified that this method takes HTTP POST requests and verifies CSRF tokens to prevent forgery attempts. The route itself can be specified using [Route("animals/add")]
if needed. However, this can also be done in the Startup.cs
file located in wwwroot
, depends on what is required. ASP.NET also infers the route from the controller and actions anmes automatically, so the actual directory to add animals would be /Animal/AddAnimal
.
ASP.NET MVC also automatically parses the POST parameters from the user, and matches creates a new Animal
object, which I find pretty cool. Alternatively, the parameters can be manually retrieved like so:
It is recommended to let ASP.NET do this automatically since it is easier for the programmer, unless specific logic to pass data elsewhere is required.
Apart from POST requests, other HTTP methods like GET, PUT or DELETE can be specified using attributes.
Note that IActionResult
is an interface in ASP.NET Core that represents the result of an action method. It is the base interface for all action results in ASP.NET core. Returning View()
would render the HTML view associated with the action (again, ASP.NET is kinda hella smart to associate this for me).
There are other interfaces like IWebHost
which represents the hosting environment, or IConfiguration
that sets the configuration settings (such as setting the default page to /home/index
) in Startup.cs
.
Lastly, there is an in-built dependency injection technique, which basically provides the objects that an objects needs rather than having it create them. Dependencies are usually passed through constructors, and inverts the flow of control. This is because the object itself creates the object it needs!
The below code would inject the instance through the constructor, instead of creating a new instance within the controller for the AnimalController
class. Again, this can be configured within the Startup.cs
file.
In conclusion, Controller does all the processing of user input, and updates the Model (e.g. database update) using the methods defined in Model, and the content displayed to the user in the View.
The View represents the user interface of an application, responsible for dynamically displaying data to the user, and also capturing the user's input through forms or clicks. The captured input is sent to the Controller for processing.
For ASP.NET, View can use Razor to embed server-based code into web pages. It is a combination of HTML and C# code (.cshtml
files). This allows the developer to display server information retrieved from the Model.
The View class can also take input via forms:
This specifies that something uses the Animal
class from the Model component, and a simple form is created to take user input. Separate files can be created to display the data:
In short, View takes user input, sends / receives it to Controller, and displays it. Razor is a powerful templating engine, that is sometimes vulnerable to SSTI if developers use Razor.Parse
and .Template
on unsanitised inputs.
Here's a simple web application I created to attempt to understand MVC and ASP.NET better.
Below is a look into some files created and components in an MVC application (just because I was curious).
wwwroot
When creating a new ASP.NET MVC project, one can see the files automatically created:
The Models, Views and Controllers folders are where the majority of our code will go.
The wwwroot
file is the web root folder, and static files can be stored under that folder and viewed from the browser. Code and Razor files should not be placed here!
Program.cs
Program.cs
is the entry point. All the services are configured and started up here. The explanation behind all the code for it can be found here:
The only thing I noted was that services could be added to the container. The default calls buider.services.AddControllerWithViews()
, and I can do AddRazorPages()
to add that.
Database contexts are added to the container at this point as well! After all the services are added, builder.Build();
is invoked to instantiate a WebAppication
object with whatever services enabled.
Next would be the Middleware portion, which is the request handling pipeline. This is the default code:
Self-explanatory.
This is the part that gives us control over the URLs used by the application, and it calls UseRouting
to do so. Custom routes can be specified like this:
MapGet
is used to define and endpoint, which is something that can be selected by the URL and HTTP method, and executed.
Route templates can be used to customise its behaviour:
The usage of :alpha
constrains the input to only have alphabetic characters. They act like regex and only select the matching characters. Here are a few more:
{id:int}
: Match any integer
{money:float}
: Match any float value.
{ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}
: Specifies regex to match.
{input:required}
: Enforce a parameter.
Routing also covers authorisation:
Pretty interesting how easy it is to add routes.