从 ValidationAttribute 更改响应状态代码

Change Response Status Code from ValidationAttribute

在 aspnet 核心 web api 项目中我有逻辑

 if(!_entityService.Exists(entityId))
    return NotFound($"{entityId} not found.");

分散在许多 endpoints/controllers 中。我想将这个横切关注点重构为一个属性,例如 ValidationAttribute:

class EntityExistsAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        var entityId = (int)value;

        var entityService = validationContext.GetRequiredService<EntityService>();

        if(entityService.Exists(entityId))
            return ValidationResult.Success;

        var httpContext = validationContext.GetRequiredService<IHttpContextAccessor>().HttpContext;

        httpContext.Response.StatusCode = StatusCodes.Status404NotFound;

        return new ValidationResult($"{entityId} not found.");
    }
}

尝试在控制器中应用:

    class EntitiesController
    {
        [HttpGet("{id:int}")]
        public Entity Get([FromRoute] [EntityExists] int id)
        {
            // no need to clutter here with the 404 anymore
        }
    }

虽然这似乎不起作用 - 端点仍将 return 400 错误请求。

possible/advised 在 asp.net 核心网络 api 中做这样的事情吗?我考虑过使用 middleware/action 过滤器,但也看不出如何通过这些过滤器实现上述目标。

您可以像下面这样自定义 ActionFilter 属性:

public class CustomActionFilter:ActionFilterAttribute
{
    
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var entityId = int.Parse(context.HttpContext.Request.RouteValues["id"].ToString());

        var entityService = context.HttpContext.RequestServices.GetService<EntityService>();
        if (!entityService.Exists(entityId))
        {
            context.HttpContext.Response.StatusCode = StatusCodes.Status404NotFound;
            context.Result = new NotFoundObjectResult($"{entityId} not found.");

        }
        base.OnActionExecuting(context);
    }
}

控制器:

[HttpGet("{id:int}")]
[CustomActionFilter]
public void Get([FromRoute] int id)
{
    // no need to clutter here with the 404 anymore
}

结果: