ASP.Net WebAPI 中无法捕获的 InternalServerError

Uncatchable InternalServerError in ASP.Net WebAPI

我正在使用 ASP.Net 4.7.2 和 ASP.Net Core 3.1 Web 应用程序作为客户端开发 WebAPI。 WebAPI 托管在我的本地 IIS 上。我还在使用 windows 表单应用程序测试 WebAPI。

在一个操作方法中,我得到一个 InternalServerError 和唯一的消息 "Error"。错误在调用后显示在客户端的 HttpResponseMessage 中,但既不在 WebAPI 操作中也不在全局异常过滤器中。尽管我在各处都设置了断点,但我无法调试它。在行动中我得到了正确的结果。

控制器:

public class LogController : ApiController
{
    private readonly ILog _logSrv;

    public LogController( ILog logSrv )
    {
        _logSrv = logSrv;
    }

    public async Task<IHttpActionResult> Get( [FromUri] LogFilterModel model )
    {
        await LogAsync( GetCurrentUserLoginId(), "Log", "Get", $"Reading log data." );

        var logs = _logSrv.Get(
            model.UserLoginId, 
            DateTime.Parse( model.DateTimeFrom ), DateTime.Parse( model.DateTimeUntil ),
            model.PageNumber, model.PageSize );

        var result = logs
            .Select( l => new LogModel
            {
                Id = l.Id,
                Message = l.Message,
                LoginId = l.LoginId,
                Username = l.Username,
                EventDate = l.EventDate,
                EventType = (LogEventType)l.EventType
            } );

        // testing result data
        var list = result.ToList();

        return Ok( result );
    }
}

服务方式:

public IEnumerable<ExtLog> Get( string[] roles,
    long? loginId, DateTime datetimeFrom, DateTime datetimeUntil,
    int pageNumber, int pageSize )
{
    if ( !roles.Contains( Constants.RoleAdmin ) ) return null;

    // preparing parameters
    // ...
    // and reading data through a stored procedure
    return _context.Database.SqlQuery<ExtLog>( 
    // ...
    );
}

来自(windows 表单)客户端的调用:

private async void ReadLogData()
{
    var filter = new LogFilterModel
    {
        UserLoginId = 3,
        DateTimeFrom = DateTime.Now.AddDays( -3 ).ToShortDateString(),
        DateTimeUntil = DateTime.Now.AddDays( 1 ).ToShortDateString(),
        PageNumber = 2,
        PageSize = 20
    };

    try
    {
        var client = GetHttpClient();

        string call = BaseURL + "/api/Log/Get" +
            $"?UserLoginId={filter.UserLoginId}&" +
            $"DateTimeFrom={filter.DateTimeFrom:d}&DateTimeUntil={filter.DateTimeUntil:d}&" +
            $"PageNumber={filter.PageNumber}&PageSize={filter.PageSize}";

        HttpResponseMessage msg = await client.GetAsync( call );

        if ( msg.IsSuccessStatusCode )
        {
            tbLog.Text = msg.Content.ReadAsStringAsync().Result;
        }
        else
        {
            // error
            if ( msg.Content != null && msg.Content.Headers.ContentLength > 0 )
            {
                try
                {
                    using ( var cs = await msg.Content.ReadAsStreamAsync() )
                    {
                        var vt = await JsonSerializer.DeserializeAsync<JsonElement>( cs );
                        if ( vt.TryGetProperty( "Message", out JsonElement jValue ) )
                            tbLog.Text = jValue.GetString();
                    }
                }
                catch { }
            }
        }
    }
    catch ( Exception ex )
    {
        tbLog.Text = ex.ToString();
    }
}

在客户端我得到 InternalServerError 但在 WebAPI 中不会出现异常。我最终将其添加到 WebAPI 配置中:

GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;

并且在操作方法和全局错误处理程序中都有断点:

public class GlobalErrorHandlingFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException( HttpActionExecutedContext context )
    {
    //......
    }
}

这个神秘的错误是从哪里来的,或者我该如何调试它?

现在我找到了问题的原因。对于遇到同样奇怪的不可调试错误的任何人。

我用

读出了客户端中的HttpResponseMessage
var result = msg.Content.ReadAsStringAsync().Result

终于可以阅读异常详细信息了:

"{"Message":"Error",
"ExceptionMessage":
"ExceptionType":"System.InvalidOperationException",
"StackTrace":null,
"InnerException":{
"Message":"Error",
"ExceptionMessage":"The SqlParameter is already contained by another SqlParameterCollection.",
"ExceptionType":"System.ArgumentException",

问题是我在控制器中进行了额外的测试调用:

// testing result data
var list = result.ToList();

意思是调用了两次服务中带sql参数和存储过程的方法,第二次使用了sql参数,这显然是不允许的。我还可以检测到对 SQL Server Profiler 的第二次调用。删除这一行问题就解决了。

但奇怪的是这个异常在网络上的任何地方都没有出现 api 并且不可调试。我不确定这是否是 .NET Framework 中的错误。

更新:

我发现,作为 FilterAttribute 的全局错误处理程序不够全局。 ExceptionLogger 或 ExceptionHandler 应该用于 Web API's (v2),如下所述: https://docs.microsoft.com/en-us/aspnet/web-api/overview/error-handling/web-api-global-error-handling