Implement Common Middleware for Standardized Success & Failure Response Format in .NET Core


When building APIs in .NET Core, maintaining a consistent response structure is extremely important. A common response format helps frontend developers handle API responses easily and improves debugging, scalability, and maintainability.

In this blog, we’ll create a reusable middleware in ASP.NET Core that automatically formats success and error responses in a standardized structure.

Why Use a Common Response Format?

Without a standard format, APIs may return inconsistent responses like:

Success Response

{
  "message": "User fetched successfully",
  "data": {
    "id": 1,
    "name": "John"
  }
}

Error Response

{
  "error": "User not found"
}

This inconsistency makes frontend handling difficult.

A standard response solves this problem.

Standard API Response Structure

Success Response

{
  "success": true,
  "statusCode": 200,
  "message": "Data fetched successfully",
  "data": {}
}

Failure Response

{
  "success": false,
  "statusCode": 500,
  "message": "Something went wrong",
  "errors": []
}

Benefits

  • Consistent API responses
  • Centralized error handling
  • Cleaner controllers
  • Better frontend integration
  • Easier debugging
  • Improved scalability

Step 1: Create Common Response Models

Create a folder:

Models/

ApiResponse.cs

// Generic API response model
namespace DemoAPI.Models
{
    public class ApiResponse<T>
    {
        // Indicates success or failure
        public bool Success { get; set; }

        // HTTP status code
        public int StatusCode { get; set; }

        // Response message
        public string Message { get; set; }

        // Actual response data
        public T Data { get; set; }

        // Error details
        public object Errors { get; set; }
    }
}

Step 2: Create Exception Middleware

Create a folder:

Middleware/

ExceptionMiddleware.cs

using System.Net;
using System.Text.Json;
using DemoAPI.Models;

namespace DemoAPI.Middleware
{
    public class ExceptionMiddleware
    {
        // Request delegate instance
        private readonly RequestDelegate _next;

        // Constructor
        public ExceptionMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        // Middleware execution method
        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                // Move request to next middleware
                await _next(context);
            }
            catch (Exception ex)
            {
                // Handle exception
                await HandleExceptionAsync(context, ex);
            }
        }

        // Exception handling method
        private static Task HandleExceptionAsync(
            HttpContext context,
            Exception exception)
        {
            // Set response content type
            context.Response.ContentType = "application/json";

            // Set status code
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

            // Create standardized response
            var response = new ApiResponse<object>
            {
                Success = false,
                StatusCode = context.Response.StatusCode,
                Message = "Something went wrong",
                Errors = exception.Message
            };

            // Convert object to JSON
            var jsonResponse = JsonSerializer.Serialize(response);

            // Return response
            return context.Response.WriteAsync(jsonResponse);
        }
    }
}

Step 3: Create Response Helper

ResponseHelper.cs

using DemoAPI.Models;

namespace DemoAPI.Helpers
{
    public static class ResponseHelper
    {
        // Success response
        public static ApiResponse<T> Success<T>(
            T data,
            string message = "Success",
            int statusCode = 200)
        {
            return new ApiResponse<T>
            {
                Success = true,
                StatusCode = statusCode,
                Message = message,
                Data = data
            };
        }

        // Error response
        public static ApiResponse<object> Fail(
            string message,
            object errors = null,
            int statusCode = 500)
        {
            return new ApiResponse<object>
            {
                Success = false,
                StatusCode = statusCode,
                Message = message,
                Errors = errors
            };
        }
    }
}

Step 4: Register Middleware

Program.cs (.NET 6 / .NET 7 / .NET 8)

using DemoAPI.Middleware;

var builder = WebApplication.CreateBuilder(args);

// Add services
builder.Services.AddControllers();

var app = builder.Build();

// Register custom exception middleware
app.UseMiddleware<ExceptionMiddleware>();

// Enable routing
app.MapControllers();

// Run application
app.Run();

Step 5: Use in Controller

UserController.cs

using Microsoft.AspNetCore.Mvc;
using DemoAPI.Helpers;

namespace DemoAPI.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class UserController : ControllerBase
    {
        // GET api/user
        [HttpGet]
        public IActionResult GetUser()
        {
            // Dummy user object
            var user = new
            {
                Id = 1,
                Name = "John Doe",
                Email = "john@example.com"
            };

            // Return standardized success response
            return Ok(
                ResponseHelper.Success(
                    user,
                    "User fetched successfully"
                )
            );
        }
    }
}

Success Output

{
  "success": true,
  "statusCode": 200,
  "message": "User fetched successfully",
  "data": {
    "id": 1,
    "name": "John Doe",
    "email": "john@example.com"
  },
  "errors": null
}

Error Output

{
  "success": false,
  "statusCode": 500,
  "message": "Something went wrong",
  "data": null,
  "errors": "Object reference not set to an instance of an object"
}

Optional Improvements

You can enhance this middleware further by adding:

  • Request tracking ID
  • Timestamp
  • Validation error handling
  • Logging with Serilog
  • Custom exception classes
  • Localization support
  • Pagination metadata

Example with Timestamp

public DateTime Timestamp { get; set; } = DateTime.UtcNow;

Recommended Folder Structure

DemoAPI/
│
├── Controllers/
│   └── UserController.cs
│
├── Helpers/
│   └── ResponseHelper.cs
│
├── Middleware/
│   └── ExceptionMiddleware.cs
│
├── Models/
│   └── ApiResponse.cs
│
├── Program.cs
│
└── appsettings.json

Best Practices

Use Proper HTTP Status Codes

Status Code Meaning
200 Success
201 Created
400 Bad Request
401 Unauthorized
404 Not Found
500 Internal Server Error

Keep Response Structure Consistent

Never change response keys between APIs.

Centralize Error Handling

Avoid writing try-catch blocks in every controller.

Keep Coding.

0 Comments Report