EntityGraphQL / EntityGraphQL

A GraphQL library for .NET

Home Page:https://entitygraphql.github.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Improve error handling to allow users to make a decision on the type of error

soilidokay opened this issue · comments

I want to handle some of my own exceptions, but I can't do it yet, it seems like the system's try-catch feature is preventing it.
I found it in the source code

 private async Task<QueryResult> DoExecuteRequestAsync(QueryRequest gql, TContextType? overwriteContext, IServiceProvider? serviceProvider, ClaimsPrincipal? user, ExecutionOptions? options)
 {
     try
     {
         if (options == null)
         {
             options = new ExecutionOptions();
         }

         GraphQLDocument graphQLDocument = null;
         if (options!.EnablePersistedQueries)
         {
             PersistedQueryExtension persistedQueryExtension = (PersistedQueryExtension)ExpressionUtil.ChangeType(gql.Extensions.GetValueOrDefault("persistedQuery"), typeof(PersistedQueryExtension), null);
             if (persistedQueryExtension != null && persistedQueryExtension.Version != 1)
             {
                 throw new EntityGraphQLExecutionException("PersistedQueryNotSupported");
             }

             string text = persistedQueryExtension?.Sha256Hash;
             if (text == null && gql.Query == null)
             {
                 throw new EntityGraphQLExecutionException("Please provide a persisted query hash or a query string");
             }

             if (text != null)
             {
                 graphQLDocument = queryCache.GetCompiledQueryWithHash(text);
                 if (graphQLDocument == null && gql.Query == null)
                 {
                     throw new EntityGraphQLExecutionException("PersistedQueryNotFound");
                 }

                 if (graphQLDocument == null)
                 {
                     graphQLDocument = graphQLCompiler.Compile(gql, new QueryRequestContext(AuthorizationService, user));
                     queryCache.AddCompiledQuery(text, graphQLDocument);
                 }
             }
             else if (graphQLDocument == null)
             {
                 graphQLDocument = ((!options!.EnableQueryCache) ? graphQLCompiler.Compile(gql, new QueryRequestContext(AuthorizationService, user)) : CompileQueryWithCache(gql, user));
             }
         }
         else if (options!.EnableQueryCache)
         {
             graphQLDocument = CompileQueryWithCache(gql, user);
         }
         else
         {
             if (gql.Query == null)
             {
                 if (((PersistedQueryExtension)ExpressionUtil.ChangeType(gql.Extensions.GetValueOrDefault("persistedQuery"), typeof(PersistedQueryExtension), null))?.Sha256Hash != null)
                 {
                     throw new EntityGraphQLExecutionException("PersistedQueryNotSupported");
                 }

                 throw new ArgumentNullException("Query", "Query must be set unless you are using persisted queries");
             }

             graphQLDocument = graphQLCompiler.Compile(gql, new QueryRequestContext(AuthorizationService, user));
         }

         return await graphQLDocument.ExecuteQueryAsync(overwriteContext, serviceProvider, gql.Variables, gql.OperationName, options);
     }
     catch (Exception exception)
     {
         return HandleException(exception);
     }
 }

Hi, can you give an example of where/how you want to handle an exception?

Hi, can you give an example of where/how you want to handle an exception?

Thank you for responding.

In my services I have designed some of my own exception definitions, then in the GraphqlController I will catch all the exceptions and classify them, if the exception does not fall into the categories I have defined it will return a general message, otherwise I will send an error information structure with error code 400 to the client. At the client, I will build a notification and error handling system based on the above error information structure received from the server.

On many of my projects, I integrate that structure because it helps me not have to rebuild the error reporting system.

Exceptions will end in up in the result.Errors. By default these are made general to not accidentally expose any internal implementation details (something pen testing will often pick up).

If you want the exception details to come through in the Errors object you can use SchemaBuilderSchemaOptions.AllowedExceptions property to define which exceptions are rendered into the results. Or mark your exceptions with the AllowedExceptionAttribute to have exception details in the results.

Hopefully that helps.

Going to explore a change in 6.0 where EntityGraphQL with either throw exceptions or have more information about them. Haven't got all the details yet but we need a way to be smart about what status codes to return. 400, 500, etc. Thinking Execute throw 1 of 2 exceptions, 1 being the query is wrong/invalid etc. and 2 being there were errors but there could be partial results. Then any other exception is (should be) from your code.

That or not exceptions but some error information broken into the same groups.