Mid-level 4 min · March 06, 2026

ASP.NET Core 502 — Auth Middleware After Endpoints

A 502 with no logs? Authentication middleware after UseEndpoints() causes NullReferenceException.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • ASP.NET Core is a cross-platform, modular web framework for .NET
  • The request pipeline is built from middleware components executed in configured order
  • Built-in dependency injection manages service lifetimes (Singleton, Scoped, Transient)
  • Minimal APIs let you write HTTP endpoints without controllers or startup ceremony
  • Performance gains come from trimming unused middleware and using async I/O throughout
Plain-English First

Imagine you run a busy restaurant. Every customer (web request) walks in through the front door, gets greeted by the host, checked for a reservation, seated by a waiter, served food, then shown the exit — in that exact order, every time. ASP.NET Core is the restaurant's operating system: it defines that pipeline of steps, lets you add or remove staff (middleware) at each stage, and makes sure every customer gets consistent, professional service. You design the menu (your app logic); ASP.NET Core handles everything else that makes the restaurant run.

Every time you hit a 'Buy Now' button on Amazon or log into your bank online, a web server somewhere processes that HTTP request in milliseconds. ASP.NET Core is Microsoft's answer to the question: 'How do we build that server-side machinery — fast, cross-platform, and scalable — with C#'? It's not just a framework; it's the backbone of millions of production applications ranging from startup APIs to Fortune 500 enterprise portals. Understanding it isn't optional for a .NET developer — it's the air you breathe.

Before ASP.NET Core, the original ASP.NET was bolted onto Windows and IIS, carrying decades of legacy baggage that made it slow to start, hard to configure, and impossible to run on Linux or Mac. ASP.NET Core was a ground-up rewrite that solved three concrete problems: it made the web pipeline composable (you only pay for what you use), it made dependency injection a first-class citizen instead of a bolt-on afterthought, and it unified MVC, Web API, and Razor Pages under one roof instead of three overlapping frameworks fighting each other.

By the end of this article you'll understand how ASP.NET Core boots up, how the middleware pipeline processes every request, how dependency injection wires your services together, and how to scaffold a real minimal API that you could extend into a production service today. You'll also know the exact mistakes that trip up developers moving from classic ASP.NET — and how to sidestep them.

The Host: How ASP.NET Core Boots

Every ASP.NET Core application starts with a host. The host is the object that owns the application's resources, lifetime, and services. There are two host types: Generic Host (default for most apps) and Web Host (legacy, still used in some templates). The host is responsible for building the dependency injection container, configuring the middleware pipeline, and running the server (Kestrel or IIS).

When you call CreateBuilder(args).Build().Run(), here's what happens under the hood: 1. The builder registers default services (logging, configuration, environment) into a ServiceCollection. 2. It loads configuration from appsettings.json, environment variables, and command-line arguments. 3. It builds the IHost instance by calling Build(). At this point, the ServiceProvider is created and all singletons are instantiated. 4. Run() starts the server, listens on configured ports, and begins processing requests.

The key thing: everything you add in ConfigureServices runs before the host is built. After Build(), you cannot modify the service collection — it's frozen. That's a common trap for devs trying to add services inside middleware.

Program.csCSHARP
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
using io.thecodeforge.Hosting;

var builder = WebHost.CreateDefaultBuilder<Startup>(args)
    .UseKestrel(options =>
    {
        options.Listen(System.Net.IPAddress.Loopback, 5000);
    });

var app = builder.Build();
await app.RunAsync();

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        // Register custom services here
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}
Output
Application starts on http://localhost:5000
Listening for incoming requests...
Mental Model: The Host as a Factory
  • ConfigureServices is the assembly line — you add all components (services) before the machine runs.
  • Build() locks the assembly line and creates the service provider.
  • Run() starts the machine. After that, you can't add or remove services without a restart.
  • If you need dynamic services, use a factory pattern or a hosted service that manages its own container.
