Authentication and Authorization in ASP.NET Core

What is Authentication?

Authentication is the process of verifying who the user is.

It ensures the user’s identity is valid, typically through:

  • Username/Password
  • Tokens (e.g., JWT)
  • Cookies
  • External providers (Google, Facebook, Azure AD)

Example: If you log in using your email and password, that’s authentication. If your credentials are correct, the server "knows" who you are.

2. What is Authorization?

Authorization is the process of determining what an authenticated user is allowed to do.

After you are authenticated:

  • Are you allowed to access this endpoint?
  • Do you have the required role?
  • Do you have permission to perform a certain action?

What happens when the user logs in?

  • User submits credentials via login form or API (e.g., /login)
  • Backend validates the credentials (e.g., against a database)
  • If valid, the server issues an authentication token (JWT or sets a cookie).
  • On the next request, the client sends the token or cookie back
  • The middleware validates it and sets the User. Identity
  • Controllers/actions protected by [Authorize] now recognize the user

Example: JWT Authentication (API-Based)

Step 1. Add JWT Authentication Packages.

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

Step 2: Configure Authentication in the Program.cs.
builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = "your-app",
            ValidateAudience = true,
            ValidAudience = "your-client",
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes("your-secret-key")),
            ValidateLifetime = true
        };
    });

builder.Services.AddAuthorization();

Then in middleware:
var app = builder.Build();

app.UseAuthentication();   // Must be before UseAuthorization
app.UseAuthorization();

app.MapControllers();

Step 3: Login Endpoint: Validate Credentials and Generate Token
[HttpPost("login")]
public IActionResult Login(LoginModel model)
{
    if (model.Username == "admin" && model.Password == "password") // DB check in real case
    {
        var claims = new[]
        {
            new Claim(ClaimTypes.Name, model.Username),
            new Claim(ClaimTypes.Role, "Admin")
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-secret-key"));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: "your-app",
            audience: "your-client",
            claims: claims,
            expires: DateTime.Now.AddHours(1),
            signingCredentials: creds);

        return Ok(new
        {
            token = new JwtSecurityTokenHandler().WriteToken(token)
        });
    }

    return Unauthorized("Invalid credentials");
}

Step 4: Client Sends JWT in Authorization Header
On every request to a protected API:
GET /api/secure-data
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Step 5: Use [Authorize] to Protect Routes
[Authorize]
[HttpGet("secure-data")]
public IActionResult GetSecureData()
{
    var username = User.Identity?.Name;
    return Ok($"This data is only visible to {username}");
}

How does it work behind the Scenes?

Step 1: Authentication Middleware checks the incoming request
Step 2: Look for a valid Authorization header (with JWT)
Step 3: If valid:
  • It decodes the token
  • Sets HttpContext.User with claims and identity
Step 4: Then, [Authorize] uses this info to allow or deny access

[Authorize] → Requires the user to be authenticated
[AllowAnonymous] → Bypasses authorization for that method

JWT Token Structure.

A JWT token is made of 3 parts:
  • HEADER
  • PAYLOAD
  • SIGNATURE
1. HEADER
The Header contains metadata about the token:
{
  "alg": "HS256",   // Algorithm used (HMAC SHA-256)
  "typ": "JWT"      // Token type
}

2. PAYLOAD (this is where all your data is)
This is the main body of the JWT. It contains:
  • iss Issuer – who created the token
  • aud Audience – who the token is for
  • exp Expiration time (Unix timestamp)
  • nbf Not before – token valid after this time
  • iat Issued at time
  • sub Subject – usually the user ID
  • jti Token ID – unique identifier
Example:
{
  "iss": "JwtAuthDemo",
  "aud": "JwtAuthClient",
  "exp": 1720542515,
  "sub": "123456"
}

Custom Claims (You define these!)
These are the claims you add for user info, roles, etc.

Example:
{
  "name": "admin",
  "email": "admin@example.com",
  "role": "Admin",
  "department": "HR"
}

ASP.NET Core reads the role claim automatically for [Authorize(Roles = "...")].

3. SIGNATURE
Signature is used to validate the token’s integrity using your secret key.
It's computed like this:
HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret-key
)

If someone tries to tamper with the payload, the signature won’t match anymore.

Note: JWT is not encrypted, just base64-encoded. Never store sensitive information like passwords in it.

Middleware in ASP.NET Core.

What is Middleware?

