Задать вопрос
@OwDafuq

Как лучше всего возвращать ответы?

Использую библиотеку FluentResults.
Допустим, что есть 1 ValueObject:
public class Address
{
    private Address()
    {

    }

    public string Value { get; private init; }

    public static Result<Address> Create(string address)
    {
        if (string.IsNullOrWhiteSpace(address))
        {
            return Result.Fail("Адрес не может быть пустым");
        }

        return new Address { Value = address };
    }
}


В контроллер приходит такой запрос:
public class CreateRequest
{
    public string FirstAddress { get; set; }
    public string SecondAddress { get; set; }
}


Необходимо сделать из 2-х свойств в запросе 2 ValueObject'a Address, но нужно как-то клиенту отдать 400 bad request, если есть ошибки при создании ValueObject'ов.
Но как указывать, что ошибка относится к первому или второму свойству в запросе?

Как вообще лучше отдавать ошибки? Сразу при создании ValueObject отдавать текст ошибки, или отдавать коды, а потом, на UI слое (в методе контроллера) их заменять на текст?

Нигде не могу найти похожих примеров
  • Вопрос задан
  • 66 просмотров
Подписаться 1 Простой Комментировать
Пригласить эксперта
Ответы на вопрос 1
@mxelgin
для ошибок уже есть 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);
        }
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы