# Implement a Common API Response Structure in ASP.NET Core

URL: https://answers.mindstick.com/blog/301/implement-a-common-api-response-structure-in-asp-dot-net-core

Building APIs in ASP.NET Core becomes much easier to maintain when all endpoints return a consistent response structure.
A common API response format helps:
Frontend developers consume APIs more easilyStandardize success and error responsesImprove debugging and loggingSimplify API documentationCreate cleaner enterprise-grade applications
Why Use a Common API Response Structure?
Without a standard format, APIs often return inconsistent responses like:
{
  "name": "John"
}
or
{
  "message": "User created successfully"
}
or
{
  "error": "Something went wrong"
}
This inconsistency creates problems for frontend applications and API consumers.
Instead, use a unified response structure like:
{
  "success": true,
  "message": "User fetched successfully",
  "data": {
    "id": 1,
    "name": "John"
  },
  "errors": null
}
Benefits of Standardized API Responses
A common structure provides:
Benefit
Description
Consistency
Every endpoint behaves similarly
Better Frontend Integration
Easier parsing on client side
Improved Debugging
Centralized error handling
Scalability
Easier to maintain large projects
Cleaner Documentation
Predictable API contracts
Step 1: Create a Generic API Response Model
Create a folder named Models and add the following class.
ApiResponse.cs
namespace DemoAPI.Models
{
    public class ApiResponseandlt;Tandgt;
    {
        public bool Success { get; set; }
        public string Message { get; set; }
        public T Data { get; set; }
        public Listandlt;stringandgt; Errors { get; set; }
        public ApiResponse()
        {
            Errors = new Listandlt;stringandgt;();
        }
    }
}
Understanding the Properties
Property
Purpose
Success
Indicates request success/failure
Message
Human-readable response message
Data
Actual response payload
Errors
Validation or exception details
Using generics (T) makes this reusable for all APIs.
Step 2: Create a Sample DTO
UserDto.cs
namespace DemoAPI.Models
{
    public class UserDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
    }
}
Step 3: Implement in Controller
UsersController.cs
using DemoAPI.Models;
using Microsoft.AspNetCore.Mvc;
namespace DemoAPI.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class UsersController : ControllerBase
    {
        [HttpGet("{id}")]
        public IActionResult GetUser(int id)
        {
            var user = new UserDto
            {
                Id = id,
                Name = "John Doe",
                Email = "john@example.com"
            };
            var response = new ApiResponseandlt;UserDtoandgt;
            {
                Success = true,
                Message = "User fetched successfully",
                Data = user
            };
            return Ok(response);
        }
    }
}
Sample Success Response
{
  "success": true,
  "message": "User fetched successfully",
  "data": {
    "id": 1,
    "name": "John Doe",
    "email": "john@example.com"
  },
  "errors": []
}
Step 4: Handle Error Responses
Example Error Response
[HttpGet("{id}")]
public IActionResult GetUser(int id)
{
    if (id andlt;= 0)
    {
        var errorResponse = new ApiResponseandlt;objectandgt;
        {
            Success = false,
            Message = "Invalid user id",
            Errors = new Listandlt;stringandgt;
            {
                "User id must be greater than zero"
            }
        };
        return BadRequest(errorResponse);
    }
    return Ok();
}
Sample Error Output
{
  "success": false,
  "message": "Invalid user id",
  "data": null,
  "errors": [
    "User id must be greater than zero"
  ]
}
Step 5: Create a Reusable Response Helper (Optional)
To reduce repetitive code, create a helper class.
ApiResponseHelper.cs
namespace DemoAPI.Helpers
{
    public static class ApiResponseHelper
    {
        public static ApiResponseandlt;Tandgt; Successandlt;Tandgt;(
            T data,
            string message = "Request successful")
        {
            return new ApiResponseandlt;Tandgt;
            {
                Success = true,
                Message = message,
                Data = data
            };
        }
        public static ApiResponseandlt;Tandgt; Failandlt;Tandgt;(
            string message,
            Listandlt;stringandgt; errors = null)
        {
            return new ApiResponseandlt;Tandgt;
            {
                Success = false,
                Message = message,
                Errors = errors ?? new Listandlt;stringandgt;()
            };
        }
    }
}
Cleaner Controller Code
[HttpGet("{id}")]
public IActionResult GetUser(int id)
{
    if (id andlt;= 0)
    {
        return BadRequest(
            ApiResponseHelper.Failandlt;objectandgt;(
                "Invalid user id",
                new Listandlt;stringandgt;
                {
                    "Id must be greater than zero"
                }));
    }
    var user = new UserDto
    {
        Id = id,
        Name = "John Doe",
        Email = "john@example.com"
    };
    return Ok(
        ApiResponseHelper.Success(
            user,
            "User fetched successfully"));
}
Step 6: Global Exception Handling
For production-grade applications, use middleware for centralized exception handling.
ExceptionMiddleware.cs
using System.Net;
using System.Text.Json;
using DemoAPI.Models;
namespace DemoAPI.Middleware
{
    public class ExceptionMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILoggerandlt;ExceptionMiddlewareandgt; _logger;
        public ExceptionMiddleware(
            RequestDelegate next,
            ILoggerandlt;ExceptionMiddlewareandgt; logger)
        {
            _next = next;
            _logger = logger;
        }
        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, ex.Message);
                context.Response.ContentType = "application/json";
                context.Response.StatusCode =
                    (int)HttpStatusCode.InternalServerError;
                var response = new ApiResponseandlt;objectandgt;
                {
                    Success = false,
                    Message = "Internal server error",
                    Errors = new Listandlt;stringandgt;
                    {
                        ex.Message
                    }
                };
                var json = JsonSerializer.Serialize(response);
                await context.Response.WriteAsync(json);
            }
        }
    }
}
Register Middleware
In Program.cs:
app.UseMiddlewareandlt;ExceptionMiddlewareandgt;();
Final API Response Standard
A robust enterprise response model typically includes:
{
  "success": true,
  "message": "Operation completed successfully",
  "data": {},
  "errors": [],
  "statusCode": 200,
  "timestamp": "2026-05-16T10:00:00Z"
}
You can extend your response model with:
Status codesPagination metadataCorrelation IDsRequest tracking IDsTimestamps
Best Practices
Keep Responses Predictable
Every endpoint should follow the same structure.
Avoid Returning Raw Exceptions
Never expose sensitive internal details in production.
Use Proper HTTP Status Codes
Examples:
200 OK201 Created400 Bad Request401 Unauthorized404 Not Found500 Internal Server Error
Use DTOs
Avoid exposing database entities directly.
Implement Validation
Use FluentValidation or DataAnnotations.
Common Mistakes to Avoid
Mistake
Problem
Returning inconsistent JSON
Hard for frontend parsing
Exposing stack traces
Security risk
Using exceptions for validation
Poor performance
Returning HTTP 200 for errors
Misleading API behavior