Middleware in ASP.NET Core is a software component that is executed on every HTTP request and response. Middleware components are chained together to form a request-processing pipeline.

Each middleware:
  • Can perform operations before and after the next component in the pipeline
  • Can short-circuit the pipeline (e.g., for authentication, error handling)
  • Is executed in the order registered in the Program.cs

Middleware Examples
ASP.NET Core has built-in middleware such as:
  • UseRouting() – for routing requests
  • UseAuthentication() – for checking identity
  • UseAuthorization() – for permission control
  • UseEndpoints() – for mapping controllers
Middleware executes in the order added — request goes top to bottom, response goes bottom to top.

Request Pipeline Flow
Here’s how a typical request moves through middleware:
Request
  
[Middleware 1]  [Middleware 2]  ...  [Middleware N]  Controller
                                                  
Response                     

Each middleware has a chance to:
  • Do something before passing the request forward
  • Do something after the next middleware has completed

How to Create a Custom Middleware.

Step 1: Create a class with a constructor accepting RequestDelegate
Step 2: Add InvokeAsync(HttpContext) method
Step 3: Call _next(context) inside the method
Step 4: Register it using app.UseMiddleware<YourMiddleware>()

Step 1: Create a Middleware Class
public class LoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<LoggingMiddleware> _logger;

    public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        _logger.LogInformation($"Incoming request: {context.Request.Method} {context.Request.Path}");
        await _next(context);
        _logger.LogInformation($"Response: {context.Response.StatusCode}");
    }
}

Step 2: To keep Program.cs clean, create extension methods for your middleware.
public static class MiddlewareExtensions
{
    public static IApplicationBuilder UseLoggingMiddleware(this IApplicationBuilder app)
    {
        return app.UseMiddleware<LoggingMiddleware>();
    }
}

Step 3: Program.cs (or Startup.cs in older projects)
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseLoggingMiddleware(); // 👈 From your extension method

app.UseRouting();
app.UseAuthorization();
app.MapControllers();

app.Run();

📁 Recommended Middleware File Structure.

/YourProjectRoot

├── Program.cs
├── Startup.cs (if applicable)

├── Middleware/
   ├── LoggingMiddleware.cs
   ├── ExceptionHandlingMiddleware.cs
   └── RequestTimingMiddleware.cs

└── Extensions/
    └── MiddlewareExtensions.cs

When to Use Custom Middleware?

You might create custom middleware to:
  • Log requests and responses
  • Handle exceptions globally
  • Modify headers
  • Implement custom authentication or IP filtering
  • Measure performance/response time

Typical Middleware Order in Program.cs

Here is the recommended and logical order of middleware in most real-world ASP.NET Core applications:
var app = builder.Build();

app.UseExceptionHandler("/error");         // 1. Global exception handling
app.UseHsts();                             // 2. Enforce HTTPS strict transport (in production)
app.UseHttpsRedirection();                 // 3. Redirect HTTP to HTTPS
app.UseStaticFiles();                      // 4. Serve static files (JS, CSS, images)
app.UseRouting();                          // 5. Enable routing system
app.UseCors();                             // 6. Enable CORS if needed
app.UseAuthentication();                   // 7. Identity/authentication check
app.UseAuthorization();                    // 8. Role-based or policy-based authorization
app.UseMiddleware<CustomLoggingMiddleware>(); // 9. Your custom middlewares
app.UseEndpoints(endpoints =>              // 10. Connects routes to controllers
{
    endpoints.MapControllers();
});

app.Run();

Common Middleware Interview Question.

Q1. What is the difference between Use, Run, and Map?
  • Use: Adds middleware to the pipeline, can call next
  • Run: Terminal middleware, doesn't call next
  • Map: Branches pipeline based on request path

Q2. Can a middleware return a response without calling the next middleware?
Answer Tip: Yes. This is called short-circuiting. For example, authentication or CORS middleware might return a response early.

Q3. What happens if you don't call _next(context) in a middleware?
Answer Tip: The pipeline is terminated, and the request doesn't reach subsequent middleware or the controller.

Q4. How do you handle exceptions globally in middleware?
Answer: Use a custom exception-handling middleware or app.UseExceptionHandler().

LINQ In Entity Framework Core.

Entity Framework Core (EF Core) is a powerful Object-Relational Mapper (ORM) for .NET that allows developers to interact with databases using .NET objects. One of EF Core’s most important and powerful features is LINQ (Language Integrated Query) — a feature that allows you to write queries directly in C# using intuitive syntax.

