ASP.NET Core Web API Project Setup

Setting up an ASP.NET Core Web API project correctly from the start โ€” with the right JSON serialisation, the right DI registrations, and the right project structure โ€” prevents a class of configuration bugs that only surface when Angular or other clients connect. The Web API template is a good starting point, but production APIs need additional configuration: strict JSON casing, null handling, enum serialisation, and Swagger documentation.

Web API Project Setup

// dotnet new webapi -n BlogApp.Api -o src/BlogApp.Api --no-openapi
// (we configure Swagger manually for full control)

// โ”€โ”€ Program.cs โ€” production Web API configuration โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
var builder = WebApplication.CreateBuilder(args);

// โ”€โ”€ Register controllers with JSON configuration โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
builder.Services
    .AddControllers(options =>
    {
        // Return 406 Not Acceptable for unsupported media types
        options.ReturnHttpNotAcceptable = true;
        // Add XML support (optional)
        options.RespectBrowserAcceptHeader = true;
    })
    .AddJsonOptions(options =>
    {
        // camelCase for all property names: "postId", "createdAt"
        options.JsonSerializerOptions.PropertyNamingPolicy =
            JsonNamingPolicy.CamelCase;

        // Omit null values from responses (smaller payloads)
        options.JsonSerializerOptions.DefaultIgnoreCondition =
            JsonIgnoreCondition.WhenWritingNull;

        // Serialize enums as strings: "Published" not 1
        options.JsonSerializerOptions.Converters.Add(
            new JsonStringEnumConverter());

        // Ignore circular references (for navigation properties)
        options.JsonSerializerOptions.ReferenceHandler =
            ReferenceHandler.IgnoreCycles;
    });

// โ”€โ”€ CORS for Angular client โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
builder.Services.AddCors(opts =>
    opts.AddPolicy("AllowAngular",
        p => p.WithOrigins("http://localhost:4200")
              .AllowAnyMethod()
              .AllowAnyHeader()));

// โ”€โ”€ Health checks โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
builder.Services.AddHealthChecks()
    .AddDbContextCheck<AppDbContext>("database");

var app = builder.Build();

app.UseHttpsRedirection();
app.UseCors("AllowAngular");       // before UseAuthentication
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapHealthChecks("/health");

app.Run();
Note: AddControllers() registers only the Web API components โ€” no Razor view engine, no HTML helpers, no static file support. Use AddControllersWithViews() for MVC applications or when you want both API and server-rendered HTML in the same project. Use AddControllers() for pure API projects. The difference is not just semantics โ€” AddControllersWithViews() loads the Razor compilation infrastructure (significant startup overhead and memory) that a pure API project does not need.
Tip: Configure JsonNamingPolicy.CamelCase globally in AddJsonOptions() so all JSON responses use camelCase property names that match Angular/JavaScript conventions. Without this, C# PascalCase property names (PostId, CreatedAt) appear as-is in JSON responses and Angular code must use post.PostId instead of the idiomatic post.postId. CamelCase is the standard for REST APIs consumed by JavaScript clients.
Warning: ReferenceHandler.IgnoreCycles silently drops circular references in JSON serialisation. This is usually correct (you don’t want to serialise the full navigation graph), but be aware that some data will be missing if your entity navigation properties create cycles. The alternative ReferenceHandler.Preserve uses JSON reference syntax ($id, $ref) which Angular does not understand natively. Design your DTOs to explicitly control which navigation properties are included, rather than relying on cycle handling.

Project Structure for Part 4

// โ”€โ”€ Recommended multi-project structure for Part 4 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// BlogApp/
// โ”œโ”€โ”€ src/
// โ”‚   โ”œโ”€โ”€ BlogApp.Domain/           โ† Chapter 33โ€“34: entities, value objects
// โ”‚   โ”œโ”€โ”€ BlogApp.Application/      โ† Chapter 35โ€“39: services, DTOs, interfaces
// โ”‚   โ”œโ”€โ”€ BlogApp.Infrastructure/   โ† Chapter 37โ€“38: EF Core, repositories
// โ”‚   โ””โ”€โ”€ BlogApp.Api/              โ† Chapter 33โ€“46: controllers, middleware
// โ”‚       โ”œโ”€โ”€ Controllers/
// โ”‚       โ”‚   โ”œโ”€โ”€ PostsController.cs
// โ”‚       โ”‚   โ”œโ”€โ”€ UsersController.cs
// โ”‚       โ”‚   โ””โ”€โ”€ CommentsController.cs
// โ”‚       โ”œโ”€โ”€ DTOs/                 โ† Request/response DTOs
// โ”‚       โ”œโ”€โ”€ Extensions/           โ† IServiceCollection extensions
// โ”‚       โ””โ”€โ”€ Program.cs
// โ””โ”€โ”€ tests/
//     โ”œโ”€โ”€ BlogApp.Unit.Tests/
//     โ””โ”€โ”€ BlogApp.Integration.Tests/

// โ”€โ”€ Project references โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// BlogApp.Api โ†’ BlogApp.Application โ†’ BlogApp.Domain
// BlogApp.Api โ†’ BlogApp.Infrastructure โ†’ BlogApp.Application

Common Mistakes

Mistake 1 โ€” Using AddControllersWithViews for a pure Web API (loads unused Razor engine)

โŒ Wrong โ€” loads Razor compilation infrastructure for an API that never renders HTML.

โœ… Correct โ€” use AddControllers() for Web APIs; AddControllersWithViews() only when Razor views are needed.

Mistake 2 โ€” Not configuring camelCase JSON (Angular clients need camelCase)

โŒ Wrong โ€” API returns PascalCase: { "PostId": 1, "Title": "Hello" }; Angular code awkward.

โœ… Correct โ€” configure PropertyNamingPolicy = JsonNamingPolicy.CamelCase globally.

🧠 Test Yourself

An ASP.NET Core Web API configured with JsonNamingPolicy.CamelCase serialises a C# property named AuthorId. What appears in the JSON response?