Your friendly guide to ASP.NET Web API
1) How to create a Web API in ASP.NET C#?
Short path: dotnet new webapi → edit → run.
Now the full, practical steps (CLI + Visual Studio), examples for minimal API and controller-based API, and tips.
Quick start (CLI)
bash
# create project
dotnet new webapi -n MyApi
cd MyApi
# run (development)
dotnet run
# default: https://localhost:5001
The template includes a sample WeatherForecast controller and Swagger out of the box.
csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
app.UseSwagger(); app.UseSwaggerUI();
app.MapGet("/", () => "Hello from Minimal API!");
app.MapGet("/api/hello/{name}", (string name) => Results.Ok(new { Message = $"Hello {name}" }));
app.Run();
- Minimal APIs are great for tiny services or learning.
Controller-based API (more structured)
Create Controllers/TodoController.cs:
csharp
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class TodoController : ControllerBase
{
private static readonly List<string> _items = new() { "Sample task" };
[HttpGet]
public IActionResult Get() => Ok(_items);
[HttpPost]
public IActionResult Create([FromBody] string item)
{
_items.Add(item);
return CreatedAtAction(nameof(Get), item);
}
}
In Program.cs ensure you have:
csharp
builder.Services.AddControllers();
app.MapControllers();
Practical tips & best practices
- Use async actions for I/O (
Task<IActionResult>
). - Use DTOs rather than raw domain models for public APIs.
- Keep controllers thin — put business logic in services and inject via DI.
- Add Swagger early for exploration (
Swashbuckle.AspNetCore
).
2) How to configure Swagger in ASP.NET Core Web API?
Swagger (Swashbuckle) documents your API and provides an interactive UI.
Install
bash
dotnet add package Swashbuckle.AspNetCore
Basic configuration (Program.cs)
csharp
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "MyApi", Version = "v1" });
// Optional: include XML comments
// var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
// c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFile));
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(c => {
c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyApi v1");
c.RoutePrefix = "docs"; // serve at /docs
});
}
Enable XML comments (helpful)
- In project file (.csproj) add:
xml
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
- Enable the IncludeXmlComments shown above.
Add JWT auth UI to Swagger
csharp
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme {
Description = "JWT Auth header: 'Bearer {token}'",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } },
new string[] {}
}
});
Customize UI
- Change RoutePrefix, inject custom CSS/JS by providing an IndexStream, or hide models with DefaultModelsExpandDepth
(-1)
.
3) How to return JSON in ASP.NET Core Web API?
ASP.NET Core returns JSON by default for POCO objects. You control serialization via System.Text.Json or Newtonsoft.Json.
Basic patterns
csharp
[HttpGet]
public ActionResult<MyDto> Get() => new MyDto { Id = 1, Name = "x" };
[HttpGet("raw")]
public IActionResult Raw() => Ok(new { success = true, data = new[] { 1,2,3 } });
- Ok(obj) sets status 200 and serializes obj to JSON (default application/json).
Using ActionResult<T>
ActionResult<T> lets you return typed responses and various status codes:
csharp
public async Task<ActionResult<TodoDto>> Get(int id)
{
var item = await _repo.FindAsync(id);
if (item == null) return NotFound();
return item; // automatically 200 with typed body
}
System.Text.Json customization
In Program.cs:
csharp
builder.Services.AddControllers()
.AddJsonOptions(opts => {
opts.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
});
Use Newtonsoft.Json if needed
bash
dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson
csharp
builder.Services.AddControllers().AddNewtonsoftJson(options => {
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
Dates & timezone caution
- Prefer ISO 8601; be explicit about UTC conversions.
- Avoid serializing DateTime without Kind information — use DateTimeOffset if possible.
4) What is ASP.NET Web API?
Short conceptual answer: ASP.NET Web API is a framework for building HTTP services that can be consumed by browsers, mobile apps, and other services. In modern .NET it’s part of ASP.NET Core.
Key features
- HTTP-first: routes map to HTTP verbs and URIs.
- Content negotiation: automatically selects JSON/XML formatter based on
Accept
header. - Model binding & validation: bind route/query/body to .NET types and validate with attributes.
- Filters & middleware: cross-cutting concerns (auth, logging, caching).
- Extensible: plug in custom formatters, filters, DI services, OpenAPI docs, etc.
Historically “Web API” referred to the separate ASP.NET Web API (pre-Core). Today you build web APIs with ASP.NET Core.
5) How to create Web API in ASP.NET C# with database?
We’ll do an end-to-end example using EF Core, with model → DbContext → DI → controller → migrations.
Step 0 — create project
bash
dotnet new webapi -n MyApiWithDb
cd MyApiWithDb
Step 1 — add EF Core packages
bash
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer # or Npgsql for Postgres
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet tool install --global dotnet-ef # if not installed
Step 2 — add model & DbContext
Models/TodoItem.cs
csharp
public class TodoItem
{
public int Id { get; set; }
public string Title { get; set; } = "";
public bool Done { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
Data/AppDbContext.cs
csharp
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {}
public DbSet<TodoItem> Todos { get; set; }
}
Step 3 — add connection string & register DbContext
appsettings.json
:
json
"ConnectionStrings": {
"DefaultConnection": "Server=.;Database=MyApiDb;Trusted_Connection=True;"
}
Program.cs
:
csharp
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builde.Services.AddControllers();
Step 4 — scaffold controller using DI
Controllers/TodoController.cs
csharp
[ApiController]
[Route("api/[controller]")]
public class TodoController : ControllerBase
{
private readonly AppDbContext _db;
public TodoController(AppDbContext db) => _db = db;
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItem>>> Get() =>
await _db.Todos.ToListAsync();
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> Get(int id)
{
var item = await _db.Todos.FindAsync(id);
return item == null ? NotFound() : Ok(item);
}
[HttpPost]
public async Task<ActionResult<TodoItem>> Create(TodoItem input)
{
_db.Todos.Add(input);
await _db.SaveChangesAsync();
return CreatedAtAction(nameof(Get), new { id = input.Id }, input);
}
}
Step 5 — migrations & database update
bash
dotnet ef migrations add InitialCreate
dotnet ef database update
Best practices
- Use DTOs for public API to avoid exposing internal schema.
- Use AsNoTracking
()
for read queries where no change tracking needed. - Centralize data access via repository/service layer if complexity grows.
- Use transactions for multi-step writes.
6) How to send email in ASP.NET Web API?
Use MailKit (modern, secure, async). Avoid System.Net.Mail.SmtpClient for new development.
Install MailKit
bash
dotnet add package MailKit
Example service
Services/EmailService.cs
:
csharp
using MailKit.Net.Smtp;
using MimeKit;
public class EmailService
{
private readonly IConfiguration _config;
public EmailService(IConfiguration config) => _config = config;
public async Task SendEmailAsync(string to, string subject, string bodyHtml)
{
var email = new MimeMessage();
email.From.Add(MailboxAddress.Parse(_config["Email:From"]));
email.To.Add(MailboxAddress.Parse(to));
email.Subject = subject;
email.Body = new TextPart("html") { Text = bodyHtml };
using var smtp = new SmtpClient();
await smtp.ConnectAsync(_config["Email:SmtpHost"], int.Parse(_config["Email:SmtpPort"]), MailKit.Security.SecureSocketOptions.StartTls);
if (!string.IsNullOrEmpty(_config["Email:Username"]))
await smtp.AuthenticateAsync(_config["Email:Username"], _config["Email:Password"]);
await smtp.SendAsync(email);
await smtp.DisconnectAsync(true);
}
}
Register in DI:
csharp
builder.Services.AddTransient<EmailService>();
Configuration (appsettings.json):
json
"Email": {
"From": "[email protected]",
"SmtpHost": "smtp.yourhost.com",
"SmtpPort": "587",
"Username": "smtp-user",
"Password": "smtp-password"
}
Security & production notes
- Never store SMTP credentials in source control. Use environment variables or Secret Manager (dotnet user-secrets) in development and a secrets store (KeyVault) in production.
- For Gmail/Workspace use OAuth2 or App Passwords (2FA requirement).
- Send emails asynchronously (non-blocking) and protect against injection (sanitize inputs if you build subject/body from user input).
7) How to deploy ASP.NET Core Web API in IIS? — step by step
This is a common pain point — follow these steps exactly.
Prerequisites on server
- Windows Server with IIS installed.
- Install the .NET Core Hosting Bundle (matching your runtime: .NET 6/7/8). This installs the ASP.NET Core Module (ANCM) that proxies IIS → Kestrel.
- Create a Windows user for the App Pool (optional but recommended).
Publishing from dev
From command line:
bash
dotnet publish -c Release -o ./publish
Or in Visual Studio: Publish → Folder Profile → Pick target folder.
Copy files to the server
- Copy contents of the publish folder to the server path (e.g., C:\inetpub\wwwroot\MyApi).
IIS site configuration
- Open IIS Manager → Sites → Add Website.
- Site name: MyApi.
- Physical path: C:\inetpub\wwwroot\MyApi.
- Binding: choose hostname / port; for production use HTTPS (bind certificate).
- Application Pool:
- Create a new App Pool or use existing.
- Managed pipeline mode: Integrated.
- .NET CLR version: No Managed Code (Kestrel handles .NET runtime).
- Set identity to your service account (if you need network resources).
- Ensure web.config exists in publish folder (it’s generated by the SDK). This configures the ASP.NET Core Module.
- Give the App Pool identity Read/Write access to the folder if logs or uploads are used.
Environment variables & connection strings
- Set ASPNETCORE_ENVIRONMENT to
Production
for production behaviour. - Configure connection strings and secrets in IIS Configuration Editor or use environment variables. Avoid appsettings with secrets checked into source.
Logs & troubleshooting
- If the site fails, check:
- Windows Event Viewer (Application logs) for ANCM errors.
- stdoutLog (enable temporarily in web.config by setting stdoutLogEnabled=true and stdoutLogFile path).
- Ensure Hosting Bundle installed and App Pool is set to No Managed Code.
HTTPS & certificates
- Bind an SSL certificate in IIS (Bindings → Add → Type = https).
- Use HSTS and redirect HTTP → HTTPS in your app via app.UseHttpsRedirection
()
.
8) How to call Web API POST method from ASP.NET C#?
Use HttpClient. Prefer IHttpClientFactory (built-in) to avoid socket exhaustion and to support DI and resilience policies.
Register IHttpClientFactory
csharp
builder.Services.AddHttpClient("MyApi", c => {
c.BaseAddress = new Uri("https://api.example.com/");
c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
});
Usage example (consume POST)
public class MyService
{
private readonly IHttpClientFactory _httpFactory;
public MyService(IHttpClientFactory httpFactory) => _httpFactory = httpFactory;
public async Task<TodoItem?> CreateTodoAsync(TodoCreateDto dto, CancellationToken ct = default)
{
var client = _httpFactory.CreateClient("MyApi");
var response = await client.PostAsJsonAsync("api/todo", dto, ct);
if (!response.IsSuccessStatusCode)
{
// read error, log, throw / return null
var error = await response.Content.ReadAsStringAsync();
throw new Exception($"API Error: {response.StatusCode} - {error}");
}
return await response.Content.ReadFromJsonAsync<TodoItem>(cancellationToken: ct);
}
}
Notes
- Use PostAsJsonAsync and ReadFromJsonAsync (System.Net.Http.Json) for convenience.
- Include CancellationToken for timeouts.
- Configure timeouts on
HttpClient
or use policies (Polly) for retries/backoff.
9) How to integrate Web API in ASP.NET MVC?
Two common integration scenarios:
A — Add Web API endpoints inside an existing ASP.NET MVC project
- In the same project add API controllers (
[ApiController]
) and register controllers:
csharp
builder.Services.AddControllersWithViews(); // supports both MVC & API
app.MapControllerRoute("default","{controller=Home}/{action=Index}/{id?}");
app.MapControllers(); // API endpoints
B — Consume an external Web API from MVC (server-side)
In your MVC controller:
csharp
public class HomeController : Controller
{
private readonly IHttpClientFactory _httpFactory;
public HomeController(IHttpClientFactory httpFactory) => _httpFactory = httpFactory;
public async Task<IActionResult> Index()
{
var client = _httpFactory.CreateClient("MyApi");
var items = await client.GetFromJsonAsync<List<TodoItem>>("api/todo");
return View(items);
}
}
- Render data in Razor views using model binding.
Best practice
- Don’t block threads in MVC — use async all the way.
- Cache API responses where appropriate (MemoryCache / Redis).
- Handle API failures gracefully when rendering views (fallback UI, friendly messages)
10) How to add migration in ASP.NET Core Web API?
This describes creating EF Core migrations and common gotchas.
Install tools
bash
dotnet tool install --global dotnet-ef # if not installed
dotnet add package Microsoft.EntityFrameworkCore.Design
Typical workflow
- Create/modify DbContext and models.
- Add migration:
bash
dotnet ef migrations add AddTodos
3. Apply migration:
bash
dotnet ef database update
Using Package Manager Console (Visual Studio)
ps
Add-Migration AddTodos
Update-Database
If you get “No executable found” or design-time errors
- Ensure your startup project is the project that contains Program.cs or set —startup–project:
bash
dotnet ef migrations add AddTodos --startup-project ../MyApi
- If EF cannot create your
DbContext
at design time, implement IDesignTimeDbContextFactory<TContext>:
csharp
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args)
{
var options = new DbContextOptionsBuilder<AppDbContext>();
options.UseSqlServer("Server=.;Database=MyApiDb;Trusted_Connection=True;");
return new AppDbContext(options.Options);
}
}
This helps tooling create the context without running your app code.
Best practices
- Keep migrations small and focused.
- Test migrations on a staging DB before applying to production.
- Commit migrations to source control.
- Use scripted deployments with transactional migration steps where possible.