Whether you're working on a Web API, MVC application, or a console app, LINQ in EF Core makes data querying clean, efficient, and maintainable.

What is LINQ?

LINQ (Language Integrated Query) allows you to query collections (like arrays, lists, and database sets) using C# syntax. With EF Core, LINQ becomes the primary way to interact with your database.

Instead of writing raw SQL queries, you use C# expressions to retrieve, filter, and project data from the database.

Benefits of Using LINQ in EF Core

  • Type-safe queries
  • IntelliSense support in IDEs
  • Compile-time checking
  • Improved maintainability
  • No need for raw SQL


Types of LINQ Queries

EF Core supports two styles of LINQ:

1. Method Syntax (most common)
var products = await _context.Products
    .Where(p => p.Price > 100)
    .OrderBy(p => p.Name)
    .ToListAsync();

2. Query Syntax
var products = await (from p in _context.Products
                      where p.Price > 100
                      orderby p.Name
                      select p).ToListAsync();

Both styles generate the same SQL under the hood.

Common LINQ Query Examples in EF Core.

1. Get All Records:
var products = await _context.Products.ToListAsync();

2. Filter Records (WHERE)
var expensive = await _context.Products
    .Where(p => p.Price > 500)
    .ToListAsync();

3. Select Specific Fields (SELECT)
var names = await _context.Products
    .Select(p => p.Name)
    .ToListAsync();

4. Sort Records (ORDER BY)
var sorted = await _context.Products
    .OrderByDescending(p => p.Price)
    .ToListAsync();

5. Get Single Record
var product = await _context.Products
    .FirstOrDefaultAsync(p => p.Id == 1);

6. Check Existence (ANY)
bool exists = await _context.Products.AnyAsync(p => p.Price > 1000);

7. Count Records
int count = await _context.Products.CountAsync();

8. Join With Another Table
var orders = await (from o in _context.Orders
                    join c in _context.Customers on o.CustomerId equals c.Id
                    select new { o.Id, c.Name }).ToListAsync();

Asynchronous Queries in LINQ

EF Core supports async versions of most LINQ methods, like:
  • ToListAsync()
  • FirstOrDefaultAsync()
  • AnyAsync()
  • CountAsync()
These async methods are essential in ASP.NET Core to avoid thread blocking and improve scalability.

Performance Tips for LINQ.

  • Use `AsNoTracking()` for read-only: Improves performance, skips tracking overhead
  • Use `Select()` to limit columns: Reduces DB load and memory usage       
  • Avoid multiple `.ToListAsync()`: Combine filters before executing the query
  • Use `AnyAsync()` instead of `Count()`: Faster existence check

Why Use .Include()?

By default, EF Core uses lazy loading, which means related data is not loaded unless explicitly accessed, and that can cause N+1 query problems.

.Include() solves this by retrieving the main entity and its related entities in one go.

Example Scenario
Suppose you have these two related entities:
var products = await _context.Products
    .Include(p => p.Category)
    .ToListAsync();

foreach (var product in products)
{
    Console.WriteLine($"Product: {product.Name}, Category: {product.Category.Name}");
}

This performs a JOIN behind the scenes and loads both the Product and its related Category.

You can use .ThenInclude() to include deeper navigation:
var products = await _context.Products
    .Include(p => p.Category)
        .ThenInclude(c => c.Supplier)
    .ToListAsync();

Common Pitfalls
  • Forget to use await with async methods: Leads to unexecuted queries or runtime bugs.
  • Loading too much data: Avoid pulling unnecessary columns or rows.
  • N+1 queries: Use .Include() to load related data when needed.

Pagination is an essential technique in web development to divide large sets of data into manageable chunks (pages) so you don’t overload the server or the user interface. Let's understand this in detail:

What is Pagination?

Pagination is the process of retrieving a specific subset of records (usually a page of data) from a larger data source. Instead of fetching all records at once, you fetch only what is needed for the current page.

Example: Imagine you have 10,000 products in your database. You don’t want to load them all at once, so you display 10 or 20 products per page:

Key Parameters in Pagination

  • PageNumber: The current page number (e.g., 1, 2, 3…)
  • PageSize: Number of records to show per page (e.g., 10, 20)
  • Skip: Number of records to skip: (PageNumber - 1) * PageSize
  • Take: Number of records to fetch: PageSize

