0

I have an application I would like to add middleware error handling to but the exception never seem to bubble up. I've read several articles about this having to do with async behavior but I can't see what I'm doing wrong.

For example this SO post (Exceptions not bubbling up to Error Handling Middleware?) is very similar but I already have async as that is how it was originally written before we added the middleware error handling.

I'll post what I think is relevant.

ExceptionMiddleware.cs:

public async Task Invoke(HttpContext context)
{
  try
  {
     await _next(context);
  }
  catch (Exception ex)
  {
     //we never get here????
     await HandleExceptionAsync(context, ex, _options);
  }
}

APIService:

public async Task<Response<PaginationModel>> GetPagination(int result, int pageNumber,...)
{
     _logger.GetPaginationInformation($"Enter with parameters result: {result},.....");

     try
     {
        ....do stuff
     }
     catch (Exception ex)
     {
        //we do get here
        throw;   //return CreateErrorMessage<PaginationModel>("GetPagination", ex);
     }
 }

Startup.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
        app.UseABCExceptionHandler(options => options.AddErrorDetails = FormatExceptionResponse);
        app.UseCors();
        

        // add http for Schema at default url /graphql
        app.UseWebSockets();
        app.UseGraphQLWebSockets<ISchema>();
        app.UseGraphQL<ISchema>("/graphql");
        app.UseGraphQLPlayground();
}


    private void FormatExceptionResponse(HttpContext context, Exception exception, Response<PaginationModel> response)
    {
        response.message = exception.Message;
    }

ExceptionMiddlewareExtensions.cs:

public static class ExceptionMiddlewareExtensions
{
    public static IApplicationBuilder UseABCExceptionHandler(this IApplicationBuilder builder)
    {
        var options = new ExceptionOptions();
        return builder.UseMiddleware<ExceptionMiddleware>(options);
    }

    public static IApplicationBuilder UseABCExceptionHandler(this IApplicationBuilder builder, Action<ExceptionOptions> configureOptions)
    {
        var options = new ExceptionOptions();
        configureOptions(options);
        return builder.UseMiddleware<ExceptionMiddleware>(options);
    }

}

I set debug breakpoints and everything seems to register and all "hooks" seem to be set and execution flows as expected first through ExceptionMiddleware Invoke _next(context) then to ApiService GetPagination but even if I throw a hard exception or remove the try catch block in GetPagination it never flows back up to Invoke catch?

I'm sure this has something to do with lack of understanding how to handle globally with async Task but I follow the articles on it and it doesn't seem to matter??

Update

Based upon the comment from Andy I'm adding this information, it could be helpful.

GetPagination is NOT an API endpoint. It is a service class called by the GraphQL query.

GraphQL Query:

FieldAsync<Response...>(
  "PaginationSearch",
  "Returns paginated for specified search terms",
  arguments: new QueryArguments(... { Name = "result" },

  resolve: async context =>
  {
    var result = context.GetArgument<int>("result");

    //Is this using statement introducing some unexpected behavior as it Disposes behind the scenes??
    using (_logger.GetScope("PaginationSearch"))
    {
       return Service.GetPagination(result...);
    }
  }
);

Update 2

Moving the registration line to the bottom of the configure method as mentioned in the comments actually makes it so it doesn't flow through the middleware Invoke BUT moving it to the first line does.

To be clear at project start BOTH first line and last line it does flow through invoke. I'm specifically referring to execution when a graphql query is received.

When startup.cs has registration last line Middleware Invoke is not used

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
   .....
   app.UseABCExceptionHandler(options => options.AddErrorDetails = FormatExceptionResponse);
}

ExceptionMiddleware.cs:

{
  try
  {
     await _next(context); //breakpoint is NOT hit when request received
  }
  catch (Exception ex)
  {
     await HandleExceptionAsync(context, ex, _options);
  }
}

When startup.cs has registration line first Middleware Invoke is used

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
   app.UseABCExceptionHandler(options => options.AddErrorDetails = FormatExceptionResponse);
.....
}

ExceptionMiddleware.cs:

{
  try
  {
     await _next(context); //breakpoint **IS** hit when request received
  }
  catch (Exception ex)
  {
     await HandleExceptionAsync(context, ex, _options);
  }
}

Also not sure if it matters but the API service that is registerd in startup.cs is a singleton.

services.AddSingleton<IAPIService, APIService>();

and the shared HTTP client (using HTTP Typed clients) is added to the services httpclient collection.

services.AddHttpClient<ISecurityClient, SecurityClient>();
7
  • 1
    Can you show your Startup.cs code? Specifically the entire Configure method? Commented May 7, 2021 at 17:11
  • Updated also included a helper method that is referenced Commented May 8, 2021 at 13:18
  • Move UseABCExceptionHandler to the end of the method after the line app.UseGraphQLPlayground(); Commented May 8, 2021 at 15:39
  • wait a minute -- where are the UseRouting and UseEndpoints calls? Is GetPagination not a WebAPI endpoint? Commented May 8, 2021 at 15:40
  • @Andy GraphQL does not do things that way. Really isn't a web API in the traditional sense. Commented May 8, 2021 at 16:06

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.