ASP.NET Core Tutorial for Beginners: Build Your First Web App

ASP.NET Tutorial for Beginners

Quick answer: ASP.NET Core is Microsoft’s free, open-source, cross-platform framework for building web apps and APIs in C#. To get started, install the .NET SDK, create a project with dotnet new webapp, run it with dotnet run, and then learn the building blocks in this order: Program.cs, middleware, routing, dependency injection, configuration, data access, security, testing and deployment.

This ASP.NET Core tutorial is written for developers who know a little C# and want a practical path from an empty folder to a deployable web app. Every step uses the .NET CLI, so it works the same on Windows, macOS and Linux, and every code sample is small enough to type and run yourself.

What Is ASP.NET Core?

ASP.NET Core is the modern web framework in the .NET platform. One framework covers server-rendered pages (Razor Pages and MVC), HTTP APIs (controllers and Minimal APIs), real-time apps (SignalR) and interactive UI (Blazor). It runs on the Kestrel web server, can sit behind IIS, Nginx or a cloud load balancer, and is deployed as an ordinary .NET application.

If you have seen older tutorials for “ASP.NET” (Web Forms or ASP.NET MVC 5), those target the Windows-only .NET Framework. They still exist in maintenance mode, but new projects should use ASP.NET Core.

ASP.NET (.NET Framework)ASP.NET Core
PlatformsWindows onlyWindows, Linux, macOS
HostingIISKestrel, IIS, Nginx, containers, cloud
Configurationweb.configappsettings.json, environment variables, secrets
Dependency injectionThird-party containersBuilt in
StatusMaintenanceActively developed

What You Need Before Starting

  • Basic C#: classes, methods, properties, async/await and generic types such as List<T>.
  • The .NET SDK. Pick the current LTS (long-term support) release unless you have a reason to use the newest STS release.
  • An editor: Visual Studio (Windows), Visual Studio Code with the C# Dev Kit extension, or JetBrains Rider.
  • A terminal. Every command below is run from the project folder.

Check that the SDK is installed and see which versions you have:

dotnet --version
dotnet --list-sdks

If dotnet is not recognised, close and reopen the terminal so the updated PATH is picked up.

Create and Run Your First App

The webapp template creates a Razor Pages project, which is the easiest starting point for page-based sites:

dotnet new webapp -n FirstWebApp
cd FirstWebApp
dotnet run

The console prints a local address such as https://localhost:7xxx. Open it in your browser to see the starter site. If the browser warns about the certificate, trust the development certificate once:

dotnet dev-certs https --trust

While you are learning, use dotnet watch instead of dotnet run. It rebuilds and reloads the app every time you save a file.

Understand the Project Structure

File or folderWhat it is for
Program.csEntry point. Registers services and builds the request pipeline.
Pages/Razor Pages: a .cshtml view plus a .cshtml.cs page model.
wwwroot/Static files (CSS, JavaScript, images) served directly.
appsettings.jsonConfiguration. appsettings.Development.json overrides it locally.
Properties/launchSettings.jsonLocal run profiles, ports and the ASPNETCORE_ENVIRONMENT value.
FirstWebApp.csprojTarget framework and NuGet package references.

Program.cs and the Request Pipeline

Modern templates put the whole startup in Program.cs. It has two halves: services are registered on builder.Services, then the middleware pipeline is configured on app. A trimmed version of the Razor Pages template looks like this:

var builder = WebApplication.CreateBuilder(args);

// 1. Register services (dependency injection)
builder.Services.AddRazorPages();

var app = builder.Build();

// 2. Configure the middleware pipeline (order matters)
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();

app.MapRazorPages();

app.Run();

Each Use... call adds a middleware component. A request passes through them top to bottom and the response travels back up. That is why order matters: the exception handler must come first so it can catch errors from everything after it, and UseAuthentication() must come before UseAuthorization() once you add login. Microsoft’s middleware documentation lists the recommended order.

You can write your own middleware inline to see the pipeline in action:

app.Use(async (context, next) =>
{
    var started = DateTime.UtcNow;
    await next(context);
    var ms = (DateTime.UtcNow - started).TotalMilliseconds;
    app.Logger.LogInformation("{Path} took {Ms} ms", context.Request.Path, ms);
});

Razor Pages, MVC or Minimal APIs?

OptionBest forHow routes are defined
Razor PagesPage-focused sites, forms, admin panelsFile location under Pages/
MVC (controllers + views)Larger apps with many related actions per featureController and action names or attributes
Web API controllersJSON APIs with filters, versioning and many endpoints[Route] and [HttpGet] attributes
Minimal APIsSmall, fast APIs and microservicesapp.MapGet(), app.MapPost()

They are not exclusive. One app can serve Razor Pages for the UI and Minimal APIs for JSON endpoints. Here is a complete Minimal API endpoint added to Program.cs:

app.MapGet("/api/time", () => new { utc = DateTime.UtcNow });

app.MapGet("/api/hello/{name}", (string name) =>
    Results.Ok(new { message = $"Hello, {name}!" }));

Add Your First Razor Page