Example in EF Core (LINQ): Let's say you want Page 2 with 5 items per page:
[HttpGet]
public async Task<IActionResult> GetPagedProducts(int pageNumber = 1, int pageSize = 10)
{
    var totalCount = await _context.Products.CountAsync();

    var products = await _context.Products
        .OrderBy(p => p.Id)
        .Skip((pageNumber - 1) * pageSize)
        .Take(pageSize)
        .ToListAsync();

    var result = new
    {
        TotalCount = totalCount,
        PageSize = pageSize,
        CurrentPage = pageNumber,
        TotalPages = (int)Math.Ceiling((double)totalCount / pageSize),
        Data = products
    };

    return Ok(result);
}

This generates a SQL query that only returns 5 records, starting from the 6th product (ID 6–10).

Using LINQ in EF Core makes your database logic clean, expressive, and maintainable — a key advantage in modern ASP.NET Core development.

Entity Framework Core (EF Core).

Entity Framework Core (EF Core) is a modern, lightweight, and extensible version of the Entity Framework, which is an Object-Relational Mapper (ORM) for .NET applications. It allows developers to work with databases using .NET objects, eliminating the need for most of the data-access code that developers usually need to write. Below, we will explore the key concepts of EF Core, including the Code-First approach, DbContext and DbSet, migrations, the repository pattern and unit of work, and LINQ queries with async operations.

What is Entity Framework Core?

Entity Framework Core (EF Core) is a modern, lightweight, open-source Object-Relational Mapper (ORM) for .NET. It allows developers to interact with databases using C# objects, eliminating the need to write most raw SQL queries.

🧠 Think of EF Core as a bridge between your C# code and the SQL database.

Key Features of EF Core

  • Cross-platform: Works on Windows, Linux, macOS
  • LINQ support: Query databases using C# syntax
  • Change tracking: Tracks changes in objects to update the DB
  • Migrations: Manage schema changes via code
  • Lazy/Eager/Explicit Loading: Controls how related data is loaded
  • Concurrency Handling: Manages data conflicts in multi-user apps

There are two types of Entity Framework Core approaches that we follow to interact with the database.
  • Database First Approach.
  • Code First Approach.

What is the Code-First Approach?

In the Code-First approach, you define your database schema using C# classes, and EF Core generates the database from this code. You don’t need an existing database instead you start with code, then create and update the database using migrations.

How Code-First Works:

Step 1: Create C# classes to represent tables (called entities).
Step 2: Create a DbContext class to manage the database connection and sets.
Step 3: Configure EF Core in Program.cs.
Step 4: Use Migrations to generate the schema and apply it to the database.

Step-by-Step Example of Code-First Approach.

Step 1. Install EF Core NuGet Packages.

dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

Step 2. Define Your Entity (Model)
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

public class Product
{
    [Key]                                // Defines primary key
    public int Id { get; set; }

    [Required]                           // NOT NULL
    [MaxLength(100)]                     // Max length for Name
    public string Name { get; set; }

    [Column(TypeName = "decimal(10,2)")] // Define precision for Price
    [Range(0, 99999.99)]                 // Validation range
    public decimal Price { get; set; }

    [Required]
    [DefaultValue(true)]                 // Default value
    public bool IsAvailable { get; set; } = true;
}

In Entity Framework Core, you can define constraints using Data Annotations (attributes on properties).

Step 3: Create a DbContext.
public class AppDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }

    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    {
    }
}

This is your custom database context class, and it's a key part of using Entity Framework Core (EF Core) with the Code-First approach.
EF Core uses this class to:
  • Configure the connection to the database
  • Map your C# models (like Product) to database tables
  • Track changes and execute queries
  • Save data to the database
DbContext is the core class in EF Core that manages all database operations (querying, saving, updating, etc.).

DbSet<Product> tells EF Core: “I want a table called Products in the database, and each row will be a Product object.”

Step 4: Register the DbContext in Program.cs.
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
And in appSettings.json
"ConnectionStrings": {
  "DefaultConnection": "Server=.;Database=EFCoreDemo;Trusted_Connection=True;"
}
Here we are defining a connection string that will help us connect to our database locally or remotely.

Step 5: Add and Apply Migrations.
dotnet ef migrations add InitialCreate
dotnet ef database update

