ApiController не всегда возвращает данные в BadRequest - PullRequest
2 голосов
/ 03 мая 2019

Я использую ASP.NET Core 2.2 ApiController, и у меня есть следующее:

[ApiController]
public class PostController : Controller {
  [HttpGet("posts")]
  public async Task<IActionResult> Get() {
    return BadRequest();
  }
}

В этом случае я получаю следующий ответ:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "Bad Request",
  "status": 400,
  "traceId": "0HLMFSL0C7SKB:00000001"
}

Но если я вернусьнекоторые данные следующим образом:

[ApiController]
public class PostController : Controller {
  [HttpGet("posts")]
  public async Task<IActionResult> Get() {
    List<String> errors = new List<String> { "Code is invalid" };
    return BadRequest(new { errors = errors });
  }
}

Я получаю следующее:

{
  "errors": ["Code is invalid"]
}

Почему ApiController добавляет type, title, status и traceId, когда содержимое не возвращается?

Я бы хотел, чтобы ответы всегда были похожи:

{
  "errors": ["Code is invalid"],
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "Bad Request",
  "status": 400,
  "traceId": "0HLMFSL0C7SKB:00000001"
}

Ответы [ 3 ]

1 голос
/ 06 мая 2019

Для ProblemDetails это зависит от того, наследуется ли ObjectResult от IClientErrorActionResult.

Для обходного пути вы можете выполнить следующие шаги:

  1. MyBadRequestObjectResult

    public class MyBadRequestObjectResult : BadRequestObjectResult, IClientErrorActionResult
    {
        public MyBadRequestObjectResult() : base((object)null)
        {
        }
    
        public MyBadRequestObjectResult(object error) : base(error)
        {
        }
    }
    
  2. Пользов. ProblemDetailsErrorFactory

    public class ProblemDetailsErrorFactory: IClientErrorFactory
    {
        private static readonly string TraceIdentifierKey = "traceId";
        private static readonly string ErrorsKey = "errors";
        private readonly ApiBehaviorOptions _options;
    
        public ProblemDetailsErrorFactory(IOptions<ApiBehaviorOptions> options)
        {
            _options = options?.Value ?? throw new ArgumentNullException(nameof(options));
        }
    
        public IActionResult GetClientError(ActionContext actionContext, IClientErrorActionResult clientError)
        {
            var problemDetails = new ProblemDetails
            {
                Status = clientError.StatusCode,
                Type = "about:blank",
            };
    
            if (clientError.StatusCode is int statusCode &&
                _options.ClientErrorMapping.TryGetValue(statusCode, out var errorData))
            {
                problemDetails.Title = errorData.Title;
                problemDetails.Type = errorData.Link;
                SetErrors(actionContext, problemDetails);
                SetTraceId(actionContext, problemDetails);
            }
    
            return new ObjectResult(problemDetails)
            {
                StatusCode = problemDetails.Status,
                ContentTypes =
                {
                    "application/problem+json",
                    "application/problem+xml",
                },
            };
        }
        internal static void SetErrors(ActionContext actionContext, ProblemDetails problemDetails)
        {
            if (actionContext is ResultExecutingContext resultExecutingContext)
            {
                if (resultExecutingContext.Result is BadRequestObjectResult result)
                {
                    problemDetails.Extensions[ErrorsKey] = result.Value;
                }
            }
            //var errors = actionContext.HttpContext.
        }
        internal static void SetTraceId(ActionContext actionContext, ProblemDetails problemDetails)
        {
            var traceId = Activity.Current?.Id ?? actionContext.HttpContext.TraceIdentifier;
            problemDetails.Extensions[TraceIdentifierKey] = traceId;
        }
    
    }
    
  3. Регистрация ProblemDetailsErrorFactory

    public void ConfigureServices(IServiceCollection services)
    {
        services.TryAddSingleton<IClientErrorFactory, ProblemDetailsErrorFactory>();
    
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }
    
  4. Действие контроллера

    [ApiController]
    public class PostController : Controller
    {
        [HttpGet("posts")]
        public IActionResult Get()
        {
            return new MyBadRequestObjectResult();
        }
        [HttpGet("posts1")]
        public IActionResult Get1()
        {
            List<String> errors = new List<String> { "Code is invalid" };
            return new MyBadRequestObjectResult(errors);
        }
    }
    
1 голос
/ 04 мая 2019

Содержание ответа ControllerBase.BadRequest() по умолчанию связано с тем, что к контроллеру применен атрибут [ApiController]. Это задокументировано онлайн:

https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-2.2#problem-details-for-error-status-codes

Если версия совместимости 2.2 или более поздняя, ​​MVC преобразует результат ошибки (результат с кодом состояния 400 или выше) в результат с ProblemDetails. Тип ProblemDetails основан на спецификации RFC 7807 для предоставления машиночитаемых сведений об ошибках в ответе HTTP.

Сюда входят значения type, title, status and traceId`: https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-2.2#problem-details-for-error-status-codes

Если вы не применили [ApiController], то ControllerBase.BadRequest() вернет пустой ответ с кодом состояния HTTP 400.

Все перегрузки ControllerBase.BadRequest, которые принимают объект ответа value или model, будут сериализованы и вернут его вместо использования ответа ApiController по умолчанию.

0 голосов
/ 04 мая 2019

Попробуйте сериализовать неверный запрос с помощью JsonConvert.

return BadRequest (JsonConvert.Serialize (новый список {«Код недействителен»}));

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...