DEV Community

Cover image for Mastering .NET 8 Web API: From Setup to Security - 50 Tips Guide for Developers
Sukhpinder Singh
Sukhpinder Singh

Posted on • Originally published at singhsukhpinder.Medium

Mastering .NET 8 Web API: From Setup to Security - 50 Tips Guide for Developers

From the initial project setup using the .NET CLI to configuring middleware, controllers, and services, learn every step to build a robust API. Discover best practices for dependency injection, asynchronous actions, and handling exceptions to create scalable, efficient web applications.

1. Setting Up a .NET 8 Web API Project

Concept

Use the .NET CLI to create a new Web API project. This sets up a basic project structure including Program.cs for startup and a WeatherForecast controller as an example.

Code Example

    dotnet new webapi -n MyWebApi
Enter fullscreen mode Exit fullscreen mode

2. Program.cs — Minimal API Configuration

Concept

.NET 8 continues the trend towards minimal APIs, allowing you to configure services and endpoints in a simplified and concise manner directly in the Program.cs file.

Code Example

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();

    app.MapGet("/", () => "Hello, World!");
    app.Run();
Enter fullscreen mode Exit fullscreen mode

3. Defining a Controller

Concept

Controllers handle incoming HTTP requests and respond to the client. They are defined by inheriting from ControllerBase and annotating with [ApiController].

Code Example

    [ApiController]
    [Route("[controller]")]
    public class MyController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get() => Ok("Hello from MyController");
    }
Enter fullscreen mode Exit fullscreen mode

4. Dependency Injection in Controllers

Concept

.NET Core’s built-in dependency injection (DI) makes it easy to manage dependencies. You can inject services into your controllers through their constructors.

Code Example

    public class MyService
    {
        public string GetMessage() => "Injected message";
    }

    public class MyController : ControllerBase
    {
        private readonly MyService _myService;
        public MyController(MyService myService)
        {
            _myService = myService;
        }
        [HttpGet]
        public IActionResult Get() => Ok(_myService.GetMessage());
    }
Enter fullscreen mode Exit fullscreen mode

5. Configuring Services

Concept

Services (like database contexts, custom services, etc.) are configured in the Program.cs file, making them available for dependency injection throughout your application.

Code Example

    builder.Services.AddScoped<MyService>();
Enter fullscreen mode Exit fullscreen mode

6. Environment-Based Configuration

Concept

.NET supports environment-specific configuration files (appsettings.json, appsettings.Development.json, etc.), allowing for different settings based on the application’s environment.

Code Example

    // appsettings.Development.json
    {
      "Logging": {
        "LogLevel": {
          "Default": "Debug"
        }
      }
    }
Enter fullscreen mode Exit fullscreen mode

7. Middleware

Concept

Middleware components form a pipeline that handles requests and responses. Custom middleware can be created for cross-cutting concerns like logging or error handling.

Code Example

    app.Use(async (context, next) =>
    {
        // Custom logic before passing to the next middleware
        await next();
        // Custom logic after executing the next middleware
    });
Enter fullscreen mode Exit fullscreen mode

8. Routing

Concept

Routing in .NET Web API is achieved through attribute routing on controllers and action methods. This allows URLs to be mapped directly to controller actions.

Code Example

    [HttpGet("myaction/{id}")]
    public IActionResult GetAction(int id) => Ok($"Action with ID = {id}");
Enter fullscreen mode Exit fullscreen mode

9. Model Binding

Concept

Model binding automatically maps data from HTTP requests to action method parameters. It supports complex types, including JSON bodies and query string parameters.

Code Example

    public class MyModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    [HttpPost]
    public IActionResult PostAction([FromBody] MyModel model) => Ok(model);
Enter fullscreen mode Exit fullscreen mode

10. Data Validation

Concept

Data annotations can be used to validate model data. The [ApiController] attribute automatically enforces validation, responding with 400 if the model is invalid.

Code Example

    public class MyModel
    {
        [Required]
        public int Id { get; set; }

        [StringLength(100)]
        public string Name { get; set; }
    }
Enter fullscreen mode Exit fullscreen mode

11. Asynchronous Actions

Concept

Asynchronous actions improve scalability by freeing up threads while waiting for I/O operations to complete. Use the async keyword and return Task or Task.

