【问题标题】:Asp.Net WebApi catch exceptions generated in controller constructorAsp.Net WebApi 捕获控制器构造函数中生成的异常
【发布时间】:2018-06-19 00:18:26
【问题描述】:

在我的基本 API 控制器类构造函数中,我根据 Windows 身份验证填充用户权限。我正在使用类似的东西

    var ctx = new PrincipalContext(ContextType.Domain, System.Environment.UserDomainName.ToUpper());
    var userInfo = UserPrincipal.FindByIdentity(ctx, System.Environment.UserName.ToUpper());

从 AD 获取用户信息。问题是当用户被锁定时,应用程序失败并显示 500,因为异常发生在构造函数中。有没有办法处理这些调用的异常并将其作为未经授权的异常或其他东西冒泡?我试图用 try catch 包围它并抛出未经授权的 httpresponse 异常,但它仍然冒泡为 500。

编辑#1

构造函数中发生的事情很少,当它失败时,我不想以 500 失败。我需要捕获异常并抛出其他东西。授权只是其中之一。

编辑#2

致所有写我不应该在构造函数中包含任何逻辑的人:

构造函数用于将对象初始化为有效状态。这部分应用程序的控制器必须有一个数据库连接、用户信息和一些其他属性,因为所有请求都在使用所有这些信息。如果其中任何一个失败,我想向用户返回不同类型的错误。在标准类的情况下,它将是不同类型的异常(SqlException、DbConnectionException、某种 AD 异常)。对于 WebApi,我希望它是不同类型的响应代码(未授权、未实现(501)等)。将相同的代码复制粘贴到应用程序该部分中的每个请求表示维护问题。

【问题讨论】:

  • 在整个应用程序部分的每个方法中复制粘贴相同的代码?这比在构造函数中有这样的逻辑还要糟糕。请阅读问题:无论构造函数中抛出什么类型的异常(即使我将其转换为 400、401 或其他),webapi 都会将其转换为 500。我想知道如何避免它被翻译成 500。就这么简单。

标签: c# asp.net-web-api active-directory windows-authentication


【解决方案1】:

由于原始异常多次被包装到其他异常中,因此到目前为止我发现的唯一方法是拥有一个全局异常过滤器,然后在上下文的内部异常中导航。异常直到它为空或者我没有达到我正在寻找的类型的异常。

namespace SomeWebApi
{
    public class GlobalExceptionHandler : ExceptionHandler
    {
        public override async Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
        {
            if (context.Exception != null)
            {
                Exception filteredException = context.Exception;

                while (
                    (filteredException != null)
                    &&
                    (filteredException.GetType() != typeof(HttpResponseException)
                )
                {
                    filteredException = filteredException.InnerException ;
                }

                if (
                    (filteredException != null)
                    &&
                    (filteredException != context.Exception)
                )
                {
                    var httpResponseException = (HttpResponseException) filteredException;
                    var response = context.Request.CreateErrorResponse(
                        httpResponseException.Response.StatusCode,
                        httpResponseException
                    );

                    context.Result = new ResponseMessageResult(response);
                }
            }
        }
    }
}

接下来我需要在 WebApiConfig.Register 中注册它:

config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());

【讨论】:

    【解决方案2】:

    最简单的方法是不在控制器构造函数中使用用户/会话相关的逻辑。 用于身份验证您可以使用属性过滤器,获取用户信息只需创建帮助程序类并在需要时使用它。

    编辑

    最后一件事:尝试使用依赖注入(如结构映射),它会迫使您在构造函数中进行适当的更改,这将使初始化更容易。 https://www.exceptionnotfound.net/setting-up-dependency-injection-in-web-api-with-structuremap/

    【讨论】:

    • 问题不仅仅是授权。基本控制器中发生的事情很少,当它失败时,我需要抛出 500 以外的东西。
    • 同样的事情。只是不要将请求相关的逻辑放入构造函数中。控制器动作支持请求,构造函数不支持,您不应在控制器中发送请求响应。
    • 是的,在 100 个请求中复制粘贴相同的代码。这是完全安全的,没有危险。
    • 1) 你不明白什么是请求。您不能“在 100 个请求中复制粘贴相同的代码”。您可能谈论的是动作,但我很确定您在一个控制器中没有 100 个动作(即使您在一个控制器中有 100 个动作,那么这是您必须解决的单独问题......)2)有很多方法可以在全局范围内做某事(应用程序启动、模块、处理程序、过滤器等。3)您无法在构造函数中正确返回对请求的响应。如果你有逻辑希望在构造函数中抛出异常,那么你做错了。
    • 请阅读我原来的问题。这是一个由多个其他(不是全部)控制器继承的基本控制器。此类控制器下的所有操作都必须访问特定的事物:广告用户信息、数据库上下文等。初始化这些数据可能会导致不同的异常。由于这是完全相同的数据和逻辑,因此在 100 个不同的端点上将其初始化完全相同,因此将其放置在基类的构造函数中(与 OOP 相对应)。你到目前为止所说的与问题无关。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-07
    • 1970-01-01
    • 1970-01-01
    • 2013-01-20
    • 1970-01-01
    相关资源
    最近更新 更多