public async Task InvokeAsync(IQueryContext context) { try { await _next(context).ConfigureAwait(false); } catch (GraphQLException ex) { context.Exception = ex; context.Result = QueryResult.CreateError( _errorHandler.Handle(ex.Errors)); } catch (SyntaxException ex) { IError error = _errorHandler.CreateUnexpectedError(ex) .SetMessage(ex.Message) .AddLocation(ex.Line, ex.Column) .Build(); error = _errorHandler.Handle(error); context.Exception = ex; context.Result = QueryResult.CreateError(error); } catch (Exception ex) { IError error = _errorHandler.CreateUnexpectedError(ex) .Build(); error = _errorHandler.Handle(error); context.Exception = ex; context.Result = QueryResult.CreateError(error); } }
public async Task <IReadOnlyQueryResult> ReadAsync( CancellationToken cancellationToken) { try { IReadOnlyQueryRequest request = _batch[_index++]; DocumentNode document = request.Query is QueryDocument d ? d.Document : Utf8GraphQLParser.Parse(request.Query.ToSpan()); OperationDefinitionNode operation = document.GetOperation(request.OperationName); if (document != _previous) { _fragments = document.GetFragments(); _visitationMap.Initialize(_fragments); } operation.Accept( _visitor, _visitationMap, n => VisitorAction.Continue); _previous = document; document = RewriteDocument(operation); operation = (OperationDefinitionNode)document.Definitions[0]; IReadOnlyDictionary <string, object> variableValues = MergeVariables(request.VariableValues, operation); request = QueryRequestBuilder.From(request) .SetQuery(document) .SetVariableValues(variableValues) .AddExportedVariables(_exportedVariables) .SetQueryName(null) // TODO ... should we create a name here? .SetQueryHash(null) .Create(); var result = (IReadOnlyQueryResult)await _executor.ExecuteAsync( request, cancellationToken) .ConfigureAwait(false); IsCompleted = _index >= _batch.Count; return(result); } catch (QueryException ex) { IsCompleted = true; return(QueryResult.CreateError(ex.Errors)); } catch (Exception ex) { IsCompleted = true; return(QueryResult.CreateError( _errorHandler.Handle( _errorHandler.CreateUnexpectedError(ex).Build()))); } }
public async Task InvokeAsync(IQueryContext context) { try { IHttpClientFactory httpClientFactory = context.Services.GetRequiredService <IHttpClientFactory>(); context.Result = await _client.FetchAsync( context.Request, httpClientFactory.CreateClient(_schemaName), context.Services.GetServices <IHttpQueryRequestInterceptor>(), context.RequestAborted) .ConfigureAwait(false); } catch (HttpRequestException ex) { IError error = _errorHandler.CreateUnexpectedError(ex) .SetCode(ErrorCodes.HttpRequestException) .Build(); context.Exception = ex; context.Result = QueryResultBuilder.CreateError(error); } await _next.Invoke(context).ConfigureAwait(false); }
private async Task HandleRequestAsync(HttpContext context) { // first we need to get the request executor to be able to execute requests. IRequestExecutor requestExecutor = await GetExecutorAsync(context.RequestAborted); IHttpRequestInterceptor requestInterceptor = requestExecutor.GetRequestInterceptor(); IErrorHandler errorHandler = requestExecutor.GetErrorHandler(); context.Items[WellKnownContextData.RequestExecutor] = requestExecutor; HttpStatusCode? statusCode = null; IExecutionResult?result; // next we parse the GraphQL request. GraphQLRequest request; using (_diagnosticEvents.ParseHttpRequest(context)) { try { request = _requestParser.ReadParamsRequest(context.Request.Query); } catch (GraphQLRequestException ex) { // A GraphQL request exception is thrown if the HTTP request body couldn't be // parsed. In this case we will return HTTP status code 400 and return a // GraphQL error result. statusCode = HttpStatusCode.BadRequest; IReadOnlyList <IError> errors = errorHandler.Handle(ex.Errors); result = QueryResultBuilder.CreateError(errors); _diagnosticEvents.ParserErrors(context, errors); goto HANDLE_RESULT; } catch (Exception ex) { statusCode = HttpStatusCode.InternalServerError; IError error = errorHandler.CreateUnexpectedError(ex).Build(); result = QueryResultBuilder.CreateError(error); _diagnosticEvents.HttpRequestError(context, error); goto HANDLE_RESULT; } } // after successfully parsing the request we now will attempt to execute the request. try { GraphQLServerOptions?options = context.GetGraphQLServerOptions(); result = await ExecuteSingleAsync( context, requestExecutor, requestInterceptor, _diagnosticEvents, request, options is null or { AllowedGetOperations : AllowedGetOperations.Query } ?_onlyQueries : null); }
private async Task <IQueryResult> ExecuteNextAsync( IReadOnlyQueryRequest request, CancellationToken cancellationToken) { try { DocumentNode document = request.Query is QueryDocument d ? d.Document : Utf8GraphQLParser.Parse(request.Query !.AsSpan()); OperationDefinitionNode operation = document.GetOperation(request.OperationName); if (document != _previous) { _fragments = document.GetFragments(); _visitationMap.Initialize(_fragments); } operation.Accept( _visitor, _visitationMap, n => VisitorAction.Continue); _previous = document; document = RewriteDocument(operation); operation = (OperationDefinitionNode)document.Definitions[0]; IReadOnlyDictionary <string, object?>?variableValues = MergeVariables(request.VariableValues, operation); request = QueryRequestBuilder.From(request) .SetQuery(document) .SetVariableValues(variableValues) .AddExportedVariables(_exportedVariables) .SetQueryId(null) // TODO ... should we create a name here? .SetQueryHash(null) .Create(); return((IReadOnlyQueryResult)await _requestExecutor.ExecuteAsync( request, cancellationToken) .ConfigureAwait(false)); } catch (GraphQLException ex) { return(QueryResultBuilder.CreateError(ex.Errors)); } catch (Exception ex) { return(QueryResultBuilder.CreateError( _errorHandler.Handle( _errorHandler.CreateUnexpectedError(ex).Build()))); } }
public static IError UnexpectedLeafValueSerializationError( Exception exception, IErrorHandler errorHandler, FieldNode field, Path path) { return(errorHandler .CreateUnexpectedError(exception) .AddLocation(field) .SetPath(path) .SetCode(ErrorCodes.Execution.CannotSerializeLeafValue) .Build()); }
/// <summary> /// Execute the current HttpContext provided using HotChocolate GraphQL and the currently configured pipeline. /// ALL parsing, and processing of the Query of the request will be handled by existing HotChoclate HttpPost & HttpGet /// middleware, this class will only proxy the context into HC for execution, and handle unexpected errors. /// NOTE: An EmptyResult() response is returned because that is what must be returned by the AzureFunction; because HotChocolate /// has already processed the request and started the response (e.g. StatusCode will be set by HC) an EmptyResult() response /// will prevent the Azure Functions framework from attempting to set it again resulting in a a Host error/crash. /// </summary> /// <param name="httpContext"></param> /// <param name="logger"></param> /// <param name="cancellationToken"></param> /// <returns> /// Returns an EmptyResult() response because HotChocolate has already processed the request and started the response /// (e.g. StatusCode will be set by HC) and an EmptyResult() response will prevent the Azure Functions framework from /// attempting to set it again resulting in a a Host error/crash. /// </returns> public async Task <IActionResult> ExecuteFunctionsQueryAsync(HttpContext httpContext, ILogger logger, CancellationToken cancellationToken) { try { //Use the Middleware Proxy to Invoke the pre-configured pipeline for Http POST & GET processing... var httpMiddlewareProxy = this.AzureFunctionsMiddlewareProxy; await httpMiddlewareProxy.InvokeAsync(httpContext).ConfigureAwait(false); } //NOTE: We Implement error handling that matches the Existing HttpPostMiddleware, to ensure that all code // has top level error handling for Graph processing. catch (GraphQLRequestException ex) { //If Debugging is enabled then Log the Errors to AzureFunctions framework (e.g. Application Insights) logger.LogDebug(ex, $"{nameof(GraphQLRequestException)} occurred while processing the GraphQL request; {ex.Message}."); // A GraphQL request exception is thrown if the HTTP request body couldn't be parsed. // In this case we will return HTTP status code 400 and return a GraphQL error result. IErrorHandler errorHandler = await this.AzureFunctionsMiddlewareProxy.GetErrorHandlerAsync(cancellationToken).ConfigureAwait(false); IQueryResult errorResult = QueryResultBuilder.CreateError(errorHandler.Handle(ex.Errors)); await HandleGraphQLErrorResponseAsync(httpContext, HttpStatusCode.BadRequest, errorResult); } catch (Exception exc) { //Log all Unknown Exceptions as GraphQLExceptions to Azure Framework (e.g. Application Insights). logger.LogError(exc, "An unhandled exception occurred while processing the GraphQL request."); // An unknown and unexpected GraphQL request exception was encountered. // In this case we will return HTTP status code 500 and return a GraphQL error result. IErrorHandler errorHandler = await this.AzureFunctionsMiddlewareProxy.GetErrorHandlerAsync(cancellationToken).ConfigureAwait(false); IError error = errorHandler.CreateUnexpectedError(exc).Build(); IQueryResult errorResult = QueryResultBuilder.CreateError(error); HttpStatusCode statusCode = exc is HttpRequestException ? HttpStatusCode.BadRequest : HttpStatusCode.InternalServerError; await HandleGraphQLErrorResponseAsync(httpContext, statusCode, errorResult).ConfigureAwait(false); } //Safely resolve the .Net Core request with Empty Result because the Response has already been handled! //NOTE: We Must return EmptyResult() so that No Action is taken on the Response or else an error will occur // since HotChocolate has ALREADY started (e.g. processed) the response, and the Status Code is already set! //For More Info See: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.emptyresult?view=aspnetcore-5.0 return(new EmptyResult()); }
public async ValueTask InvokeAsync(IRequestContext context) { try { await _next(context).ConfigureAwait(false); } catch (GraphQLException ex) { context.Exception = ex; context.Result = QueryResultBuilder.CreateError(_errorHandler.Handle(ex.Errors)); } catch (Exception ex) { context.Exception = ex; IError error = _errorHandler.CreateUnexpectedError(ex).Build(); context.Result = QueryResultBuilder.CreateError(_errorHandler.Handle(error)); } }
private static async Task ExecuteMiddlewareAsync( ResolverContext resolverContext, IErrorHandler errorHandler) { try { await resolverContext.Middleware.Invoke(resolverContext) .ConfigureAwait(false); } catch (QueryException ex) { resolverContext.Result = ex.Errors; } catch (Exception ex) { resolverContext.Result = errorHandler.CreateUnexpectedError(ex) .SetPath(resolverContext.Path) .AddLocation(resolverContext.FieldSelection) .Build(); } }
private static async Task ExecuteMiddlewareAsync( ResolverContext resolverContext, IErrorHandler errorHandler) { try { await resolverContext.Middleware.Invoke(resolverContext) .ConfigureAwait(false); // TODO : this should be handled more elegant if (resolverContext.Result is IQueryable q) { resolverContext.Result = await Task.Run(() => { var items = new List <object>(); foreach (object o in q) { items.Add(o); } return(items); }) .ConfigureAwait(false); } } catch (GraphQLException ex) { resolverContext.Result = ex.Errors; } catch (Exception ex) { resolverContext.Result = errorHandler.CreateUnexpectedError(ex) .SetPath(resolverContext.Path) .AddLocation(resolverContext.FieldSelection) .Build(); } }
private async Task HandleRequestAsync(HttpContext context) { // first we need to get the request executor to be able to execute requests. IRequestExecutor requestExecutor = await GetExecutorAsync(context.RequestAborted); IHttpRequestInterceptor requestInterceptor = requestExecutor.GetRequestInterceptor(); IErrorHandler errorHandler = requestExecutor.GetErrorHandler(); HttpStatusCode? statusCode = null; IExecutionResult?result; try { // next we parse the GraphQL request. GraphQLRequest request = _requestParser.ReadParamsRequest(context.Request.Query); result = await ExecuteSingleAsync( context, requestExecutor, requestInterceptor, request); } catch (GraphQLRequestException ex) { // A GraphQL request exception is thrown if the HTTP request body couldn't be // parsed. In this case we will return HTTP status code 400 and return a // GraphQL error result. statusCode = HttpStatusCode.BadRequest; result = QueryResultBuilder.CreateError(errorHandler.Handle(ex.Errors)); } catch (Exception ex) { statusCode = HttpStatusCode.InternalServerError; IError error = errorHandler.CreateUnexpectedError(ex).Build(); result = QueryResultBuilder.CreateError(error); } // in any case we will have a valid GraphQL result at this point that can be written // to the HTTP response stream. Debug.Assert(result is not null, "No GraphQL result was created."); await WriteResultAsync(context.Response, result, statusCode, context.RequestAborted); }
private async Task HandleRequestAsync( HttpContext context, AllowedContentType contentType) { // first we need to get the request executor to be able to execute requests. IRequestExecutor requestExecutor = await GetExecutorAsync(context.RequestAborted); IHttpRequestInterceptor requestInterceptor = requestExecutor.GetRequestInterceptor(); IErrorHandler errorHandler = requestExecutor.GetErrorHandler(); HttpStatusCode? statusCode = null; IExecutionResult?result; try { // next we parse the GraphQL request. IReadOnlyList <GraphQLRequest> requests = await _requestParser.ReadJsonRequestAsync( context.Request.Body, context.RequestAborted); switch (requests.Count) { // if the HTTP request body contains no GraphQL request structure the // whole request is invalid and we will create a GraphQL error response. case 0: { statusCode = HttpStatusCode.BadRequest; IError error = errorHandler.Handle(ErrorHelper.RequestHasNoElements()); result = QueryResultBuilder.CreateError(error); break; } // if the HTTP request body contains a single GraphQL request and we do have // the batch operations query parameter specified we need to execute an // operation batch. // // An operation batch consists of a single GraphQL request document that // contains multiple operations. The batch operation query parameter // defines the order in which the operations shall be executed. case 1 when context.Request.Query.ContainsKey(_batchOperations): { string operationNames = context.Request.Query[_batchOperations]; if (TryParseOperations(operationNames, out IReadOnlyList <string>?ops)) { result = await ExecuteOperationBatchAsync( context, requestExecutor, requestInterceptor, requests[0], ops); } else { IError error = errorHandler.Handle(ErrorHelper.InvalidRequest()); statusCode = HttpStatusCode.BadRequest; result = QueryResultBuilder.CreateError(error); } break; } // if the HTTP request body contains a single GraphQL request and // no batch query parameter is specified we need to execute a single // GraphQL request. // // Most GraphQL requests will be of this type where we want to execute // a single GraphQL query or mutation. case 1: { result = await ExecuteSingleAsync( context, requestExecutor, requestInterceptor, requests[0]); break; } // if the HTTP request body contains more than one GraphQL request than // we need to execute a request batch where we need to execute multiple // fully specified GraphQL requests at once. default: result = await ExecuteBatchAsync( context, requestExecutor, requestInterceptor, requests); break; } } catch (GraphQLRequestException ex) { // A GraphQL request exception is thrown if the HTTP request body couldn't be // parsed. In this case we will return HTTP status code 400 and return a // GraphQL error result. statusCode = HttpStatusCode.BadRequest; result = QueryResultBuilder.CreateError(errorHandler.Handle(ex.Errors)); } catch (Exception ex) { statusCode = HttpStatusCode.InternalServerError; IError error = errorHandler.CreateUnexpectedError(ex).Build(); result = QueryResultBuilder.CreateError(error); } // in any case we will have a valid GraphQL result at this point that can be written // to the HTTP response stream. Debug.Assert(result is not null, "No GraphQL result was created."); await WriteResultAsync(context.Response, result, statusCode, context.RequestAborted); }
protected override async Task HandleAsync( ISocketConnection connection, DataStartMessage message, CancellationToken cancellationToken) { var session = new CancellationTokenSource(); var combined = CancellationTokenSource.CreateLinkedTokenSource( session.Token, cancellationToken); var sessionIsHandled = false; IExecutionResult result = await ExecuteAsync(combined.Token); try { switch (result) { case SubscriptionResult subscriptionResult: // first we add the cts to the result so that they are disposed when the // subscription is disposed. subscriptionResult.RegisterDisposable(combined); // while a subscription result must be disposed we are not handling it here // and leave this responsibility to the subscription session. ISubscription subscription = GetSubscription(result); var subscriptionSession = new SubscriptionSession( session, _socketSessionInterceptor, connection, subscriptionResult, subscription, _diagnosticEvents, message.Id); connection.Subscriptions.Register(subscriptionSession); sessionIsHandled = true; break; case IResponseStream streamResult: // stream results represent deferred execution streams that use execution // resources. We need to ensure that these are disposed when we are // finished. await using (streamResult) { await HandleStreamResultAsync( connection, message, streamResult, cancellationToken); } break; case IQueryResult queryResult: // query results use pooled memory an need to be disposed after we have // used them. using (queryResult) { await HandleQueryResultAsync( connection, message, queryResult, cancellationToken); } break; default: throw DataStartMessageHandler_RequestTypeNotSupported(); } } finally { if (!sessionIsHandled) { session.Dispose(); combined.Dispose(); } } async ValueTask <IExecutionResult> ExecuteAsync(CancellationToken cancellationToken) { try { IQueryRequestBuilder requestBuilder = QueryRequestBuilder.From(message.Payload) .SetServices(connection.RequestServices); await _socketSessionInterceptor.OnRequestAsync( connection, requestBuilder, cancellationToken); return(await _requestExecutor.ExecuteAsync( requestBuilder.Create(), cancellationToken)); } catch (Exception ex) { IErrorBuilder error = _errorHandler.CreateUnexpectedError(ex); return(QueryResultBuilder.CreateError(error.Build())); } } }
public async Task HandleRequestAsync( HttpContext context, AllowedContentType contentType, CancellationToken cancellationToken) { // first we need to gather an executor to start execution the request. IRequestExecutor executor = await GetExecutorAsync(context.RequestAborted) .ConfigureAwait(false); IErrorHandler errorHandler = executor.Services.GetRequiredService <IErrorHandler>(); IExecutionResult?result = null; int?statusCode = null; try { IReadOnlyList <GraphQLRequest>?requests = await ReadRequestAsync( contentType, context.Request.Body, context.RequestAborted) .ConfigureAwait(false); if (requests.Count == 0) { // IError error = ErrorHandler.Handle(ErrorHelper.RequestHasNoElements()); // httpHelper.Result = CreateError(error); } else if (requests.Count == 1) { string operations = context.Request.Query[_batchOperations]; if (operations is null) { result = await ExecuteQueryAsync(context, executor, requests[0]) .ConfigureAwait(false); } else if (TryParseOperations(operations, out IReadOnlyList <string>?operationNames)) { // await ExecuteOperationBatchAsync( // httpHelper, batch[0], operationNames) // .ConfigureAwait(false); } else { // IError error = ErrorHandler.Handle(ErrorHelper.InvalidRequest()); // httpHelper.StatusCode = BadRequest; // httpHelper.Result = CreateError(error); } } else { } } catch (GraphQLRequestException ex) { statusCode = 400; IEnumerable <IError> errors = errorHandler.Handle(ex.Errors); result = QueryResultBuilder.CreateError(errors); } catch (Exception ex) { statusCode = 500; IError error = errorHandler.CreateUnexpectedError(ex).Build(); result = QueryResultBuilder.CreateError(error); } Debug.Assert(result is { });
protected async Task HandleRequestAsync( HttpContext context, AllowedContentType contentType) { // first we need to get the request executor to be able to execute requests. IRequestExecutor requestExecutor = await GetExecutorAsync(context.RequestAborted); IHttpRequestInterceptor requestInterceptor = requestExecutor.GetRequestInterceptor(); IErrorHandler errorHandler = requestExecutor.GetErrorHandler(); context.Items[WellKnownContextData.RequestExecutor] = requestExecutor; HttpStatusCode? statusCode = null; IExecutionResult?result; // next we parse the GraphQL request. IReadOnlyList <GraphQLRequest> requests; using (DiagnosticEvents.ParseHttpRequest(context)) { try { requests = await GetRequestsFromBody(context.Request, context.RequestAborted); } catch (GraphQLRequestException ex) { // A GraphQL request exception is thrown if the HTTP request body couldn't be // parsed. In this case we will return HTTP status code 400 and return a // GraphQL error result. statusCode = HttpStatusCode.BadRequest; IReadOnlyList <IError> errors = errorHandler.Handle(ex.Errors); result = QueryResultBuilder.CreateError(errors); DiagnosticEvents.ParserErrors(context, errors); goto HANDLE_RESULT; } catch (Exception ex) { statusCode = HttpStatusCode.InternalServerError; IError error = errorHandler.CreateUnexpectedError(ex).Build(); result = QueryResultBuilder.CreateError(error); DiagnosticEvents.HttpRequestError(context, error); goto HANDLE_RESULT; } } // after successfully parsing the request we now will attempt to execute the request. try { switch (requests.Count) { // if the HTTP request body contains no GraphQL request structure the // whole request is invalid and we will create a GraphQL error response. case 0: { statusCode = HttpStatusCode.BadRequest; IError error = errorHandler.Handle(ErrorHelper.RequestHasNoElements()); result = QueryResultBuilder.CreateError(error); DiagnosticEvents.HttpRequestError(context, error); break; } // if the HTTP request body contains a single GraphQL request and we do have // the batch operations query parameter specified we need to execute an // operation batch. // // An operation batch consists of a single GraphQL request document that // contains multiple operations. The batch operation query parameter // defines the order in which the operations shall be executed. case 1 when context.Request.Query.ContainsKey(_batchOperations): { string operationNames = context.Request.Query[_batchOperations]; if (TryParseOperations(operationNames, out IReadOnlyList <string>?ops)) { result = await ExecuteOperationBatchAsync( context, requestExecutor, requestInterceptor, DiagnosticEvents, requests[0], ops); } else { IError error = errorHandler.Handle(ErrorHelper.InvalidRequest()); statusCode = HttpStatusCode.BadRequest; result = QueryResultBuilder.CreateError(error); DiagnosticEvents.HttpRequestError(context, error); } break; } // if the HTTP request body contains a single GraphQL request and // no batch query parameter is specified we need to execute a single // GraphQL request. // // Most GraphQL requests will be of this type where we want to execute // a single GraphQL query or mutation. case 1: { result = await ExecuteSingleAsync( context, requestExecutor, requestInterceptor, DiagnosticEvents, requests[0]); break; } // if the HTTP request body contains more than one GraphQL request than // we need to execute a request batch where we need to execute multiple // fully specified GraphQL requests at once. default: result = await ExecuteBatchAsync( context, requestExecutor, requestInterceptor, DiagnosticEvents, requests); break; } } catch (GraphQLException ex) { // This allows extensions to throw GraphQL exceptions in the GraphQL interceptor. statusCode = null; // we let the serializer determine the status code. result = QueryResultBuilder.CreateError(ex.Errors); foreach (IError error in ex.Errors) { DiagnosticEvents.HttpRequestError(context, error); } } catch (Exception ex) { statusCode = HttpStatusCode.InternalServerError; IError error = errorHandler.CreateUnexpectedError(ex).Build(); result = QueryResultBuilder.CreateError(error); DiagnosticEvents.HttpRequestError(context, error); } HANDLE_RESULT: IDisposable? formatScope = null; try { // if cancellation is requested we will not try to attempt to write the result to the // response stream. if (context.RequestAborted.IsCancellationRequested) { return; } // in any case we will have a valid GraphQL result at this point that can be written // to the HTTP response stream. Debug.Assert(result is not null, "No GraphQL result was created."); if (result is IQueryResult queryResult) { formatScope = DiagnosticEvents.FormatHttpResponse(context, queryResult); } await WriteResultAsync(context.Response, result, statusCode, context.RequestAborted); } finally { // we must dispose the diagnostic scope first. formatScope?.Dispose(); // query results use pooled memory an need to be disposed after we have // used them. if (result is IAsyncDisposable asyncDisposable) { await asyncDisposable.DisposeAsync(); } if (result is IDisposable disposable) { disposable.Dispose(); } } }