Production Insight
A common production issue: a service registered as Singleton accidentally captures a Scoped dependency. The fix is to use IServiceScopeFactory or register the service as Scoped and either scope it manually or accept the shorter lifetime.
The host is also the place to configure server timeouts and limits. Forgetting to increase the request body size limit for file uploads is a frequent 413 error source.
Key Takeaway
The host is the single entry point that builds the service container and starts the server.
After Build(), the service collection is frozen.
Register everything in ConfigureServices — nothing after.
Choosing a Host Type
IfBuilding a web app (MVC, API, Razor Pages)
UseUse WebApplication.CreateBuilder(args) — the new minimal API approach. It simplifies startup.
IfBuilding a background service or worker
UseUse Host.CreateDefaultBuilder(args) with AddHostedService<T>.
IfNeed to support IIS in-process hosting
UseUse WebHost.CreateDefaultBuilder<Startup>(args) with UseIISIntegration().

The Middleware Pipeline: Order Matters

Middleware is the heart of ASP.NET Core request processing. Each piece of middleware decides whether to pass the request to the next piece or to short-circuit and return a response immediately. The order in which you register middleware determines the pipeline's behaviour.

The classic pattern is: logging → static files → authentication → routing → authorization → endpoints. But it's not just about ordering — each middleware can modify the request and response. A custom middleware can read the request body, add headers, or stop the pipeline entirely (like app.UseExceptionHandler).

Don't confuse middleware with services. Middleware runs per request; services are injected into middleware's constructor at app startup. If a middleware needs a scoped service, it must inject it via Invoke(HttpContext, IServiceScopedService) — not through the constructor.

io/thecodeforge/Middleware/RequestLoggingMiddleware.csCSHARP
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
37
38
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;

namespace io.thecodeforge.Middleware
{
    public class RequestLoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

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

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

            // Call the next middleware in the pipeline
            await _next(context);

            _logger.LogInformation($"Response: {context.Response.StatusCode}");
        }
    }

    // Extension method to register middleware
    public static class RequestLoggingMiddlewareExtensions
    {
        public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RequestLoggingMiddleware>();
        }
    }
}
Output
Incoming request: GET /api/products
Response: 200
Pipeline Order Is Irreversible
Once the host is built, middleware order is fixed. To change it, you must restart the application. There is no runtime dynamic ordering. Plan your middleware order at design time.
Production Insight
A real incident: the team placed app.UseCors() after app.UseAuthentication(). The CORS preflight (OPTIONS request) was rejected with 401 because the authentication middleware required Authorization header. The fix was simple: move CORS before authentication.
Another issue: a custom middleware that reads the request body twice. For example, if you want to log the request body and also pass it to the next middleware, you must enable buffering and reset the stream position. Forgetting this causes 'Cannot read after stream is closed' errors.
Key Takeaway
Middleware order is your application's execution contract.
Register exception handling first, then security, then endpoints.
When debugging pipeline issues, start by printing the middleware order or using the DeveloperExceptionPage.
Middleware Order Decision Guide
IfNeed to handle errors universally
UsePlace exception handling middleware (UseExceptionHandler) at the top of the pipeline.
IfServe static files
UsePlace UseStaticFiles early, before authentication, so that static files don't require auth.
IfNeed to add CORS
UsePlace UseCors before UseAuthentication and UseAuthorization to handle preflight without auth.
IfNeed to read request body in middleware
UseEnable request buffering (services.AddHttpContextAccessor) and reset stream position after reading.

Dependency Injection: Service Lifetimes and Scope

ASP.NET Core has a built-in dependency injection container that manages service lifetimes. There are three lifetimes: - Transient: a new instance every time it's requested. Use for lightweight, stateless services. - Scoped: one instance per HTTP request (scope). Use for services that need to share state within a single request, like DbContext. - Singleton: one instance for the entire application lifetime. Use for services that are expensive to create and hold no per-request state.