Create Pages/Hello.cshtml. The @page directive makes it reachable at /Hello:

@page
@model HelloModel

<h1>Hello, @Model.Name!</h1>
<form method="get">
    <input name="name" placeholder="Your name" />
    <button type="submit">Greet me</button>
</form>

Then create the page model in Pages/Hello.cshtml.cs:

using Microsoft.AspNetCore.Mvc.RazorPages;

public class HelloModel : PageModel
{
    public string Name { get; private set; } = "Developer";

    public void OnGet(string? name)
    {
        if (!string.IsNullOrWhiteSpace(name))
        {
            Name = name;
        }
    }
}

Submit the form with your name. Razor HTML-encodes @Model.Name automatically, so a value like <script> is displayed as text instead of running. That built-in encoding is one of the reasons Razor views are safe by default.

Dependency Injection and Service Lifetimes

ASP.NET Core has a built-in dependency injection (DI) container. You register a service once and the framework passes it to any page, controller or endpoint that asks for it.

public interface IGreetingService
{
    string Greet(string name);
}

public class GreetingService : IGreetingService
{
    public string Greet(string name) => $"Welcome, {name}!";
}

// Program.cs
builder.Services.AddScoped<IGreetingService, GreetingService>();

// Minimal API: the service is injected as a parameter
app.MapGet("/api/greet/{name}", (string name, IGreetingService svc) => svc.Greet(name));
LifetimeOne instance per…Typical use
AddSingletonapplicationCaches, configuration readers, thread-safe helpers
AddScopedHTTP requestBusiness services, DbContext
AddTransientinjectionLightweight, stateless helpers

A common bug is injecting a scoped service such as a DbContext into a singleton. In the Development environment ASP.NET Core validates scopes and throws an exception at startup when it detects this, which is a good reason to keep running locally in Development.

Configuration, Environments and Secrets

Configuration is read from appsettings.json, then appsettings.{Environment}.json, then user secrets (Development only), then environment variables and command-line arguments. Later sources override earlier ones.

// appsettings.json
{
  "Site": {
    "Name": "First Web App",
    "PageSize": 10
  }
}
public class SiteOptions
{
    public string Name { get; set; } = "";
    public int PageSize { get; set; }
}

// Program.cs
builder.Services.Configure<SiteOptions>(builder.Configuration.GetSection("Site"));

Inject IOptions<SiteOptions> wherever you need the values. Never commit passwords or API keys to appsettings.json. For local development, use the Secret Manager:

dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:Default" "Server=.;Database=FirstWebApp;Trusted_Connection=True;TrustServerCertificate=True"

In production, supply secrets through environment variables or a vault service such as Azure Key Vault.

Model Binding and Validation

Model binding maps form fields, route values, query strings and JSON bodies to C# objects. Data annotations describe the rules:

using System.ComponentModel.DataAnnotations;

public class ContactInput
{
    [Required, StringLength(80)]
    public string Name { get; set; } = "";

    [Required, EmailAddress]
    public string Email { get; set; } = "";
}

public class ContactModel : PageModel
{
    [BindProperty]
    public ContactInput Input { get; set; } = new();

    public IActionResult OnPost()
    {
        if (!ModelState.IsValid)
        {
            return Page(); // redisplay the form with error messages
        }

        // save or send the message here
        return RedirectToPage("Thanks");
    }
}

Always check ModelState.IsValid on the server. Client-side validation improves the experience, but it can be bypassed. Razor Pages forms also include an anti-forgery token by default, which protects POST requests against cross-site request forgery.

Database Access with Entity Framework Core

Entity Framework Core (EF Core) is the standard data-access library. For SQL Server, add the provider and the design-time tools:

dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet tool install --global dotnet-ef
using Microsoft.EntityFrameworkCore;

public class Note
{
    public int Id { get; set; }
    public string Title { get; set; } = "";
    public DateTime CreatedUtc { get; set; } = DateTime.UtcNow;
}

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    public DbSet<Note> Notes => Set<Note>();
}

// Program.cs
builder.Services.AddDbContext<AppDbContext>(o =>
    o.UseSqlServer(builder.Configuration.GetConnectionString("Default")));

Create the database from your model with migrations:

dotnet ef migrations add InitialCreate
dotnet ef database update

Query asynchronously so request threads are not blocked while the database works:

app.MapGet("/api/notes", async (AppDbContext db) =>
    await db.Notes.OrderByDescending(n => n.CreatedUtc).Take(20).ToListAsync());

If you prefer SQL Server stored procedures or hand-written SQL, Dapper is a popular lightweight alternative, and EF Core itself can run raw SQL through FromSql.

Authentication, Authorization and Security Basics

Authentication answers “who are you?” and authorization answers “what are you allowed to do?”. For a site with its own user accounts, ASP.NET Core Identity provides registration, login, password hashing and lockout. For company apps, you can sign in with Microsoft Entra ID or another OpenID Connect provider instead.

// Program.cs (after adding an authentication scheme)
app.UseAuthentication();
app.UseAuthorization();

