для ошибок уже есть RFC 7807, в c# уже есть готовый ProblemDetail
https://datatracker.ietf.org/doc/html/rfc7807
не выявленные ошибки также нужно отлавливать в middlware
вот пример
public class ResponseResult<T> : IResponseResult<T>
{
public bool IsSuccess { get; private set; }
public ProblemDetails? Error { get; private set; }
public T? Data { get; private set; }
public static ResponseResult<T> Success(T data) => new ResponseResult<T> { IsSuccess = true, Data = data };
public static ResponseResult<T> Success() => new ResponseResult<T> { IsSuccess = true};
public static ResponseResult<T> Failure(Exception exception) => new ResponseResult<T>
{
IsSuccess = false,
Error = new ProblemDetails
{
Title = GetTitleByException(exception),
Detail = exception.Message,
Status = exception switch
{
ArgumentException => StatusCodes.Status400BadRequest,
KeyNotFoundException => StatusCodes.Status404NotFound,
UnauthorizedAccessException => StatusCodes.Status401Unauthorized,
_ => StatusCodes.Status500InternalServerError
},
Instance = Guid.NewGuid().ToString()
}
};
private static string GetTitleByException(Exception exception) =>
exception switch
{
ArgumentException => "Invalid argument",
KeyNotFoundException => "Resource not found",
UnauthorizedAccessException => "Unauthorized",
_ => "Internal server error"
};
}
Слой бизнес логики
return ResponseResult<bool>.Failure(
new KeyNotFoundException(
string.Format("User with login '{0}' not found", request.Login))
);
Презентационный слой
[HttpGet("verify")]
public async Task<ActionResult> VerifyUser(
[FromQuery] string login,
[FromQuery] string password,
CancellationToken cancellationToken)
{
var result = await mediator.Send(new VerifyQuery() { Login = login, Password = password }, cancellationToken);
if (result.IsSuccess)
{
return Ok(new { verify = result.Data });
}
return StatusCode(result.Error?.Status ?? 400, result.Error);
}