【问题标题】:Catching errors from services in Exception Filter在异常过滤器中从服务中捕获错误
【发布时间】:2021-07-21 08:43:56
【问题描述】:

我创建了一个在我的整个 aspnet 项目中使用的服务,用于检索和验证标头以及其他内容。问题是异常过滤器无法捕获服务抛出的错误,因为它不在异常过滤器的范围内,从而给用户一个丑陋的内部服务器错误。有什么方法可以优雅地向使用服务的用户返回带有描述的参数错误?

初创公司:

services.AddScoped<UserService>();
services
    .AddMvc(x =>
    {
        x.Filters.Add(typeof(Filters.MyExceptionFilter));
    })

异常过滤器:

public class MyExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        if (context.Exception is ArgumentException argumentException)
        {
            var response = context.HttpContext.Response;
            context.ExceptionHandled = true;
            response.StatusCode = 400;
            context.Result = new ObjectResult(argumentException.Message);
            return;
        }
    }
}

服务:

public class UserService
{
    public readonly string UserId;

    public UserService(IHttpContextAccessor context)
    {
        if (!context.HttpContext.Request.Headers.TryGetValue("x-test", out var user))
        {
            throw new ArgumentException($"x-test header is required.");
        }
        UserId = user;
        //do other stuff
    }
}

控制器动作方法:

public async Task<IActionResult> Delete(string id, 
    [FromServices] UserService userService)
{
    //do stuff
}

【问题讨论】:

    标签: c# asp.net asp.net-core action-filter


    【解决方案1】:

    C# 中的一条经验法则是在构造函数中做的工作尽可能少。这有几个很好的理由(例如,您不能处理在其构造函数中引发异常的类)。另一个很好的理由是,正如您所发现的,构造可能发生在与您实际使用该类的地方不同的地方(例如 DI 容器)。

    修复应该非常简单——只需将逻辑移出构造函数即可。例如,您可以使用Lazy&lt;T&gt; 来执行此操作:

    public class UserService
    {
        public readonly Lazy<string> _userId ;
    
        public UserService(IHttpContextAccessor context)
        {
            _userId = new Lazy<string>(() => 
            {
                if (!context.HttpContext.Request.Headers.TryGetValue("x-test", out var user))
                {
                    throw new ArgumentException($"x-test header is required.");
                }
                
                return user;
            });
    
            //do other stuff
        }
    
        public string UserId => _userId.Value;
    }
    

    或者您可以在需要时获取价值:

    public class UserService
    {
        public readonly IHttpContextAccessor _context;
    
        public UserService(IHttpContextAccessor context)
        {
            _context = context;
            //do other stuff
        }
    
        public string UserId
        {
            get
            {
                if (_context.HttpContext.Request.Headers.TryGetValue("x-test", out var user))
                {
                    return user;
                }
                else
                {
                    throw new ArgumentException($"x-test header is required.");
                }
            }
        }
    }
    

    【讨论】:

    • 谢谢。我在私有字段中采用了惰性方法,并使用带有get 的属性进行检索(因此字符串插值不会通过隐式强制转换产生任何有趣的结果)。最后,我还将验证移到了一个单独的方法中,该方法在 Controller 的构造函数中调用,以防任何其他服务最终使用 UserService。
    猜你喜欢
    • 1970-01-01
    • 2020-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多