The most common mistake we see in production: a Singleton service captures a Scoped dependency. For example, a Singleton logger that needs a Scoped database context. The Singleton holds a reference to the DbContext for the first request, then reuses it for all subsequent requests — causing data corruption or stale reads. The fix: inject IServiceScopeFactory and create a new scope per operation.

Another subtle issue: multiple constructors. The container uses the constructor with the most parameters. If you have multiple constructors that are both resolvable, the container picks the greediest one. That leads to weird bugs when you add a new constructor for testing and forget to remove the old one.

io/thecodeforge/Services/OrderService.csCSHARP
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
37
38
39
40
41
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading.Tasks;

namespace io.thecodeforge.Services
{
    public interface IOrderService
    {
        Task ProcessAsync(int orderId);
    }

    public class OrderService : IOrderService
    {
        private readonly IOrderRepository _repository;
        private readonly ILogger _logger;

        // Correct: both dependencies are injected
        public OrderService(IOrderRepository repository, ILogger<OrderService> logger)
        {
            _repository = repository;
            _logger = logger;
        }

        public async Task ProcessAsync(int orderId)
        {
            _logger.LogInformation("Processing order {OrderId}", orderId);
            await _repository.UpdateStatusAsync(orderId, "Processed");
        }
    }

    // Registration in IServiceCollection
    public static class ServiceCollectionExtensions
    {
        public static IServiceCollection AddIoTheCodeForge(this IServiceCollection services)
        {
            services.AddScoped<IOrderService, OrderService>();
            services.AddScoped<IOrderRepository, OrderRepository>();
            return services;
        }
    }
}
Output
No direct output; services are registered and injected at runtime.
Mental Model: Service Container as a Smart Factory with Time
  • Singleton = one machine in the factory that runs forever.
  • Scoped = a new machine for each shift (HTTP request).
  • Transient = a new machine for every single product.
  • Never let a longer-living service hold a direct reference to a shorter-living service.
Production Insight
A production issue we encountered: a background service (IHostedService) used a Scoped DbContext directly injected into its constructor. The background service ran once and then the DbContext was disposed, causing all subsequent background loops to fail with 'Cannot access a disposed object'. The fix: inject IServiceScopeFactory and create a new scope for each iteration of the background loop.
Key Takeaway
Lifetime matters — Singleton captures Scoped = bugs.
Register your services explicitly. Let the container decide.
Use IServiceScopeFactory for background tasks.
Choosing a Service Lifetime
IfService is stateless and lightweight (e.g., utility functions)
UseTransient
IfService needs to maintain state per HTTP request (e.g., DbContext, unit of work)
UseScoped
IfService is expensive to create, thread-safe, and holds no per-request state (e.g., cache, configuration provider)
UseSingleton
IfService must be shared across multiple requests but is not thread-safe
UseDo not use Singleton. Use Scoped with a lock or consider a different architecture.

Configuration and Options Pattern

ASP.NET Core has a powerful configuration system that aggregates settings from multiple sources: appsettings.json, environment variables, Azure App Configuration, user secrets (development), and command-line arguments. The last source wins.

The Options pattern (IOptions&lt;T&gt;, IOptionsSnapshot&lt;T&gt;, IOptionsMonitor&lt;T&gt;) allows you to bind configuration sections to strongly typed POCOs. IOptions&lt;T&gt; is singleton and reads values at app startup. IOptionsSnapshot&lt;T&gt; is scoped and re-reads per request. IOptionsMonitor&lt;T&gt; is singleton but reacts to configuration changes at runtime (e.g., when using Azure App Configuration).

Common mistake: injecting IOptions&lt;T&gt; into a Scoped service expecting the configuration to change between requests. It won't — you need IOptionsSnapshot for per-request reload. Also, forgetting to call services.Configure&lt;TOptions&gt;(configuration.GetSection("SectionName")) leads to a runtime error when the Options class is resolved.