Code Example

    [HttpGet("{id}")]
    public async Task<IActionResult> GetAsync(int id)
    {
        var result = await _service.GetByIdAsync(id);
        return Ok(result);
    }
Enter fullscreen mode Exit fullscreen mode

12. Handling Exceptions Globally

Concept

Global exception handling allows for centralized error processing, logging, and standardized API responses on unhandled exceptions.

Code Example

    app.UseExceptionHandler(a => a.Run(async context =>
    {
        var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
        var exception = exceptionHandlerPathFeature.Error;
        // Log the exception, generate a custom response, etc.
        context.Response.StatusCode = 500;
        await context.Response.WriteAsJsonAsync(new { Error = "An unexpected error occurred" });
    }));
Enter fullscreen mode Exit fullscreen mode

13. API Versioning

Concept

API versioning helps manage changes to the API over time. The .NET platform supports versioning through query string, URL path, or request header.

Code Example

    builder.Services.AddApiVersioning(options =>
    {
        options.AssumeDefaultVersionWhenUnspecified = true;
        options.DefaultApiVersion = new ApiVersion(1, 0);
        options.ReportApiVersions = true;
    });
Enter fullscreen mode Exit fullscreen mode

14. Content Negotiation

Concept

Content negotiation allows an API to serve different formats of the response based on the Accept header in the request, enabling support for formats like JSON, XML, etc.

Code Example

    builder.Services.AddControllers()
        .AddXmlDataContractSerializerFormatters();
Enter fullscreen mode Exit fullscreen mode

15. Custom JSON Serialization Settings

Concept

Customize JSON response formatting, such as camelCase naming or ignoring null values, by configuring JSON serializer settings.

Code Example

    builder.Services.AddControllers()
        .AddJsonOptions(options =>
        {
            options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
            options.JsonSerializerOptions.IgnoreNullValues = true;
        });
Enter fullscreen mode Exit fullscreen mode

16. Configuring CORS

Concept

Cross-Origin Resource Sharing (CORS) allows your API to be called from web applications hosted on different domains. Configure the CORS policy as per your requirements.

Code Example

    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowSpecificOrigin",
            builder => builder.WithOrigins("http://example.com"));
    });

    app.UseCors("AllowSpecificOrigin");
Enter fullscreen mode Exit fullscreen mode

17. Authentication

Concept

Secure your API by enabling authentication, which verifies the identity of users or services making requests.

Code Example

    builder.Services.AddAuthentication("Bearer")
        .AddJwtBearer(options =>
        {
            options.Authority = "https://your-auth-server";
            options.Audience = "your-api";
        });
Enter fullscreen mode Exit fullscreen mode

18. Authorization

Concept

After authentication, authorization determines if an authenticated user has permission to perform an action or access a resource.

Code Example

    [Authorize]
    public class SecureController : ControllerBase
    {
        // Action methods here
    }
Enter fullscreen mode Exit fullscreen mode

19. Swagger/OpenAPI Integration

Concept

Swagger (OpenAPI) provides interactive documentation for your API, allowing developers to understand and consume it easily.

Code Example

    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    app.UseSwagger();
    app.UseSwaggerUI();
Enter fullscreen mode Exit fullscreen mode

20. Logging

Concept

.NET Core provides a built-in logging framework that can log messages to various outputs (console, debug window, external services, etc.).

Code Example

    logger.LogInformation("This is an informational message");

    app.Use(async (context, next) =>
    {
        logger.LogError("This is an error message before the next middleware");
        await next.Invoke();
        // Log after calling the next middleware
    });
Enter fullscreen mode Exit fullscreen mode

21. Using Entity Framework Core

Concept

Entity Framework Core is an ORM used for data access in .NET applications. It allows you to query and manipulate data using strongly typed objects.

Code Example

    public class MyDbContext : DbContext
    {
        public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) {}
        public DbSet<MyModel> MyModels { get; set; }
    }
Enter fullscreen mode Exit fullscreen mode

22. Migrations in Entity Framework Core

Concept

Migrations allow you to apply version control to your database schema by tracking changes in your data models.

Code Example

    dotnet ef migrations add InitialCreate
    dotnet ef database update
Enter fullscreen mode Exit fullscreen mode

