# API Design - Detailed Reference
This reference contains detailed code examples for RESTful API design patterns in ASP.NET Core.
## Standard Response Format / Định Dạng Response Chuẩn
### ApiResponse Wrapper
```csharp
///
/// EN: Standard API response wrapper.
/// VI: Wrapper response API chuẩn.
///
public class ApiResponse
{
public bool Success { get; set; }
public T? Data { get; set; }
public string? Error { get; set; }
public PaginationInfo? Pagination { get; set; }
public MetadataInfo? Metadata { get; set; }
}
public record PaginationInfo(
int Page,
int Limit,
int Total,
int TotalPages);
public record MetadataInfo(
string Timestamp,
string Version,
string RequestId);
// EN: Success response example / VI: Ví dụ response thành công
// {
// "success": true,
// "data": { "id": "123", "email": "user@example.com" },
// "pagination": { "page": 1, "limit": 10, "total": 100, "totalPages": 10 }
// }
// EN: Error response example / VI: Ví dụ response lỗi
// {
// "success": false,
// "error": "User not found"
// }
```
## DTOs (Data Transfer Objects)
### Request DTOs with Records
```csharp
///
/// EN: DTO for creating a user.
/// VI: DTO để tạo user.
///
public record CreateUserRequest(
[Required]
[EmailAddress]
string Email,
[Required]
[MinLength(6)]
string Password,
string? Name);
///
/// EN: DTO for updating a user.
/// VI: DTO để cập nhật user.
///
public record UpdateUserRequest(
[EmailAddress]
string? Email,
string? Name,
string? Avatar);
///
/// EN: Query parameters for listing users.
/// VI: Query parameters để list users.
///
public record GetUsersQuery(
int Skip = 0,
int Take = 20,
string? Search = null,
string SortBy = "CreatedAt",
string Order = "desc");
```
### Response DTOs
```csharp
///
/// EN: DTO for user information.
/// VI: DTO cho thông tin user.
///
public record UserDto(
Guid Id,
string Email,
string? Name,
string? Avatar,
string Role,
DateTime CreatedAt,
DateTime UpdatedAt);
///
/// EN: Result for paginated user list.
/// VI: Kết quả danh sách user có phân trang.
///
public record UsersListResult(
IReadOnlyList Users,
int TotalCount);
///
/// EN: Mapper from domain entities to DTOs.
/// VI: Mapper từ domain entities sang DTOs.
///
public static class UserDtoMapper
{
public static UserDto ToDto(this User user) => new(
user.Id,
user.Email,
user.Name,
user.Avatar,
user.Role.ToString(),
user.CreatedAt,
user.UpdatedAt);
}
```
## Controller Implementation / Triển Khai Controller
### Complete Controller Example
```csharp
///
/// EN: Controller for user management.
/// VI: Controller quản lý user.
///
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/users")]
[SwaggerTag("User Management - Create, read, update, and delete users")]
public class UsersController : ControllerBase
{
private readonly IMediator _mediator;
private readonly ILogger _logger;
public UsersController(IMediator mediator, ILogger logger)
{
_mediator = mediator;
_logger = logger;
}
///
/// EN: Get list of users with pagination.
/// VI: Lấy danh sách users với phân trang.
///
[HttpGet]
[Authorize(Roles = "Admin")]
[SwaggerOperation(Summary = "List users", Description = "Get paginated list of users")]
[SwaggerResponse(200, "Users retrieved successfully")]
[SwaggerResponse(401, "Unauthorized")]
[SwaggerResponse(403, "Forbidden - Admin role required")]
public async Task>> GetUsers(
[FromQuery] int skip = 0,
[FromQuery] int take = 20,
[FromQuery] string? search = null,
CancellationToken cancellationToken = default)
{
var query = new GetUsersQuery(skip, take, search);
var result = await _mediator.Send(query, cancellationToken);
return Ok(new ApiResponse
{
Success = true,
Data = result,
Pagination = new PaginationInfo(
Page: skip / take + 1,
Limit: take,
Total: result.TotalCount,
TotalPages: (int)Math.Ceiling(result.TotalCount / (double)take))
});
}
///
/// EN: Get user by ID.
/// VI: Lấy user theo ID.
///
[HttpGet("{userId:guid}")]
[Authorize]
[SwaggerOperation(Summary = "Get user by ID")]
[SwaggerResponse(200, "User retrieved successfully")]
[SwaggerResponse(404, "User not found")]
public async Task>> GetUser(
Guid userId,
CancellationToken cancellationToken = default)
{
var query = new GetUserByIdQuery(userId);
var result = await _mediator.Send(query, cancellationToken);
if (result == null)
return NotFound(new ApiResponse
{
Success = false,
Error = "User not found"
});
return Ok(new ApiResponse { Success = true, Data = result });
}
///
/// EN: Create a new user.
/// VI: Tạo user mới.
///
[HttpPost]
[SwaggerOperation(Summary = "Create user")]
[SwaggerResponse(201, "User created successfully")]
[SwaggerResponse(400, "Invalid request")]
[SwaggerResponse(409, "User already exists")]
public async Task>> CreateUser(
[FromBody] CreateUserRequest request,
CancellationToken cancellationToken = default)
{
var command = new CreateUserCommand(request.Email, request.Password, request.Name);
var result = await _mediator.Send(command, cancellationToken);
if (!result.Success)
return BadRequest(new ApiResponse
{
Success = false,
Error = result.Error
});
return CreatedAtAction(
nameof(GetUser),
new { userId = result.User!.Id },
new ApiResponse { Success = true, Data = result.User });
}
///
/// EN: Update user by ID.
/// VI: Cập nhật user theo ID.
///
[HttpPut("{userId:guid}")]
[Authorize]
[SwaggerOperation(Summary = "Update user")]
[SwaggerResponse(200, "User updated successfully")]
[SwaggerResponse(400, "Invalid request")]
[SwaggerResponse(404, "User not found")]
public async Task>> UpdateUser(
Guid userId,
[FromBody] UpdateUserRequest request,
CancellationToken cancellationToken = default)
{
// EN: Check if user is updating their own profile or is admin
// VI: Kiểm tra user có đang cập nhật profile của mình hoặc là admin
var currentUserId = GetUserId();
if (currentUserId != userId.ToString() && !User.IsInRole("Admin"))
return Forbid();
var command = new UpdateUserCommand(userId, request.Email, request.Name, request.Avatar);
var result = await _mediator.Send(command, cancellationToken);
if (!result.Success)
return NotFound(new ApiResponse
{
Success = false,
Error = result.Error
});
return Ok(new ApiResponse { Success = true, Data = result.User });
}
///
/// EN: Delete user by ID.
/// VI: Xóa user theo ID.
///
[HttpDelete("{userId:guid}")]
[Authorize(Roles = "Admin")]
[SwaggerOperation(Summary = "Delete user")]
[SwaggerResponse(204, "User deleted successfully")]
[SwaggerResponse(404, "User not found")]
public async Task DeleteUser(
Guid userId,
CancellationToken cancellationToken = default)
{
var command = new DeleteUserCommand(userId);
var result = await _mediator.Send(command, cancellationToken);
if (!result.Success)
return NotFound(new ApiResponse