Последняя активность 1768571050

Версия 9aec7d380174baf32731a980437c228dee486be7

AlveusApiStandard.md Исходник

Alveus API Standard

This document describes the standardized API response format used across all Alveus API projects built with Alveus.Api.Common.

Overview

All API endpoints return responses wrapped in a standardized ApiResponse object. This ensures consistency across all APIs and makes it easier for clients to handle responses, errors, and pagination.

Response Models

Base Response: ApiResponse

The base response model used for all API responses without data payloads.

Properties:

  • success (boolean): Indicates whether the request was successful
  • message (string): A human-readable message describing the result
  • timestamp (long): When the response was generated (Unix UTC milliseconds since epoch)
  • traceId (string): Request correlation ID for debugging

Note: The timestamp is represented as Unix UTC milliseconds since epoch (January 1, 1970). This can be easily converted to a DateTime in most languages:

  • C#: DateTimeOffset.FromUnixTimeMilliseconds(timestamp)
  • JavaScript: new Date(timestamp)
  • Python: datetime.fromtimestamp(timestamp / 1000, tz=timezone.utc)

Example - Success Response:

{
  "success": true,
  "message": "Operation completed successfully",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF"
}

Example - Simple Failure Response:

{
  "success": false,
  "message": "Operation failed",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF"
}

Generic Data Response: ApiResponse<T>

Used when returning data in the response. Inherits from ApiResponse.

Additional Properties:

  • data (T, optional): The response payload

Example - User Data Response:

{
  "success": true,
  "message": "User retrieved successfully",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "username": "john.doe",
    "email": "[email protected]",
    "createdAt": "2025-01-01T00:00:00Z"
  }
}

Example - List of Items:

{
  "success": true,
  "message": "Products retrieved successfully",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF",
  "data": [
    {
      "id": 1,
      "name": "Product A",
      "price": 29.99
    },
    {
      "id": 2,
      "name": "Product B",
      "price": 49.99
    }
  ]
}

Error Response: ApiErrorResponse

Used when returning validation errors. Inherits from ApiResponse.

Additional Properties:

  • errors (ValidationError[], optional): Array of validation errors

ValidationError Properties:

  • field (string): The field name that failed validation
  • message (string): The error message
  • code (string, optional): An error code for programmatic handling
  • attemptedValue (any, optional): The value that failed validation

Example - Validation Error Response:

{
  "success": false,
  "message": "Validation failed",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF",
  "errors": [
    {
      "field": "email",
      "message": "Email address is invalid",
      "code": "InvalidFormat",
      "attemptedValue": "not-an-email"
    },
    {
      "field": "password",
      "message": "Password must be at least 8 characters",
      "code": "MinLength",
      "attemptedValue": "short"
    }
  ]
}

Paginated Response: PagedApiResponse<T>

Used for paginated list endpoints. Inherits from ApiResponse<IEnumerable<T>>.

Additional Properties:

  • data (T[]): Array of items for the current page
  • pagination (PaginationMetadata): Pagination information

PaginationMetadata Properties:

  • currentPage (int): Current page number (1-based)
  • pageSize (int): Number of items per page
  • totalCount (long): Total number of items across all pages
  • totalPages (int): Total number of pages
  • hasNextPage (boolean): Whether a next page exists
  • hasPreviousPage (boolean): Whether a previous page exists

Example - Paginated Users Response:

{
  "success": true,
  "message": "Users retrieved successfully",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF",
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "username": "john.doe",
      "email": "[email protected]"
    },
    {
      "id": "660f9511-f3ac-52e5-b827-557766551111",
      "username": "jane.smith",
      "email": "[email protected]"
    }
  ],
  "pagination": {
    "currentPage": 1,
    "pageSize": 2,
    "totalCount": 10,
    "totalPages": 5,
    "hasNextPage": true,
    "hasPreviousPage": false
  }
}

HTTP Status Codes

The API uses standard HTTP status codes in combination with the success field:

Success Codes (2xx)

  • 200 OK: Standard success response
  • 201 Created: Resource successfully created
  • 204 No Content: Success with no data to return

Client Error Codes (4xx)

  • 400 Bad Request: Invalid request format or parameters
  • 401 Unauthorized: Authentication required or failed
  • 403 Forbidden: User lacks permission for the requested resource
  • 404 Not Found: Resource does not exist
  • 409 Conflict: Request conflicts with current state (e.g., duplicate resource)
  • 422 Unprocessable Entity: Validation failed

Server Error Codes (5xx)

  • 500 Internal Server Error: Unexpected server error
  • 503 Service Unavailable: Service temporarily unavailable

Common Response Patterns

Success with Data (200 OK)

{
  "success": true,
  "message": "Resource retrieved successfully",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF",
  "data": {
    "id": 123,
    "name": "Example"
  }
}

Resource Created (201 Created)

{
  "success": true,
  "message": "Resource created",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF",
  "data": {
    "id": 124,
    "name": "New Resource",
    "createdAt": "2026-01-16T14:30:00Z"
  }
}

Success Without Data (204 No Content)

{
  "success": true,
  "message": "No content",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF"
}

Bad Request (400)

{
  "success": false,
  "message": "Bad request",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF"
}

Unauthorized (401)

{
  "success": false,
  "message": "Unauthorized",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF"
}

Forbidden (403)

{
  "success": false,
  "message": "Forbidden",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF"
}

Not Found (404)

{
  "success": false,
  "message": "Resource not found",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF"
}