// Require login for a Minimal API endpoint
app.MapGet("/api/admin/report", () => "secret report").RequireAuthorization();

Security habits worth building from day one:

  • Keep HTTPS redirection and HSTS enabled outside Development.
  • Validate input on the server and use parameterised queries (EF Core does this for you).
  • Never log passwords, tokens or full payment details.
  • Return generic error pages in production. Detailed exception pages belong only in Development.
  • Keep the SDK and NuGet packages updated, since security fixes ship through regular servicing releases.

The official ASP.NET Core security documentation covers each of these topics in depth. If your forms attract spam bots, our guide to adding DNTCaptcha.Core to an ASP.NET Core form shows one way to add a CAPTCHA.

Logging and Error Handling

ILogger<T> is available everywhere through DI. Use structured placeholders instead of string concatenation so log tools can filter by value:

public class OrdersModel : PageModel
{
    private readonly ILogger<OrdersModel> _logger;
    public OrdersModel(ILogger<OrdersModel> logger) => _logger = logger;

    public void OnGet(int id)
    {
        _logger.LogInformation("Order {OrderId} viewed", id);
    }
}

Set log levels per category in appsettings.json. In production, send logs to a central store such as Application Insights, Seq or an OpenTelemetry collector so you can search them after a failure.

Testing Your App

Use unit tests for business logic and integration tests for the full HTTP pipeline. The Microsoft.AspNetCore.Mvc.Testing package starts your app in memory:

dotnet new xunit -n FirstWebApp.Tests
cd FirstWebApp.Tests
dotnet add reference ../FirstWebApp/FirstWebApp.csproj
dotnet add package Microsoft.AspNetCore.Mvc.Testing
using Microsoft.AspNetCore.Mvc.Testing;

public class HomePageTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly HttpClient _client;

    public HomePageTests(WebApplicationFactory<Program> factory)
        => _client = factory.CreateClient();

    [Fact]
    public async Task HomePage_ReturnsSuccess()
    {
        var response = await _client.GetAsync("/");
        response.EnsureSuccessStatusCode();
    }
}

If the test project reports that Program is inaccessible (top-level statements can generate an internal class), add public partial class Program { } at the end of Program.cs. Microsoft’s integration testing guide explains more options, such as replacing the database with a test instance.

Publish and Deploy

Create a release build in a folder:

dotnet publish -c Release -o ./publish

Common hosting choices:

  • IIS on Windows Server: install the ASP.NET Core Hosting Bundle, then point an IIS site at the publish folder.
  • Linux with Nginx: run the app as a systemd service on Kestrel and use Nginx as a reverse proxy.
  • Containers: recent SDKs can build an image directly with dotnet publish /t:PublishContainer, or you can write your own Dockerfile.
  • Managed cloud: Azure App Service and similar platforms deploy straight from the publish output or a CI pipeline.

Before going live, set ASPNETCORE_ENVIRONMENT to Production, move secrets out of files, add a health-check endpoint with builder.Services.AddHealthChecks() and app.MapHealthChecks("/health"), and confirm that logging reaches a place you can read.

Common Beginner Mistakes

  • Putting middleware in the wrong order, for example calling UseAuthorization() before UseRouting().
  • Blocking on async code with .Result or .Wait() instead of using await.
  • Registering a DbContext as a singleton or capturing it in a background thread.
  • Committing connection strings or API keys to source control.
  • Showing the developer exception page in production.
  • Trying to learn Blazor, microservices and cloud deployment before the request pipeline makes sense.

A Four-Week Practice Plan

WeekFocusBuild
1CLI, project structure, Razor Pages, routingA three-page site with a contact form and validation
2Dependency injection, configuration, loggingMove business logic into services and read settings from configuration
3EF Core and Minimal APIsA notes app with CRUD pages and a JSON API
4Identity, testing, publishingAdd login, write integration tests and deploy to a test server

Once these basics feel comfortable, explore the wider programming and development guide. If your apps need to move data between systems, our comparison of ETL tools for data pipelines and our overview of Microsoft BizTalk Server cover integration options in the Microsoft ecosystem.

Frequently Asked Questions

Is ASP.NET Core good for beginners?

Yes, as long as you already know basic C#. The templates run immediately, the CLI is the same on every operating system, and the official documentation is detailed. Start with Razor Pages because it keeps each page’s HTML and logic close together.

Do I need to learn C# first?

You need the fundamentals: types, classes, methods, collections, LINQ basics and async/await. You do not need advanced C# features before building your first app.

Can ASP.NET Core run on Linux?

Yes. ASP.NET Core is cross-platform. Many production apps run on Linux servers or in Linux containers behind Nginx or a cloud load balancer.

Should I use Visual Studio or Visual Studio Code?

Both work. Visual Studio on Windows has the richest designers and debugging tools. Visual Studio Code with the C# Dev Kit is lighter and works on every platform. Rider is a strong paid alternative.

Which .NET version should I use?

Use the current LTS release for production apps, because each LTS release is supported for three years. Check the official download page for the latest version and its support dates.

Leave A Comment

Scroll to top