graphql-dotnet: How to properly use "UnhandledExceptionDelegate" in GraphQLMiddleware "ExecuteAsync" class?

I am working on investigating a weird issue which is happening in below code. I am seeing NRE being thrown where HttpContext is null.

I have below ExecuteAsync code in my GraphQL middleware class -

private async Task ExecuteAsync(HttpContext context, ISchema schema)
{
    var loggingService = context.RequestServices.GetService<ILoggingService>();
    var loggingContext = new LoggingContext(context.Request.Headers);
    context.Items["logRequest"] = new RequestInfo();
    using (var logTimer = new LogTimer(loggingService, loggingContext, "Debug Logs", GetEndpoint(context), "start"))
    {
        try
        {
            var request = Deserialize<GraphQLRequest>(context.Request.Body);
            var result = await _executer.ExecuteAsync(opts =>
            {
                opts.Schema = schema;
                opts.Query = request?.Query;
                opts.OperationName = request?.OperationName;
                opts.Inputs = request?.Variables.ToInputs();
                opts.RequestServices = context.RequestServices;
                opts.UserContext = _settings.BuildUserContext?.Invoke(context);
                opts.ValidationRules = DocumentValidator.CoreRules.Concat(new[] { new InputValidationRule() });
                opts.EnableMetrics = _settings.EnableMetrics;
                if (_settings.EnableMetrics)
                {
                    opts.FieldMiddleware.Use<InstrumentFieldsMiddleware>();
                }
                opts.UnhandledExceptionDelegate = async ctx =>
                {
                    // called from here when that NRE exception happens.
                    await HandleErrors(context, ctx.Exception, loggingService, loggingContext);
                };
            });
            await WriteResponseAsync(context, result);
            logTimer.StatusCode = context.Response.StatusCode;
            logTimer.Message = ((RequestInfo)context.Items["logRequest"]).Value;
        }
        catch (Exception ex)
        {
            await HandleErrors(context, ex, loggingService, loggingContext);
            logTimer.StatusCode = context.Response.StatusCode;
            logTimer.Message = ((RequestInfo)context.Items["logRequest"]).Value;
        }
    }
}

And below is my HandleErrors method -

private async Task HandleErrors(HttpContext context, Exception ex, ILoggingService loggingService, LoggingContext loggingContext)
{
    if (ex == null)
    {
        // log here
        return;
    }
    int statusCode;
    var title = "GraphQLMiddleware";
    var error = ex.Source;
    var message = ex.Message;
    var stackTrace = ex.StackTrace;
    string eventName = null;
    switch (ex)
    {
        // bunch of cases here
        default:
            message = $"Exception handling: {ex.Message}";
            statusCode = StatusCodes.Status500InternalServerError;
            eventName = "Default Exception";
            break;
    }
    // log error here
    // now this below line throws exception as "System.NullReferenceException" and I am confuse why?
    context.Response.StatusCode = statusCode;
}

Whenever below line is called in ExecuteAsync method and then it calls HandleErrors method which throws exception at context.Response.StatusCode = statusCode where I am confuse why it could do that?

opts.UnhandledExceptionDelegate = async ctx =>
{
    // called from here when that exception happens.
    await HandleErrors(context, ctx.Exception, loggingService, loggingContext);
};

Is it because it is synchronous and had to be done that way? I see GQL doesn’t support async for “UnhandledExceptionDelegate”

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 23 (16 by maintainers)

Most upvoted comments

That answers my question. Thanks @Shane32. Appreciate your help!

Thanks @Shane32. So I am gonna go with below code then by removing async/await altogether-

opts.UnhandledExceptionDelegate = ctx =>
{
    HandleErrors(context, ctx.Exception, loggingService, loggingContext);
};

HandleError method -

private void HandleErrors(HttpContext context, Exception ex, LoggingContext loggingContext)
{
    // no async code in this method!!
    if (ex == null)
    {
        // log here normally? I am anyway logging everything synchronous
        return;
    }

    switch (ex)
    {
        // bunch of cases here
        default:
            message = $"Exception handling: {ex.Message}";
            statusCode = StatusCodes.Status500InternalServerError;
            eventName = "Default Exception";
            break;
    }
    // log error here
    // now this below line throws exception as "System.NullReferenceException" and I am confuse why?
    context.Response.StatusCode = statusCode;
}

I opened a new issue for the feature request to allow async unhandled exception delegate. I suggest closing this issue at your leisure.