appsettings.jsonCSHARP
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
{
  "IoTheCodeForge": {
    "Database": {
      "ConnectionString": "Server=localhost;Database=forge;User Id=sa;Password=Your_password123;",
      "TimeoutSeconds": 30
    },
    "FeatureFlags": {
      "EnableNewOrderFlow": false
    }
  }
}

// Program.cs
using io.thecodeforge.Configuration;

var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<ForgeDatabaseOptions>(
    builder.Configuration.GetSection("IoTheCodeForge:Database"));

// ForgeDatabaseOptions.cs
namespace io.thecodeforge.Configuration
{
    public class ForgeDatabaseOptions
    {
        public string ConnectionString { get; set; } = string.Empty;
        public int TimeoutSeconds { get; set; } = 30;
    }
}
Output
At runtime, ForgeDatabaseOptions is populated with values from appsettings.json.
Configuration Binding Insight
The Options pattern uses recursive binding. Nested sections map to nested POCO classes. Arrays and lists work too: use '0:Key' notation in JSON or name the section with array index in environment variables (e.g., ForgeOptions:0:Name).
Production Insight
A team in production used IOptions<ConnectionStringOptions> in a Scoped DbContext factory. The connection string was read at app startup only, not per request. When they rotated the database password via a vault secret, the application continued using the old password until restarted. They replaced IOptions with IOptionsSnapshot to pick up the new password on each request (since the vault updated the config source).
Key Takeaway
Configuration is aggregated, last source wins.
Bind strongly typed options to avoid magic strings.
Use IOptionsSnapshot when you need per-request refresh.
Don't assume configuration is static in production.
Which Options Interface to Use
IfConfiguration is static and never changes
UseUse IOptions<T> (singleton).
IfConfiguration can change between requests (e.g., feature flags from external source)
UseUse IOptionsSnapshot<T> (scoped) or IOptionsMonitor<T> (singleton with changes).
IfNeed to react to live configuration changes (e.g., Azure App Configuration)
UseUse IOptionsMonitor<T> with OnChange callback.

Minimal APIs vs Controllers: When to Use Each

In .NET 6, Microsoft introduced Minimal APIs: a way to define HTTP endpoints with a simple lambda instead of creating a full controller class. Minimal APIs are perfect for small services, health checks, and prototypes. They reduce boilerplate and make the code easier to read for lightweight operations.

Controllers are still the right choice for large projects that benefit from model binding, validation attributes, filters, and Swagger generation out of the box. Controllers also support dependency injection via constructor injection, which is more familiar to most .NET devs.

The important thing is to not overuse Minimal APIs. If an endpoint needs more than a couple of dependencies, filters, or complex routing, switch to a controller. The line is blurry, but a good rule: if your endpoint lambda is over 20 lines or uses more than 3 injected services, it's time to create a controller.

Program.cs (Minimal API style)CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using io.thecodeforge.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IProductRepository, InMemoryProductRepository>();
var app = builder.Build();

app.MapGet("/api/products", async (IProductRepository repo) =>
{
    var products = await repo.GetAllAsync();
    return Results.Ok(products);
});

app.MapGet("/api/products/{id:int}", async (int id, IProductRepository repo) =>
{
    var product = await repo.GetByIdAsync(id);
    return product is null ? Results.NotFound() : Results.Ok(product);
});

app.Run();
Output
GET /api/products returns JSON array
GET /api/products/1 returns single product or 404
When to Choose Controllers Over Minimal APIs
If you need: - Multiple filters (e.g., [Authorize] + [ValidateAntiForgeryToken]) - Shared model binding via [FromForm], [FromRoute] - Complex validation with FluentValidation - Swagger/OpenAPI documentation via [ApiController] Then use a controller. Otherwise Minimal APIs are fine.
Production Insight
In a real project, the team built a large microservice entirely with Minimal APIs. After six months, they had 30 endpoints each with 40-line lambdas, duplicate validation logic, and no easy way to add a global exception filter. They refactored to controllers in two sprints and reduced code duplication by 40%. The lesson: Minimal APIs scale down, not up.
Key Takeaway
Minimal APIs are for small, focused endpoints.
Controllers are for complex, structured APIs.
Do not let a single endpoint exceed 20 lines of lambda code.
Choosing Between Minimal API and Controller
IfEndpoint is simple, few lines, one or two dependencies
UseMinimal API — keep it inline.
IfEndpoint needs filters, model binding, or complex dependency injection
UseController — use [ApiController] and business logic in separate service.
IfBuilding a new microservice or prototype
UseStart with Minimal APIs and refactor to controllers when complexity grows.
IfExisting codebase is controller-based
UseStay consistent — do not mix unless there's a compelling reason (e.g., health check endpoints).
● Production incidentPOST-MORTEMseverity: high