Conflict (409)

{
  "success": false,
  "message": "Resource already exists",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF"
}

Validation Error (422)

{
  "success": false,
  "message": "Validation failed",
  "timestamp": "2026-01-16T14:30:00.123Z",
  "traceId": "0HN1234567890ABCDEF",
  "errors": [
    {
      "field": "username",
      "message": "Username is required",
      "code": "Required"
    }
  ]
}

Internal Server Error (500)

{
  "success": false,
  "message": "An unexpected error occurred",
  "timestamp": 1737033000123,
  "traceId": "0HN1234567890ABCDEF"
}

Using the API Standard in Controllers

Controllers should inherit from ApiControllerBase which provides helper methods for returning standardized responses:

Success Methods

// Success with data
return Success("User retrieved", user);

// Success with data and custom status code
return Success("User updated", user, 200);

// Success without data
return Success("Operation completed");

// Created (201)
return Created("User created", newUser);
return Created(newUser); // Uses default message

// No Content (204)
return NoContent();

Error Methods

// Generic failure
return Failure("Something went wrong");

// Bad Request (400)
return BadRequest("Invalid input");

// Unauthorized (401)
return Unauthorized("Authentication required");

// Forbidden (403)
return Forbidden("Access denied");

// Not Found (404)
return NotFound("User not found");

// Conflict (409)
return Conflict("Username already exists");

Pagination Methods

// Paginated response
return PagedSuccess(
    "Users retrieved",
    users,
    currentPage: 1,
    pageSize: 10,
    totalCount: 100
);

Exception Handling

When using the Alveus.Api.Common.Client for making API calls, responses are automatically parsed and exceptions are thrown for error responses:

Built-in Exceptions

All exceptions inherit from ApiException which contains:

  • Message: Error description
  • StatusCode: HTTP status code

Available Exception Types:

  • BadRequestException (400)
  • UnauthorizedException (401)
  • ForbiddenException (403)
  • NotFoundException (404)
  • ConflictException (409)
  • ValidationException (422) - includes ValidationErrors property
  • TooManyRequestsException (429)
  • InternalServerErrorException (500)
  • ServiceUnavailableException (503)

Example Usage:

try
{
    var user = await apiClient.GetUserAsync(userId);
}
catch (NotFoundException ex)
{
    Console.WriteLine($"User not found: {ex.Message}");
}
catch (ValidationException ex)
{
    foreach (var error in ex.ValidationErrors)
    {
        Console.WriteLine($"{error.Field}: {error.Message}");
    }
}
catch (ApiException ex)
{
    Console.WriteLine($"API Error ({ex.StatusCode}): {ex.Message}");
}

Best Practices

  1. Always wrap responses: Use the helper methods from ApiControllerBase to ensure consistent response format
  2. Include meaningful messages: Provide clear, actionable messages for both success and error cases
  3. Use appropriate status codes: Match the HTTP status code to the actual result
  4. Validate input: Return ValidationException with detailed field-level errors for invalid input
  5. Handle exceptions: Use try-catch blocks and return appropriate error responses
  6. Document your endpoints: Include response examples in API documentation
  7. Use pagination: For list endpoints that may return many items, use PagedSuccess
  8. Be consistent: Always use the same response structure across all endpoints
  9. Leverage traceId for debugging: Use the traceId to correlate requests across services and logs

Complete API Endpoint Example

[HttpGet("users/{id}")]
[ProducesResponseType(typeof(ApiResponse<UserDto>), 200)]
[ProducesResponseType(typeof(ApiResponse), 404)]
public async Task<IActionResult> GetUser(string id)
{
    var user = await _userService.GetUserByIdAsync(id);
    
    if (user == null)
    {
        return NotFound($"User with ID '{id}' not found");
    }
    
    return Success("User retrieved successfully", user);
}

[HttpPost("users")]
[ProducesResponseType(typeof(ApiResponse<UserDto>), 201)]
[ProducesResponseType(typeof(ApiErrorResponse), 422)]
public async Task<IActionResult> CreateUser([FromBody] CreateUserRequest request)
{
    if (!ModelState.IsValid)
    {
        var errors = ModelState
            .Where(x => x.Value?.Errors.Count > 0)
            .SelectMany(x => x.Value!.Errors.Select(e => new ValidationError
            {
                Field = x.Key,
                Message = e.ErrorMessage
            }));
            
        return Failure("Validation failed", errors, 422);
    }
    
    var user = await _userService.CreateUserAsync(request);
    return Created("User created successfully", user);
}

[HttpGet("users")]
[ProducesResponseType(typeof(PagedApiResponse<UserDto>), 200)]
public async Task<IActionResult> GetUsers(
    [FromQuery] int page = 1,
    [FromQuery] int pageSize = 10)
{
    var (users, totalCount) = await _userService.GetUsersAsync(page, pageSize);
    
    return PagedSuccess(
        "Users retrieved successfully",
        users,
        page,
        pageSize,
        totalCount
    );
}

Summary

This standardized response format provides:

  • Consistency: All endpoints return responses in the same structure
  • Type Safety: Strong typing with generics for data payloads
  • Easy Error Handling: Automatic exception throwing on client side
  • Developer Experience: Clear, predictable responses with helpful metadata
  • Debugging: TraceId for correlating requests across services
  • Pagination Support: Built-in pagination metadata for list endpoints
  • Validation: Structured validation error reporting
  • AOT Compatibility: Works with Native AOT compilation in .NET 8+