Unified Response Format
MiCake provides a unified API response wrapping feature that automatically wraps controller return values into a standard format, making API responses more standardized and consistent.
The Standard Response Format
Section titled “The Standard Response Format”MiCake’s standard response format contains three fields:
{ "code": "200", "message": "Success", "data": { // The actual returned data }}Field description:
code: the business status code (a string type)message: the response messagedata: the actual business data
Basic Usage
Section titled “Basic Usage”Enabled by Default
Section titled “Enabled by Default”MiCake enables response wrapping by default - no additional configuration is needed:
[ApiController][Route("api/[controller]")]public class OrderController : ControllerBase{ [HttpGet("{id}")] public async Task<Order> GetOrder(int id) { var order = await _orderRepository.FindAsync(id); return order; }}
// The actual response:// {// "code": "200",// "message": "Success",// "data": {// "id": 1,// "customerName": "Zhang San",// "totalAmount": 999.00// }// }Collection Data
Section titled “Collection Data”[HttpGet]public async Task<List<OrderDto>> GetOrders(){ return await _orderService.GetAllOrders();}
// Response:// {// "code": "200",// "message": "Success",// "data": [// { "id": 1, "customerName": "Zhang San" },// { "id": 2, "customerName": "Li Si" }// ]// }Simple Types
Section titled “Simple Types”[HttpPost]public async Task<int> CreateOrder([FromBody] CreateOrderDto dto){ return await _orderService.CreateOrder(dto);}
// Response:// {// "code": "200",// "message": "Success",// "data": 123// }Error Responses
Section titled “Error Responses”Exceptions are automatically converted into the error response format:
[HttpGet("{id}")]public async Task<Order> GetOrder(int id){ var order = await _orderRepository.FindAsync(id); if (order == null) throw new NotFoundException("Order", id);
return order;}
// When the order does not exist:// {// "code": "NOT_FOUND",// "message": "Order with id 123 was not found",// "errors": null// }Validation Errors
Section titled “Validation Errors”[HttpPost]public async Task<int> CreateOrder([FromBody] CreateOrderDto dto){ if (!ModelState.IsValid) { var errors = ModelState.Values .SelectMany(v => v.Errors) .Select(e => e.ErrorMessage) .ToList();
throw new ValidationException("Validation failed", errors); }
return await _orderService.CreateOrder(dto);}
// When validation fails:// {// "code": "VALIDATION_ERROR",// "message": "Validation failed",// "errors": [// { "field": "CustomerName", "message": "The customer name cannot be empty" },// { "field": "TotalAmount", "message": "The amount must be greater than zero" }// ]// }Custom Responses
Section titled “Custom Responses”Using ApiResponse
Section titled “Using ApiResponse”If you need to customize the response, you can use the ApiResponse class:
using MiCake.AspNetCore.Responses;
[HttpPost]public async Task<ApiResponse<int>> CreateOrder([FromBody] CreateOrderDto dto){ var orderId = await _orderService.CreateOrder(dto);
return new ApiResponse<int> { Code = "ORDER_CREATED", Message = "The order was created successfully", Data = orderId };}
// Response:// {// "code": "ORDER_CREATED",// "message": "The order was created successfully",// "data": 123// }Custom Error Responses
Section titled “Custom Error Responses”[HttpPost]public async Task<ApiResponse<bool>> ProcessOrder(int orderId){ try { await _orderService.ProcessOrder(orderId); return new ApiResponse<bool> { Code = "SUCCESS", Message = "The order was processed successfully", Data = true }; } catch (BusinessException ex) { return new ApiResponse<bool> { Code = ex.Code ?? "BUSINESS_ERROR", Message = ex.Message, Data = false }; }}Disabling Response Wrapping
Section titled “Disabling Response Wrapping”Disabling at the Method Level
Section titled “Disabling at the Method Level”For certain special endpoints, you may not want response wrapping:
using MiCake.AspNetCore.Responses;
[HttpGet("raw")][DisableResponseWrapper] // Disable response wrappingpublic async Task<Order> GetRawOrder(int id){ return await _orderRepository.FindAsync(id);}
// Returns the order object directly, unwrapped:// {// "id": 1,// "customerName": "Zhang San",// "totalAmount": 999.00// }Disabling at the Controller Level
Section titled “Disabling at the Controller Level”[ApiController][Route("api/[controller]")][DisableResponseWrapper] // Disable response wrapping for the entire controllerpublic class RawDataController : ControllerBase{ // None of the methods will have response wrapping}Configuring Response Wrapping
Section titled “Configuring Response Wrapping”Global Configuration
Section titled “Global Configuration”Configure the response wrapping options in Startup.cs:
public void ConfigureServices(IServiceCollection services){ services.AddMiCakeWithDefault<MyAppModule, MyDbContext>(options => { options.AspNetConfig = asp => { // Configure the data wrapper asp.DataWrapperOptions = wrapperOptions => { // Set the default success code wrapperOptions.DefaultSuccessCode = "0";
// Set the default success message wrapperOptions.DefaultSuccessMessage = "Operation succeeded";
// Set the default error message wrapperOptions.DefaultErrorMessage = "Operation failed"; }; }; }).Build();}A Custom Wrapper
Section titled “A Custom Wrapper”If you need to fully customize the response format, you can implement the IResponseWrapper interface:
public class CustomResponseWrapper : IResponseWrapper{ public string? Code { get; set; } public string? Message { get; set; } public object? Data { get; set; } public DateTime Timestamp { get; set; } = DateTime.UtcNow; public string? TraceId { get; set; }}
// Register the custom wrapperpublic class MyModule : MiCakeModule{ public override void ConfigureServices(ModuleConfigServiceContext context) { context.Services.AddSingleton<IResponseWrapper, CustomResponseWrapper>(); base.ConfigureServices(context); }}Response Wrapping Best Practices
Section titled “Response Wrapping Best Practices”1. Keep It Consistent
Section titled “1. Keep It Consistent”Use a unified response format throughout the entire application:
// ✅ Correct: let MiCake wrap the response automatically[HttpGet("{id}")]public async Task<Order> GetOrder(int id){ return await _orderRepository.FindAsync(id);}
// ❌ Not recommended: manually build the response object[HttpGet("{id}")]public async Task<IActionResult> GetOrder(int id){ var order = await _orderRepository.FindAsync(id); return Ok(new { code = "200", data = order });}2. Use Appropriate HTTP Status Codes
Section titled “2. Use Appropriate HTTP Status Codes”MiCake automatically sets the correct HTTP status code:
// Success: 200 OK[HttpGet("{id}")]public async Task<Order> GetOrder(int id){ return await _orderRepository.FindAsync(id);}
// Not found: 404 Not Found[HttpGet("{id}")]public async Task<Order> GetOrder(int id){ var order = await _orderRepository.FindAsync(id); if (order == null) throw new NotFoundException("Order", id); return order;}
// Validation error: 400 Bad Request[HttpPost]public async Task<int> CreateOrder([FromBody] CreateOrderDto dto){ if (!ModelState.IsValid) throw new ValidationException("Validation failed"); return await _orderService.CreateOrder(dto);}3. Provide Meaningful Error Codes
Section titled “3. Provide Meaningful Error Codes”// ✅ Correct: use meaningful error codesthrow new BusinessException("Insufficient stock", code: "INSUFFICIENT_STOCK");throw new BusinessException("The order has been cancelled", code: "ORDER_CANCELLED");
// ❌ Wrong: use a generic error codethrow new Exception("Error");4. Keep Return Types Consistent
Section titled “4. Keep Return Types Consistent”// ✅ Correct: consistent return types[HttpGet]public async Task<List<OrderDto>> GetOrders(){ return await _orderService.GetAllOrders();}
// ❌ Not recommended: inconsistent return types[HttpGet]public async Task<IActionResult> GetOrders(){ var orders = await _orderService.GetAllOrders(); if (!orders.Any()) return NotFound(); return Ok(orders);}- Enabled by default: MiCake enables response wrapping by default
- Automatic exception handling: exceptions are automatically converted into unified error responses
- Custom responses: you can customize the response using the
ApiResponseclass - Disabling wrapping: use the
[DisableResponseWrapper]attribute to disable wrapping - HTTP status codes: MiCake automatically sets the correct HTTP status code based on the exception type