The Silent 502: Authentication Middleware Placed After Endpoint Routing

Symptom
Every request to a protected endpoint returns HTTP 502. No error logs appear except a generic 'An unhandled exception occurred'. Local development works fine with debugger attached.
Assumption
The team assumed that adding the authentication middleware anywhere in Configure() would work because 'ASP.NET Core is smart enough to figure it out'.
Root cause
The authentication middleware was registered after app.UseEndpoints(). When a request hits the endpoint middleware, it short-circuits the pipeline if the endpoint is matched. The authentication middleware never ran, but the endpoint handler tried to access the User object that was never populated. This threw a NullReferenceException that bubbled up as a 502.
Fix
Move app.UseAuthentication() and app.UseAuthorization() before app.UseEndpoints(). The correct order is: routing → authentication → authorization → endpoints. Also add a global exception handler middleware to catch unhandled exceptions and return a proper 500 with diagnostic details.
Key lesson
  • Middleware order in ASP.NET Core is deterministic and critical — it's not 'smart'.
  • Always place authentication and authorization middleware after UseRouting() and before UseEndpoints().
  • When you see a 502 with no clear logs, suspect an unhandled exception in the pipeline that kills the response before logging.
Production debug guideSymptom → Action pairs for common middleware misconfiguration4 entries
Symptom · 01
Endpoint returns 404 even though route is defined
Fix
Check that app.UseRouting() is called before app.UseEndpoints(). Also verify that UseEndpoints maps the correct route pattern.
Symptom · 02
Static files return 404 in production but work locally
Fix
Confirm that app.UseStaticFiles() is placed before app.UseRouting(). Static files middleware short-circuits if it finds a match, so it must come early.
Symptom · 03
CORS errors in browser, preflight failing
Fix
Ensure app.UseCors() is called before app.UseRouting(). If you have authentication middleware, CORS must run before it to handle OPTIONS requests without authentication.
Symptom · 04
Exception details shown in browser (HTTP 500 with stack trace)
Fix
Wrap the entire pipeline in a try-catch or use app.UseExceptionHandler() at the top of Configure(). Never expose exception details in production — use ILogger and return a generic error response.
★ Quick Debug Cheat Sheet for ASP.NET Core PipelineThree commands and one fix for the most common pipeline failures in production.
Middleware not executing
Immediate action
Check the order in Program.cs/Startup.Configure(). Middleware runs in registration order.
Commands
dotnet run --environment Development
dotnet add package Microsoft.AspNetCore.Diagnostics --version 6.0.0
Fix now
Add app.UseDeveloperExceptionPage() inside if (env.IsDevelopment()) to see the full pipeline execution.
Dependency injection throws InvalidOperationException+
Immediate action
Check that the service is registered in ConfigureServices/AddServices.
Commands
dotnet build
IServiceCollection extension method: services.AddTransient<IMyService, MyService>()
Fix now
Add the registration and check for circular dependencies by reviewing constructor parameters.
Minimal API endpoint returns 400 Bad Request+
Immediate action
Inspect the request body against the expected model.
Commands
curl -v -X POST https://localhost:5001/api/endpoint -H 'Content-Type: application/json' -d '{}'
Check validation attributes on the parameter model.
Fix now
Add fluent validation or manual model state checks inside the endpoint handler.
ASP.NET Core vs Classic ASP.NET
FeatureASP.NET CoreClassic ASP.NET
PlatformCross-platform (Windows, Linux, macOS)Windows only (IIS)
Dependency InjectionBuilt-in container, first-class citizenNo built-in; required third-party (Unity, Autofac)
PipelineModular middleware; order controlled by developerFixed HTTP module pipeline (global.asax)
Performance~10x faster requests per second than classicSlower due to System.Web dependency
ConfigurationJSON, environment variables, command lineWeb.config XML, appSettings only
HostingKestrel (self-host), IIS, HTTP.sysIIS only