EF Core will:
  • Create a Migrations folder
  • Generate SQL commands
  • Apply them to your database
Step 6. Use the DbContext to Perform CRUD
public class ProductService
{
    private readonly AppDbContext _context;
    
    public ProductService(AppDbContext context)
    {
        _context = context;
    }

    public async Task AddProduct()
    {
        var product = new Product { Name = "Laptop", Price = 1500 };
        _context.Products.Add(product);
        await _context.SaveChangesAsync();
    }
}

EF Core will keep your code and database in sync using migrations, making your development fast, clean, and flexible.

What is the Database-First Approach?

In the Database-First approach, you start with an existing database, and Entity Framework Core generates the C# classes (models and DbContext) based on that database schema.

When to Use It?

  • You already have a pre-existing database
  • You're working with legacy systems
  • You want your C# models to match a database that someone else designed

Step-by-Step Example of Database-First Approach:

Step 1: Install Required NuGet Packages.
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

Step 2: Run the Scaffold Command
Use the following CLI command to reverse engineer your DB:
dotnet ef dbcontext scaffold "Your_Connection_String" Microsoft.EntityFrameworkCore.SqlServer -o Models

Explanation:
  • "Your_Connection_String" – The connection string to your existing DB
  • Microsoft.EntityFrameworkCore.SqlServer – The database provider
  • -o Models – Output directory for generated models and DbContext
What It Generates:
  • DbContext file → manages the connection to the DB
  • Model classes for each table → mapped to DB tables

What is the Repository Pattern?

The Repository Pattern acts as a mediator between the business logic and the data access layer. It hides the details of data access and provides a simple and consistent API for performing database operations.

Benefits:
  • Encapsulates data access logic
  • Promotes loose coupling
  • Makes unit testing easier
  • Clean separation of concerns

Dependancy Injection and Its Lifetime in ASP.NET Core.

Dependency Injection (DI) is a fundamental concept in ASP.NET Core. It allows you to write loosely coupled, testable, and maintainable code by injecting the required dependencies into your classes instead of hard-coding them. ASP.NET Core has a built-in DI container that supports constructor injection and handles object lifetimes efficiently.

What is Dependency Injection?

Dependency Injection (DI) is a design pattern used to achieve Inversion of Control (IoC) between classes and their dependencies. Rather than a class creating its own dependencies, they are provided from outside, typically via the constructor.

📌 In simple terms: A class doesn’t create what it needs — it receives it from someone else.

Use of Dependency Injection:

  • Promotes loose coupling
  • Makes code testable
  • Supports the separation of concerns
  • Encourages interface-based programming

Step-by-Step Implementation of Dependency Injection:

Step 1: Create a New ASP.NET Core Project: You can create a new project using the .NET CLI or Visual Studio.

dotnet new webapi -n MyApi
cd MyApi

Step 2: Define a Service Interface: Create an interface that defines the contract for the service.
public interface IProductService
{
   IEnumerable<string> GetProducts();
}
   

Step 3: Implement the Service: Create a class that implements the service interface.
   public class ProductService : IProductService
   {
       public IEnumerable<string> GetProducts()
       {
           return new List<string> { "Product1", "Product2", "Product3" };
       }
   }
   

Step 4: Registering Services in the Dependency Injection Container.
   var builder = WebApplication.CreateBuilder(args);

   // Register the ProductService with the DI container
   builder.Services.AddScoped<IProductService, ProductService>();

   var app = builder.Build();
   

Step 5: Injecting Dependencies into Controllers
   using Microsoft.AspNetCore.Mvc;

   [ApiController]
   [Route("api/[controller]")]
   public class ProductsController : ControllerBase
   {
       private readonly IProductService _productService;

       // Constructor injection
       public ProductsController(IProductService productService)
       {
           _productService = productService;
       }

       [HttpGet]
       public IActionResult Get()
       {
           var products = _productService.GetProducts();
           return Ok(products);
       }
   }
   

Step 6: Run the Solution.

Service Lifetimes in ASP.NET Core.

In ASP.NET Core, when you register a service in the Dependency Injection (DI) container, you must specify its lifetime. This tells the framework how long to keep the object in memory and how often to create a new one.

ASP.NET Core provides three service lifetimes:
  • Singleton
  • Scoped
  • Transient
Singleton: A single instance is created once and shared throughout the application's entire lifetime.
Use When:
  • The service is stateless and thread-safe
  • You want to cache or reuse resources
  • Examples: Logging, Configuration, In-memory cache

