ASP.NET Core API Pagination and Filtering

Implementing pagination and filtering in an API is a common practice to optimize responses and efficiently handle large datasets. Here's a step-by-step guide on how to achieve this in an ASP.NET Core Web API:

Step 1. Create a new ASP.NET Core Web API project

Create the project, Asp.net Core Web API using Visual Studio OR CLI.

Step 2. Define your model class

Define your model class named ListCSharpCornerArticles

Author: Sardar Mudassar Ali Khan
public class ListCSharpCornerArticles
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Category { get; set; }
}

Step 3. Create a custom middleware for logging incoming requests

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;

Author: Sardar Mudassar Ali Khan
public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _logger;

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

    public async Task Invoke(HttpContext context)
    {
        _logger.LogInformation($"Request: {context.Request.Method} {context.Request.Path}");
        await _next(context);
    }
}

Step 4. Configure the custom middleware

Configure the custom middleware and implement pagination and filtering in the controller

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

Author: Sardar Mudassar Ali Khan
public class Startup
{
    public IConfiguration Configuration { get; }

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();

        // Add logging
        services.AddLogging();

        // Configure content negotiation
        services.AddMvc(options =>
        {
            options.RespectBrowserAcceptHeader = true;
            options.ReturnHttpNotAcceptable = true;
        }).AddXmlDataContractSerializerFormatters();
    }

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

        // Use the custom logging middleware
        app.UseMiddleware<RequestLoggingMiddleware>();

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Step 5. Create a controller that handles pagination and filtering

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;

Author: Sardar Mudassar Ali Khan
[ApiController]
[Route("api/[controller]")]
public class CSharpCornerArticlesController : ControllerBase
{
    private static List<ListCSharpCornerArticles> _articles = GenerateSampleArticles();

    private static List<ListCSharpCornerArticles> GenerateSampleArticles()
    {
        // Generate and return sample articles
    }

    [HttpGet]
    public IActionResult Get([FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] string filter = "")
    {
        var query = _articles.AsQueryable();

        if (!string.IsNullOrEmpty(filter))
        {
            query = query.Where(article => article.Title.Contains(filter) || article.Category.Contains(filter));
        }

        var totalCount = query.Count();
        var totalPages = (int)Math.Ceiling((double)totalCount / pageSize);

        query = query.Skip((page - 1) * pageSize).Take(pageSize);

        var result = new
        {
            TotalCount = totalCount,
            TotalPages = totalPages,
            CurrentPage = page,
            PageSize = pageSize,
            Articles = query.ToList()
        };

        return Ok(result);
    }
}

With this setup, the custom middleware RequestLoggingMiddleware will log information about incoming requests, which can be helpful for monitoring and debugging purposes. It's added to the application pipeline before the routing and endpoint handling.

By combining the custom middleware with the pagination and filtering logic, you have a more comprehensive example that demonstrates both the implementation of custom middleware and the optimization techniques of pagination and filtering in an ASP.NET Core Web API.

Conclusion

Implementing pagination and filtering, along with custom middleware, in an ASP.NET Core Web API enhances both the performance and functionality of your API. Let's summarize the key takeaways:

Pagination and Filtering

  1. Pagination divides large datasets into manageable chunks, improving response times and reducing the load on both the server and the network.
  2. Filtering allows clients to request specific subsets of data that match their criteria, leading to more relevant and efficient data retrieval.
  3. Combining pagination and filtering provides users with the ability to navigate large datasets effectively and retrieve only the information they need.

Custom Middleware

  • Custom middleware extends the functionality of the request pipeline by introducing reusable components that perform specific tasks, such as logging, authentication, or modifying requests and responses.
  • Middleware components are configured in the Startup.cs file and can be placed at specific points in the pipeline to intercept and process requests before they reach the controllers.
  • In the provided example, the custom middleware `RequestLoggingMiddleware` logs incoming requests, offering insight into how the API is being utilized.

By implementing pagination and filtering, you optimize API responses and provide users with better control over the data they access. Integrating custom middleware enhances the observability and monitoring capabilities of your API, allowing you to gain insights into incoming requests and diagnose potential issues more effectively.

Both features contribute to a more efficient, user-friendly, and maintainable ASP.NET Core Web API. When designing APIs, it's crucial to balance performance considerations with the flexibility and functionality that users require. By incorporating these practices, you're better equipped to deliver a high-quality API that meets user expectations and performs optimally under various conditions.