23. Repository Pattern

Concept

The Repository pattern abstracts the data layer, making your application more modular and easier to maintain.

Code Example

    public interface IRepository<T>
    {
        Task<IEnumerable<T>> GetAllAsync();
        Task<T> GetByIdAsync(int id);
        // Other methods...
    }

    public class MyRepository<T> : IRepository<T> where T : class
    {
        private readonly MyDbContext _context;
        public MyRepository(MyDbContext context)
        {
            _context = context;
        }
        // Implement methods...
    }
Enter fullscreen mode Exit fullscreen mode

24. Unit Testing

Concept

Unit testing ensures your Web API functions correctly by testing individual units of code in isolation.

Code Example

    public class MyControllerTests
    {
        [Fact]
        public async Task Get_ReturnsExpectedValue()
        {
            // Arrange
            var serviceMock = new Mock<IMyService>();
            serviceMock.Setup(service => service.GetAsync()).ReturnsAsync("test");
            var controller = new MyController(serviceMock.Object);
            // Act
            var result = await controller.Get();
            // Assert
            Assert.Equal("test", result.Value);
        }
    }
Enter fullscreen mode Exit fullscreen mode

25. Integrating with a Front-end

Concept

.NET Web API can serve as the backend for a front-end application, providing RESTful services.

Code Example

    fetch('https://localhost:5001/mycontroller')
      .then(response => response.json())
      .then(data => console.log(data));
Enter fullscreen mode Exit fullscreen mode

26. Health Checks

Concept

Health checks provide a way to monitor the status of your application and its dependencies, useful for microservices architectures.

Code Example

    builder.Services.AddHealthChecks();
    app.MapHealthChecks("/health");
Enter fullscreen mode Exit fullscreen mode

27. Using SignalR for Real-time Communication

Concept

SignalR enables real-time web functionality, allowing server-side code to send asynchronous notifications to client-side web applications.

Code Example

    public class MyHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }
Enter fullscreen mode Exit fullscreen mode

28. Configuring Response Caching

Concept

Response caching reduces the number of requests a server must handle by storing a copy of previously requested resources.

Code Example

    [HttpGet("{id}")]
    [ResponseCache(Duration = 60)]
    public IActionResult GetById(int id)
    {
        // Retrieve and return your resource
    }
Enter fullscreen mode Exit fullscreen mode

29. Static Files

Concept

Serving static files (HTML, CSS, JavaScript, etc.) is essential for backing front-end applications with a .NET Web API.

Code Example

    app.UseStaticFiles(); // Enable static file serving
Enter fullscreen mode Exit fullscreen mode

30. Advanced Configuration and Options Pattern

Concept

The options pattern uses classes to represent groups of related settings. Using IOptions, you can access these settings anywhere in your application.

Code Example

    public class MySettings
    {
        public string Setting1 { get; set; }
        // Other settings
    }

    builder.Services.Configure<MySettings>(builder.Configuration.GetSection("MySettings"));
    public class MyService
    {
        private readonly MySettings _settings;
        public MyService(IOptions<MySettings> settings)
        {
            _settings = settings.Value;
        }
        // Use _settings.Setting1
    }
Enter fullscreen mode Exit fullscreen mode

31. Custom Middleware

Concept

Middleware is software that’s assembled into an application pipeline to handle requests and responses. Custom middleware can be created to perform specific tasks.

Code Example

    public class MyCustomMiddleware
    {
        private readonly RequestDelegate _next;
        public MyCustomMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task Invoke(HttpContext httpContext)
        {
            // Pre-processing logic here
            await _next(httpContext); // Call the next middleware in the pipeline
            // Post-processing logic here
        }
    }
    // Extension method for easy middleware registration
    public static class MyCustomMiddlewareExtensions
    {
        public static IApplicationBuilder UseMyCustomMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyCustomMiddleware>();
        }
    }
Enter fullscreen mode Exit fullscreen mode

32. Rate Limiting

Concept

Rate limiting protects your API from overuse by limiting how often a user can make requests within a certain time frame.