Scoped: A new instance is created per HTTP request. The same instance is reused within that request.
Use When:
  • You want to maintain state only during a single request
  • You’re working with Entity Framework Core DbContext

Transient: A new instance is created every time the service is requested (even multiple times within the same request).
Use When:
  • Service is lightweight and stateless
  • You need fresh data or processing logic every time
Example:
builder.Services.AddSingleton<ISingletonService, MyService>();
builder.Services.AddScoped<IScopedService, MyService>();
builder.Services.AddTransient<ITransientService, MyService>();

Example: Unit Testing with DI.
var mockService = new Mock<IProductService>();
mockService.Setup(x => x.GetAll()).Returns(new[] { "Test Product" });

var controller = new ProductsController(mockService.Object);

var result = controller.GetAll();

Thanks to DI and interface-based design, testing is simple and clean.

Dependency Injection in ASP.NET Core is not just a feature — it’s a core part of the framework. By registering services properly, choosing the right lifetimes, and following interface-based design, you can build robust, maintainable, and testable web applications with ease.

Model Binding and Model Validation in ASP.NET Core

In ASP.NET Core Web API or MVC, Model Binding and Model Validation work together to handle incoming HTTP requests cleanly and securely.

What is Model Binding?

Model Binding is the process by which ASP.NET Core automatically maps incoming HTTP request data (from the query string, form data, route, headers, or body) to C# parameters or objects.

Sources of Model Binding:

Source Attribute Used (Optional) Example
Query string [FromQuery] /api/products?name=TV
Route [FromRoute] /api/products/5
Body (JSON) [FromBody] POST with JSON payload
Form data [FromForm] Used in HTML form uploads
Header [FromHeader] Custom header values

Here's a comprehensive example demonstrating all types of Model Binding in ASP.NET Core Web API — using [FromRoute], [FromQuery], [FromBody], [FromHeader], and [FromForm].

ASP.NET Core Model Binding Example:

Step 1: Model Class:
public class ProductDto
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

Step 2: Controller with All Model Binding Types
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    // 1. FromRoute Example
    // URL: /api/products/5
    [HttpGet("{id}")]
    public IActionResult GetById([FromRoute] int id)
    {
        return Ok($"FromRoute: Product ID = {id}");
    }

    // 2. FromQuery Example
    // URL: /api/products/filter?name=Laptop&price=1000
    [HttpGet("filter")]
    public IActionResult Filter([FromQuery] string name, [FromQuery] decimal price)
    {
        return Ok($"FromQuery: Name = {name}, Price = {price}");
    }

    // 3. FromBody Example
    // POST JSON: { "name": "Tablet", "price": 500 }
    [HttpPost]
    public IActionResult Create([FromBody] ProductDto product)
    {
        return Ok($"FromBody: Name = {product.Name}, Price = {product.Price}");
    }

    // 4. FromHeader Example
    // Headers: X-User-Id: 101
    [HttpGet("secure")]
    public IActionResult Secure([FromHeader(Name = "X-User-Id")] int userId)
    {
        return Ok($"FromHeader: User ID = {userId}");
    }

    // 5. FromForm Example
    // Form Data: name=Phone, price=300
    [HttpPost("upload")]
    public IActionResult Upload([FromForm] ProductDto product)
    {
        return Ok($"FromForm: Name = {product.Name}, Price = {product.Price}");
    }
}

What is Model Validation?

Model Validation ensures that the data received from the client meets the specified rules and constraints. It runs after model binding and before executing the controller action.

Common Validation Attributes:
  • [Required]: Field must not be null/empty
  • [StringLength]: Limits string length
  • [Range]: Specifies a numeric range
  • [EmailAddress]: Validates email format
  • [RegularExpression]: Pattern-based validation
Example of Model Validation:
public class Product
{
    public int Id { get; set; }

    [Required]
    [StringLength(100)]
    public string Name { get; set; }

    [Range(1, 10000)]
    public decimal Price { get; set; }
}

[HttpPost]
public IActionResult CreateProduct([FromBody] Product product)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState); // Returns validation errors
    }

    return Ok(product);
}

Flow: Model Binding + Validation

Client Request (JSON or Query) 
     
