ApiController 并不总是在 BadRequest 中返回数据

问题描述 投票:0回答:6

我正在使用 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"
}
c# asp.net-core asp.net-core-mvc asp.net-core-2.2
6个回答
13
投票

无需返回

BadRequest()
结果,您只需返回
ValidationProblem()
结果即可。这将为您提供包含当前 ModelState 错误的 ProblemDetails 对象响应。


10
投票

默认

ControllerBase.BadRequest()
响应内容是因为您已将
[ApiController]
属性应用于您的控制器。这是在线记录

当兼容版本为2.2或更高版本时,MVC将错误结果(状态码为400或更高的结果)转换为带有

ProblemDetails
的结果。
ProblemDetails
类型基于 RFC 7807 规范,用于在 HTTP 响应中提供机器可读的错误详细信息。

这包括

type
title
status
traceId
值。

如果您没有应用

[ApiController]
,那么
ControllerBase.BadRequest()
将返回带有
HTTP 400
状态代码的空响应。

所有接受

ControllerBase.BadRequest
value
响应对象的
model
重载将序列化并返回,而不是使用
ApiController
默认响应。


6
投票

对于

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);
        }
    }
    

2
投票

对我有用

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

0
投票

不想用的人

ValidationProblem()
.

只需使用ControllerBase.Problem

例子:

public IActionResult Post()
{
    // ...

    return Problem("msg", statusCode: (int)HttpStatusCode.BadRequest);
}

哪些输出

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

-2
投票

尝试使用 JsonConvert 序列化您的错误请求。

return BadRequest(JsonConvert.Serialize(new List { “Code is invalid” }));

© www.soinside.com 2019 - 2024. All rights reserved.