Code Example

    // Assume using a third-party library like AspNetCoreRateLimit
    builder.Services.AddInMemoryRateLimiting();
    builder.Services.Configure<IpRateLimitOptions>(options =>
    {
        options.GeneralRules = new List<RateLimitRule>
        {
            new RateLimitRule
            {
                Endpoint = "*",
                Limit = 100,
                Period = "1h"
            }
        };
    });
Enter fullscreen mode Exit fullscreen mode

33. API Keys Authentication

Concept

API keys are a simple way to authenticate and authorize API calls. They’re passed from client to server either in the query string or header.

Code Example

    public class ApiKeyMiddleware
    {
        private readonly RequestDelegate _next;
        private const string APIKEYNAME = "x-api-key";
        public ApiKeyMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task Invoke(HttpContext context)
        {
            if (!context.Request.Headers.TryGetValue(APIKEYNAME, out var extractedApiKey))
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("API Key was not provided.");
                return;
            }
            // Validate the extracted API Key here...
            await _next(context);
        }
    }
Enter fullscreen mode Exit fullscreen mode

34. Output Caching

Concept

Output caching allows you to store the response to a request. Subsequent requests can be served from the cache, significantly improving performance.

Code Example

    [HttpGet]
    [ResponseCache(Duration = 120, Location = ResponseCacheLocation.Client, NoStore = false)]
    public IActionResult Get()
    {
        // Your logic here
    }
Enter fullscreen mode Exit fullscreen mode

35. Background Tasks

Concept

Background tasks enable operations to run in the background, independent of user requests, like sending emails or processing long-running jobs.

Code Example

    public class MyBackgroundService : BackgroundService
    {
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                // Your background task logic here
                await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
            }
        }
    }
Enter fullscreen mode Exit fullscreen mode

36. WebSockets

Concept

WebSockets provide a full-duplex communication channel over a single, long-lived connection, ideal for real-time applications.

Code Example

    app.UseWebSockets();
    app.Use(async (context, next) =>
    {
        if (context.WebSockets.IsWebSocketRequest)
        {
            WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
            // Handle the WebSocket request here
        }
        else
        {
            await next();
        }
    });
Enter fullscreen mode Exit fullscreen mode

37. Request Localization

Concept

Request localization provides a way to localize content for different cultures and languages, based on the request’s information.

Code Example

    var supportedCultures = new[] { "en-US", "fr-FR" };
    var localizationOptions = new RequestLocalizationOptions()
        .SetDefaultCulture(supportedCultures[0])
        .AddSupportedCultures(supportedCultures)
        .AddSupportedUICultures(supportedCultures);

    app.UseRequestLocalization(localizationOptions);
Enter fullscreen mode Exit fullscreen mode

38. Integrating with GraphQL

Concept

GraphQL is a query language for APIs. Integrating a .NET Web API with GraphQL allows for more efficient data retrieval.

Code Example

    // Assume using a library like HotChocolate
    builder.Services
        .AddGraphQLServer()
        .AddQueryType<Query>();

    app.MapGraphQL();
Enter fullscreen mode Exit fullscreen mode

39. Monitoring and Telemetry

Concept

Monitoring and telemetry involve collecting, analyzing, and acting on data about your application’s performance and usage.

Code Example

    // Assume using Application Insights
    builder.Services.AddApplicationInsightsTelemetry("YOUR_INSTRUMENTATION_KEY");
Enter fullscreen mode Exit fullscreen mode

40. SignalR Hubs and Real-time Communication

Concept

SignalR is a library that simplifies adding real-time web functionality to apps. Real-time web functionality is the ability to have server code push content to connected clients instantly as it happens, not requiring the server to wait for a client to request new data. SignalR is perfect for developing chat applications, real-time dashboards, and more interactive web applications.

Code Example

    public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            // Call the broadcastMessage method to update clients.
            await Clients.All.SendAsync("broadcastMessage", user, message);
        }
    }

    // Startup or Program.cs
    app.MapHub<ChatHub>("/chathub");
Enter fullscreen mode Exit fullscreen mode

Integration in Program.cs:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // Other configurations...
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapHub<ChatHub>("/chathub");
        });
    }
Enter fullscreen mode Exit fullscreen mode

41. Advanced Entity Framework Core — Relationships

Concept

Entity Framework Core allows for the mapping of complex relationships between entities, such as one-to-one, one-to-many, and many-to-many.

