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();
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.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.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.