Interview Questions: Routing, Middleware and Razor vs. Razor Pages

Understand routing, middleware, dependency injection, and more

Posted by Rodrigo Castro on June 27, 2025

If you’re preparing for a .NET developer interview, these are five fundamental ASP.NET Core questions that appear regularly. Here’s a breakdown with added depth, like you’d expect in a real-world selection process.

How does Routing work in ASP.NET Core?

Routing in ASP.NET Core is handled by the Endpoint Routing system, introduced in ASP.NET Core 3.0 and now the standard. Routing maps incoming HTTP requests to executable endpoints in your app: typically controller actions, Razor Pages, or Minimal API delegates.

Pipeline Configuration

Routing is configured in Program.cs using:

1
2
3
4
5
6
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

Here’s what happens:

  • UseRouting() discovers route definitions.
  • UseAuthorization() and other middleware can act on matched routes.
  • UseEndpoints() finalizes and executes the match.

Route Types

  • Attribute Routing: [HttpGet("api/products/{id}")]
  • Convention-based Routing: endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");

Routing Constraints and Attributes

You can add constraints and annotations for more control:

  • Route constraints: [HttpGet("api/user/{id:int:min(1)}")]
  • String-only required segments: [HttpGet("api/user/{id:string:required}")]
  • Use [FromBody], [FromQuery], [FromRoute] to explicitly bind parameters
  • Nullable checks: [FromBody, NotNull] User user

Example:

1
2
[HttpPost("api/user/{id:guid}")]
public IActionResult CreateUser([FromRoute] Guid id, [FromBody, NotNull] CreateUserRequest body)

This ensures:

  • id must be a Guid
  • body must be present in request and not null

If you have more time, investigate further at Microsoft Learn!

Routing Order

  • Minimal APIs and endpoints are matched in order of registration.
  • Attribute routes override conventional ones.

What is Middleware and in what order do they execute?

Middleware is software assembled into an application pipeline to handle requests and responses. Think of it as a chain of delegates. Each middleware component can:

  • Process request
  • Pass the request to the next component via await next()
  • Process the response after next middleware completes

Middleware can also be custom-built to handle specific needs, like logging, request throttling, or custom authentication. But beware: don’t rewrite the wheel or overengineer your pipeline. The more middleware you have, the harder it is to maintain and debug. Aim for simplicity: the fewer middleware components, the better.

Execution Order

They execute in the order registered in Program.cs. For example:

1
2
3
4
app.UseRouting(); // 1
app.UseAuthentication(); // 2
app.UseAuthorization(); // 3
app.UseEndpoints(); // 4

Custom Middleware

Custom middleware can be created as a class or inline using:

1
2
3
4
5
6
app.Use(async (context, next) =>
{
    // Before next middleware
    await next();
    // After next middleware
});

How can you stop other middlewares from executing?

You can stop the pipeline by not calling await next(). Example:

1
2
3
4
5
6
7
8
9
10
app.Use(async (context, next) =>
{
    if (!context.User.Identity.IsAuthenticated)
    {
        context.Response.StatusCode = 401;
        await context.Response.WriteAsync("Unauthorized");
        return; // Prevent further middleware
    }
    await next();
});

Once you short-circuit, subsequent middleware (and endpoint handlers) won’t run.

This is common for:

  • Authentication/authorization failures
  • Maintenance mode
  • Early return with cached response

What is the difference between MVC and Razor Pages?

Both are built on the same infrastructure, but they differ in structure and use case.

Feature MVC Razor Pages
Structure Controller + View + Model View + PageModel in one
Suited for Large, complex apps CRUD, page-focused apps
Routing Convention/Attribute Folder/File-based
Testability More granular Page-focused integration
Separation Logic and view are split Logic/view are together

What is Razor Pages?

Razor Pages is a newer feature introduced in ASP.NET Core 2.0. Instead of separating controller logic from views like MVC does, Razor Pages keeps them together. Each page has a .cshtml view and an associated .cshtml.cs PageModel file. This model simplifies development for page-centric apps like admin dashboards, CRUD pages, or forms.

Side-by-side Code Example

MVC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Controller
public class ProductsController : Controller
{
    public IActionResult Details(int id)
    {
        var product = productService.GetById(id);
        return View(product);
    }

    [HttpPost]
    public IActionResult Add([FromForm] Product product)
    {
        productService.Add(product);
        return RedirectToAction("Details", new { id = product.Id });
    }
}

// View: Views/Products/Details.cshtml
@model Product
<h2>@Model.Name</h2>

<form method="post" asp-action="Add">
    <input asp-for="Name" />
    <input asp-for="Price" />
    <button type="submit">Add</button>
</form>

Razor Pages

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Pages/Products/Details.cshtml.cs
public class DetailsModel : PageModel
{
    private readonly IProductService productService;

    public Product Product { get; private set; }

    [BindProperty]
    public Product NewProduct { get; set; }

    public DetailsModel(IProductService productService) => this.productService = productService;

    public void OnGet(int id)
    {
        Product = productService.GetById(id);
    }

    public IActionResult OnPost()
    {
        if (!ModelState.IsValid) return Page();

        productService.Add(NewProduct);
        return RedirectToPage("./Details", new { id = NewProduct.Id });
    }
}

// Pages/Products/Details.cshtml
@page "{id}"
@model DetailsModel
<h2>@Model.Product.Name</h2>

<form method="post">
    <input asp-for="NewProduct.Name" />
    <input asp-for="NewProduct.Price" />
    <button type="submit">Add</button>
</form>

When to use each?

Use Razor Pages if:

  • Your app is primarily made of pages, not APIs
  • You want a simpler, organized structure with logic close to the view
  • You’re building internal tools or admin panels

Use MVC if:

  • You need full control over routing and separation of concerns
  • Your app has a complex domain layer
  • You’re building an API-first or service-oriented system

Name 3 ways to create Middleware

  1. Inline Middleware:
1
app.Use(async (context, next) => { await next(); });
  1. Middleware Class with Invoke:
1
2
3
4
5
6
7
8
9
10
public class LoggingMiddleware
{
    private readonly RequestDelegate _next;
    public LoggingMiddleware(RequestDelegate next) => _next = next;
    public async Task Invoke(HttpContext context)
    {
        Console.WriteLine("Request: " + context.Request.Path);
        await _next(context);
    }
}

And register with:

1
app.UseMiddleware<LoggingMiddleware>();
  1. IMiddleware + DI:

More testable, services are injected via constructor.

1
2
3
4
public class AuthMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next) { ... }
}

Register:

1
2
services.AddTransient<AuthMiddleware>();
app.UseMiddleware<AuthMiddleware>();

Tip: Understand not just “how” middleware works, but also “why” the order matters: it can define the security, performance, and behavior of your app.

In the next posts I’ll be adding more and more posts about interviewing questions and stuff related to that, so keep posted.