Key takeaways

1
ASP.NET Core is a cross-platform, modular web framework that gives you full control over the request pipeline.
2
Middleware order is critical
error handling first, routing after static files, authentication before endpoints.
3
Dependency injection is built-in and governs service lifetimes. Captive dependencies cause subtle production bugs.
4
Configuration is aggregated and supports runtime reload. Use the correct Options interface for your needs.
5
Minimal APIs are for simple endpoints; controllers are for complex structured APIs. Choose based on code clarity and maintainability.

Common mistakes to avoid

5 patterns
×

Using depends_on without a healthcheck in Docker Compose

Symptom
API crashes on startup with ECONNREFUSED because the database container started but is not yet ready to accept connections.
Fix
Add a healthcheck block to the database service using pg_isready, then use condition: service_healthy in the API depends_on block.
×

Registering services after Build()

Symptom
InvalidOperationException: 'Cannot resolve scoped service from root provider' or 'Collection was modified' during first request.
Fix
Move all service registrations into ConfigureServices (or builder.Services). Never manipulate IServiceCollection after Build() is called.
×

Using IOptions in a Singleton service expecting runtime changes

Symptom
Configuration changes are not picked up even after external updates; service continues using stale values.
Fix
Use IOptionsSnapshot (scoped) or IOptionsMonitor (singleton reactive). For background tasks, inject IServiceScopeFactory and resolve IOptionsSnapshot inside each scope.
×

Middleware ordering: exception handling placed at the bottom of pipeline

Symptom
Exceptions thrown in middleware that run after the exception handler are not caught; user receives blank 500 or full stack trace in production.
Fix
Place app.UseExceptionHandler (or UseDeveloperExceptionPage in dev) as the very first middleware in the pipeline.
×

Not disposing of scoped services in background tasks

Symptom
Memory leak or 'Cannot access a disposed object' exception when background service runs multiple iterations.
Fix
Create and dispose a new scope within each iteration using IServiceScopeFactory. Do not inject scoped services directly into the IHostedService constructor.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain the middleware pipeline in ASP.NET Core. How does the order of m...
Q02JUNIOR
What are the three service lifetimes in ASP.NET Core DI? Give a real pro...
Q03SENIOR
How would you handle configuration changes at runtime without restarting...
Q04SENIOR
Compare Minimal APIs with Controller-based APIs. When would you choose o...
Q01 of 04SENIOR

Explain the middleware pipeline in ASP.NET Core. How does the order of middleware affect the response?

ANSWER
The middleware pipeline is a series of delegates that handle HTTP requests and responses. Each middleware can inspect, modify, or short-circuit the request/response by either calling the next delegate or not. The order is determined by the order you add them in Configure() (or program.cs). If you place authentication after endpoints, authentication never runs. The typical order: Exception → Static Files → Routing → Authentication → Authorization → Endpoints. Order is fixed after app start.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is ASP.NET Core and how is it different from classic ASP.NET?
02
Why does my middleware not execute?
03
How do I read the request body in middleware without breaking downstream middleware?
04
Can I change the middleware order at runtime?
05
What is the correct way to use IOptionsSnapshot in a background service?
🔥

That's ASP.NET. Mark it forged?

4 min read · try the examples if you haven't

Previous
ValueTask in C#
1 / 14 · ASP.NET
Next
REST API with ASP.NET Core