Code Example

    public class Author
    {
        public int AuthorId { get; set; }
        public string Name { get; set; }
        public ICollection<Book> Books { get; set; }
    }

    public class Book
    {
        public int BookId { get; set; }
        public string Title { get; set; }
        public int AuthorId { get; set; }
        public Author Author { get; set; }
    }
Enter fullscreen mode Exit fullscreen mode

42. Custom Validation Attributes

Concept

Custom validation attributes allow you to define your validation logic for data models, extending the built-in validation attributes.

Code Example

    public class MyCustomValidationAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            // Your custom validation logic here
            if (value is int intValue && intValue > 0)
            {
                return ValidationResult.Success;
            }
            return new ValidationResult("Value must be positive");
        }
    }

    public class MyModel
    {
        [MyCustomValidationAttribute]
        public int MyProperty { get; set; }
    }
Enter fullscreen mode Exit fullscreen mode

43. Advanced Configuration Scenarios

Concept

.NET’s options pattern supports complex configuration scenarios, including nested objects, lists, and validation.

Code Example

    public class MyOptions
    {
        public MyNestedOptions Nested { get; set; }
        public List<string> Items { get; set; }
    }
    public class MyNestedOptions
    {
        public string Key { get; set; }
    }
    // In Program.cs or Startup.cs
    builder.Services.Configure<MyOptions>(builder.Configuration.GetSection("MyOptions"));
Enter fullscreen mode Exit fullscreen mode

44. Performance Monitoring and Profiling

Concept

Monitoring and profiling an application can identify bottlenecks and inefficiencies, essential for optimizing performance.

Code Example

    app.UseMiniProfiler();
Enter fullscreen mode Exit fullscreen mode

45. API Documentation with Swagger and XML Comments

Concept

Enhance your API documentation by integrating XML comments into your Swagger UI, providing a richer experience for developers consuming your API.

Code Example

    builder.Services.AddSwaggerGen(c =>
    {
        var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
        var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
        c.IncludeXmlComments(xmlPath);
    });
Enter fullscreen mode Exit fullscreen mode

46. Globalization and Localization

Concept

Globalization and localization allow your application to support multiple languages and cultures, making it accessible to a global audience.

Code Example

    builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
    app.UseRequestLocalization(app.Services.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value);
Enter fullscreen mode Exit fullscreen mode

47. Security Headers

Concept

Improving the security of your web application by adding various HTTP headers can protect against common attacks and vulnerabilities.

Code Example

    app.UseHsts();
    app.UseXContentTypeOptions();
    app.UseReferrerPolicy(opts => opts.NoReferrer());
    app.UseXXssProtection(options => options.EnabledWithBlockMode());
    app.UseXfo(options => options.Deny());
Enter fullscreen mode Exit fullscreen mode

48. Feature Flags

Concept

Feature flags allow you to toggle features of your application on and off without deploying new code, facilitating easier testing and rollouts.

Code Example

    // Using a library like Microsoft.FeatureManagement
    builder.Services.AddFeatureManagement();
Enter fullscreen mode Exit fullscreen mode

49. Blazor Integration

Concept

Blazor allows you to build interactive web UIs using C# instead of JavaScript. Integrating Blazor with Web API provides a seamless full-stack development experience.

Code Example

    // In a Blazor Server app
    @code {
        private IEnumerable<WeatherForecast> forecasts;
        protected override async Task OnInitializedAsync()
        {
            forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
        }
    }
Enter fullscreen mode Exit fullscreen mode

50. Advanced Middleware for Response Compression

Concept

Response compression can reduce the size of your API responses, improving load times for clients over slow networks.

Code Example

    builder.Services.AddResponseCompression(options =>
    {
        options.Providers.Add<GzipCompressionProvider>();
        options.EnableForHttps = true;
    });

    app.UseResponseCompression();
Enter fullscreen mode Exit fullscreen mode

C# Programming🚀

Thank you for being a part of the C# community! Before you leave:

If you’ve made it this far, please show your appreciation with a clap and follow the author! 👏️️

Follow us: X | LinkedIn | Dev.to | Hashnode | Newsletter | Tumblr

Visit our other platforms: GitHub | Instagram | Tiktok | Quora | Daily.dev

More content at C# Programming

Top comments (0)