Model Binding (Maps request to C# model)
     
Model Validation (Validates using attributes)
     
Action Method Executes (if valid)

Purpose of Program.cs File in ASP.NET Core Application.

In an ASP.NET Core application, the Program.cs file is the entry point where everything begins. Whether you’re building a Web API, MVC app, or minimal API, this file is responsible for bootstrapping the application and configuring services, defining middleware, and launching the server.

Understanding the role and flow of Program.cs is essential for every ASP.NET Core developer.

What is Program.cs?

In ASP.NET Core, Program.cs contains the main method that runs when the application starts. With the newer .NET 6 and later versions (using the minimal hosting model), Program.cs is simplified and more readable.

📌 In short: Program.cs sets up everything your app needs before it starts handling requests.

Key Responsibilities of the Program.cs

  • Application Entry Point: The Program.cs file contains the Main method, which is the starting point of the application. When the application is run, this method is executed first.
  • Host Configuration: It sets up the web host, which is responsible for handling HTTP requests and managing the application's lifecycle.
  • Service Registration: It configures services that the application will use, such as dependency injection, middleware, and other services.
  • Middleware Pipeline: It defines the middleware pipeline, which processes incoming requests and outgoing responses.
  • Environment Configuration: It allows for configuration based on the environment (Development, Staging, Production).


ASP.NET Core Program.cs File:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container
builder.Services.AddControllersWithViews(); // For MVC
builder.Services.AddEndpointsApiExplorer(); // For API documentation
builder.Services.AddSwaggerGen(); // For Swagger UI

var app = builder.Build();

// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage(); // Show detailed error pages in development
}
else
{
    app.UseExceptionHandler("/Home/Error"); // Redirect to error page in production
    app.UseHsts(); // Use HTTP Strict Transport Security
}

app.UseHttpsRedirection(); // Redirect HTTP requests to HTTPS
app.UseStaticFiles(); // Serve static files (CSS, JS, images, etc.)

app.UseRouting(); // Enable routing

app.UseAuthorization(); // Enable authorization middleware

// Configure endpoints
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}"); // Default route

app.Run(); // Start the application

ASP.NET Core Program Flow

Step What Happens? Analogy
CreateBuilder Build the kitchen & staff Set up the services needed
AddServices Add chefs, waiters, and recipes Register controllers, DB, and auth
Build() Open the restaurant Create the app instance
UseMiddleware() Define the rules of service Add routing, HTTPS, and error handling
MapEndpoints() Open the door for customers Route incoming requests
Run() Start serving customers Start the web server

Interview Question Related to Program.cs File.

1. What does Program.cs do?

Program.cs is the entry point of an ASP.NET Core application. It configures everything needed before the application starts handling requests.

Responsibilities of Program.cs:
  • Build and configure the application host
  • Register services (like controllers, DbContext, etc.) into the Dependency Injection (DI) container
  • Configure the middleware pipeline to handle requests and responses
  • Set up routing, authentication, CORS, Swagger, etc.
  • Run the application
Example:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); // Add services

var app = builder.Build();
app.UseRouting();                  // Middleware
app.MapControllers();              // Endpoint mapping
app.Run();                         // Start server

2. In what order does middleware execute?

Middleware in ASP.NET Core executes in the order they are added in Program.cs.

Execution Flow:
  • Request comes in → first middleware executes
  • Each middleware does some work and optionally passes the request to the next
  • Last middleware returns a response → back through the pipeline in reverse
🧠 Key Rule:
Order matters – if UseRouting() comes after UseEndpoints(), routing won’t work!

3. How do you add a service or middleware?

Add a Service: Use builder.Services to register a service with the Dependency Injection container:
builder.Services.AddControllers();          // Built-in
builder.Services.AddScoped<IProductService, ProductService>(); // Custom service

Add Middleware: Use app.UseXyz() to insert middleware in the pipeline:
app.UseRouting();
app.UseAuthorization();

4. Where do you configure routing?

Routing is configured in Program.cs using UseRouting() and MapControllers() or MapControllerRoute().
Attribute Routing:

Enable with MapControllers():
app.UseRouting();
app.MapControllers(); // Used with [Route] attributes in controllers

Convention-Based Routing:
Enable with MapControllerRoute():
app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});

The Program.cs file is the heart of your ASP.NET Core application. It controls everything — from startup to shutdown. By learning its flow and responsibilities, you can debug better, structure your app well, and ace backend interviews.

DON'T MISS

Tech News
© all rights reserved
made with by AlgoLesson