Giter VIP home page Giter VIP logo

server's Introduction

ASP.NET Core GraphQL Server driven by GraphQL.NET

License codecov Nuget Nuget GitHub Release Date GitHub commits since latest release (by date) GitHub contributors Size

GraphQL ASP.NET Core server on top of GraphQL.NET. HTTP transport compatible with the GraphQL over HTTP draft specification. WebSocket transport compatible with both subscriptions-transport-ws and graphql-ws subscription protocols. The transport format of all messages is supposed to be JSON.

Provides the following packages:

Package Downloads Version Description
GraphQL.Server.All Nuget Nuget Includes all the packages below, excluding the legacy authorization package, plus the GraphQL.DataLoader and GraphQL.MemoryCache packages
GraphQL.Server.Authorization.AspNetCore (deprecated) Nuget Nuget Provides legacy authorization rule support (deprecated; please use GraphQL.Server.Transports.AspNetCore)
GraphQL.Server.Transports.AspNetCore Nuget Nuget Provides GraphQL over HTTP/WebSocket server support on top of ASP.NET Core, plus authorization rule support
GraphQL.Server.Ui.Altair Nuget Nuget Provides Altair UI middleware
GraphQL.Server.Ui.Playground Nuget Nuget Provides Playground UI middleware
GraphQL.Server.Ui.GraphiQL Nuget Nuget Provides GraphiQL UI middleware
GraphQL.Server.Ui.Voyager Nuget Nuget Provides Voyager UI middleware

You can install the latest stable versions via NuGet. Also you can get all preview versions from GitHub Packages. Note that GitHub requires authentication to consume the feed. See more information here.

⚠️ When upgrading from prior versions, please remove references to these old packages ⚠️
GraphQL.Server.Core
GraphQL.Server.Transports.AspNetCore.NewtonsoftJson
GraphQL.Server.Transports.AspNetCore.SystemTextJson
GraphQL.Server.Transports.Subscriptions.Abstractions
GraphQL.Server.Transports.Subscriptions.WebSockets
GraphQL.Server.Transports.WebSocktes

Description

This package is designed for ASP.NET Core (2.1 through 6.0) to facilitate easy set-up of GraphQL requests over HTTP. The code is designed to be used as middleware within the ASP.NET Core pipeline, serving GET, POST or WebSocket requests. GET requests process requests from the query string. POST requests can be in the form of JSON requests, form submissions, or raw GraphQL strings. Form submissions either accepts query, operationName, variables and extensions parameters, or operations and map parameters along with file uploads as defined in the GraphQL multipart request spec. WebSocket requests can use the graphql-ws or graphql-transport-ws WebSocket sub-protocol, as defined in the apollographql/subscriptions-transport-ws and enisdenjo/graphql-ws repositories, respectively.

The middleware can be configured through the IApplicationBuilder or IEndpointRouteBuilder builder interfaces. In addition, an ExecutionResultActionResult class is added for returning ExecutionResult instances directly from a controller action.

Authorization is also supported with the included AuthorizationValidationRule. It will scan GraphQL documents and validate that the schema and all referenced output graph types, fields of output graph types, and query arguments meet the specified policy and/or roles held by the authenticated user within the ASP.NET Core authorization framework. It does not validate any policies or roles specified for input graph types, fields of input graph types, or directives. It skips validations for fields or fragments that are marked with the @skip or @include directives.

See migration notes for changes from version 6.x.

Configuration

Typical configuration with HTTP middleware

First add either the GraphQL.Server.All nuget package or the GraphQL.Server.Transports.AspNetCore nuget package to your application. Referencing the "all" package will include the UI middleware packages. These packages depend on GraphQL version 7.0.0 or later.

Then update your Program.cs or Startup.cs to configure GraphQL, registering the schema and the serialization engine as a minimum. Configure WebSockets and GraphQL in the HTTP pipeline by calling UseWebSockets and UseGraphQL at the appropriate point. Finally, you may also include some UI middleware for easy testing of your GraphQL endpoint by calling UseGraphQLVoyager or a similar method at the appropriate point.

Below is a complete sample of a .NET 6 console app that hosts a GraphQL endpoint at http://localhost:5000/graphql:

Project file

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="GraphQL.Server.All" Version="7.0.0" />
  </ItemGroup>

</Project>

Program.cs file

using GraphQL;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGraphQL(b => b
    .AddAutoSchema<Query>()  // schema
    .AddSystemTextJson());   // serializer

var app = builder.Build();
app.UseDeveloperExceptionPage();
app.UseWebSockets();
app.UseGraphQL("/graphql");            // url to host GraphQL endpoint
app.UseGraphQLPlayground(
    "/",                               // url to host Playground at
    new GraphQL.Server.Ui.Playground.PlaygroundOptions
    {
        GraphQLEndPoint = "/graphql",         // url of GraphQL endpoint
        SubscriptionsEndPoint = "/graphql",   // url of GraphQL endpoint
    });
await app.RunAsync();

Schema

public class Query
{
    public static string Hero() => "Luke Skywalker";
}

Sample request url

http://localhost:5000/graphql?query={hero}

Sample response

{"data":{"hero":"Luke Skywalker"}}

Configuration with endpoint routing

To use endpoint routing, call MapGraphQL from inside the endpoint configuration builder rather than UseGraphQL on the application builder. See below for the sample of the application builder code:

var app = builder.Build();
app.UseDeveloperExceptionPage();
app.UseWebSockets();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.MapGraphQL("graphql");
    endpoints.MapGraphQLVoyager("ui/voyager");
});
await app.RunAsync();

Using endpoint routing is particularly useful when you want to select a specific CORS configuration for the GraphQL endpoint. See the CORS section below for a sample.

Please note that when using endpoint routing, you cannot use WebSocket connections while a UI package is also configured at the same URL. You will need to use a different URL for the UI package, or use UI middleware prior to endpoint routing. So long as different URLs are used, there are no issues. Below is a sample when the UI and GraphQL reside at the same URL:

var app = builder.Build();
app.UseDeveloperExceptionPage();
app.UseWebSockets();
app.UseRouting();
app.UseGraphQLVoyager("/graphql");
app.UseEndpoints(endpoints =>
{
    endpoints.MapGraphQL("/graphql");
});
await app.RunAsync();

Configuration with a MVC controller

Although not recommended, you may set up a controller action to execute GraphQL requests. You will not need UseGraphQL or MapGraphQL in the application startup. You may use GraphQLExecutionActionResult to let the middleware handle the entire parsing and execution of the request, including subscription requests over WebSocket connections, or you can execute the request yourself, only using ExecutionResultActionResult to serialize the result.

You can also reference the UI projects to display a GraphQL user interface as shown below.

Using GraphQLExecutionActionResult

public class HomeController : Controller
{
    public IActionResult Index()
        => new GraphiQLActionResult(opts =>
        {
            opts.GraphQLEndPoint = "/Home/graphql";
            opts.SubscriptionsEndPoint = "/Home/graphql";
        });

    [HttpGet]
    [HttpPost]
    [ActionName("graphql")]
    public IActionResult GraphQL()
        => new GraphQLExecutionActionResult();
}

Using ExecutionResultActionResult

Note: this is very simplified; a much more complete sample can be found in the Samples.Controller project within this repository.

public class HomeController : Controller
{
    private readonly IDocumentExecuter _documentExecuter;

    public TestController(IDocumentExecuter<ISchema> documentExecuter)
    {
        _documentExecuter = documentExecuter;
    }

    [HttpGet]
    public async Task<IActionResult> GraphQL(string query)
    {
        var result = await _documentExecuter.ExecuteAsync(new()
        {
            Query = query,
            RequestServices = HttpContext.RequestServices,
            CancellationToken = HttpContext.RequestAborted,
        });
        return new ExecutionResultActionResult(result);
    }
}

Configuration with Azure Functions

This project also supports hosting GraphQL endpoints within Azure Functions. You will need to complete the following steps:

  1. Configure the Azure Function to use Dependency Injection: See https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection for details.

  2. Configure GraphQL via builder.Services.AddGraphQL() the same as you would in a typical ASP.NET Core application.

  3. Add an HTTP function that returns an appropriate ActionResult:

[FunctionName("GraphQL")]
public static IActionResult RunGraphQL(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post"] HttpRequest req)
{
    return new GraphQLExecutionActionResult();
}
  1. Optionally, add a UI package to the project and configure it:
[FunctionName("Playground")]
public static IActionResult RunGraphQL(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get"] HttpRequest req)
{
    return new PlaygroundActionResult(opts => opts.GraphQLEndPoint = "/api/graphql");
}

Middleware can be configured by passing a configuration delegate to new GraphQLExecutionActionResult(). Multiple schemas are supported by the use of GraphQLExecutionActionResult<TSchema>(). It is not possible to configure subscription support, as Azure Functions do not support WebSockets since it is a serverless environment.

See the Samples.AzureFunctions project for a complete sample based on the .NET template for Azure Functions.

Please note that the GraphQL schema needs to be initialized for every call through Azure Functions, since it is a serverless environment. This is done automatically but will come at a performance cost. If you are using a schema that is expensive to initialize, you may want to consider using a different hosting environment.

User context configuration

To set the user context to be used during the execution of GraphQL requests, call AddUserContextBuilder during the GraphQL service setup to set a delegate which will be called when the user context is built. Alternatively, you can register an IUserContextBuilder implementation to do the same.

Program.cs / Startup.cs

builder.Services.AddGraphQL(b => b
    .AddAutoSchema<Query>()
    .AddSystemTextJson()
    .AddUserContextBuilder(httpContext => new MyUserContext(httpContext));

MyUserContext.cs

public class MyUserContext : Dictionary<string, object?>
{
    public ClaimsPrincipal User { get; }

    public MyUserContext(HttpContext context)
    {
        User = context.User;
    }
}

Authorization configuration

You can configure authorization for all GraphQL requests, or for individual graphs, fields and query arguments within your schema. Both can be used if desired.

Be sure to call app.UseAuthentication() and app.UseAuthorization() prior to the call to app.UseGraphQL(). For example:

app.UseAuthentication();
app.UseAuthorization();
app.UseWebSockets();
app.UseGraphQL("/graphql");

Endpoint authorization (which would include introspection requests)

Endpoint authorization will check authorization requirements are met for the entire GraphQL endpoint, including introspection requests. These checks occur prior to parsing, validating or executing the document.

When calling UseGraphQL, specify options as necessary to configure authorization as required.

app.UseGraphQL("/graphql", config =>
{
    // require that the user be authenticated
    config.AuthorizationRequired = true;

    // require that the user be a member of at least one role listed
    config.AuthorizedRoles.Add("MyRole");
    config.AuthorizedRoles.Add("MyAlternateRole");

    // require that the user pass a specific authorization policy
    config.AuthorizedPolicy = "MyPolicy";
});

For individual graph types, fields and query arguments

To configure the ASP.NET Core authorization validation rule for GraphQL, add the corresponding validation rule during GraphQL configuration, typically by calling .AddAuthorizationRule() as shown below:

builder.Services.AddGraphQL(b => b
    .AddAutoSchema<Query>()
    .AddSystemTextJson()
    .AddAuthorizationRule());

Both roles and policies are supported for output graph types, fields on output graph types, and query arguments. If multiple policies are specified, all must match; if multiple roles are specified, any one role must match. You may also use .Authorize() and/or the [Authorize] attribute to validate that the user has authenticated. You may also use .AllowAnonymous() and/or [AllowAnonymous] to allow fields to be returned to unauthenticated users within an graph that has an authorization requirement defined.

Please note that authorization rules do not apply to values returned within introspection requests, potentially leaking information about protected areas of the schema to unauthenticated users. You may use the ISchemaFilter to restrict what information is returned from introspection requests, but it will apply to both authenticated and unauthenticated users alike.

Introspection requests are allowed unless the schema has an authorization requirement set on it. The @skip and @include directives are honored, skipping authorization checks for fields or fragments skipped by @skip or @include.

Please note that if you use interfaces, validation might be executed against the graph field or the interface field, depending on the structure of the query. For instance:

{
  cat {
    # validates against Cat.Name
    name

    # validates against Animal.Name
    ... on Animal {
      name
    }
  }
}

Similarly for unions, validation occurs on the exact type that is queried. Be sure to carefully consider placement of authorization rules when using interfaces and unions, especially when some fields are marked with AllowAnonymous.

⚠️ Note that authorization rules are ignored for input types and fields of input types ⚠️

Custom authentication configuration for GET/POST requests

To provide custom authentication code, bypassing ASP.NET Core's authentication, derive from the GraphQLHttpMiddleware<T> class and override HandleAuthorizeAsync, setting HttpContext.User to an appropriate ClaimsPrincipal instance.

See 'Customizing middleware behavior' below for an example of deriving from GraphQLHttpMiddleware.

Authentication for WebSocket requests

Since WebSocket requests from browsers cannot typically carry a HTTP Authorization header, you will need to authorize requests via the ConnectionInit WebSocket message or carry the authorization token within the URL. Below is a sample of the former:

builder.Services.AddGraphQL(b => b
    .AddAutoSchema<Query>()
    .AddSystemTextJson()
    .AddAuthorizationRule()  // not required for endpoint authorization
    .AddWebSocketAuthentication<MyAuthService>());

app.UseGraphQL("/graphql", config =>
{
    // require that the user be authenticated
    config.AuthorizationRequired = true;
});

class MyAuthService : IWebSocketAuthenticationService
{
    private readonly IGraphQLSerializer _serializer;

    public MyAuthService(IGraphQLSerializer serializer)
    {
        _serializer = serializer;
    }

    public async ValueTask<bool> AuthenticateAsync(IWebSocketConnection connection, OperationMessage operationMessage)
    {
        // read payload of ConnectionInit message and look for an "Authorization" entry that starts with "Bearer "
        var payload = _serializer.ReadNode<Inputs>(operationMessage.Payload);
        if ((payload?.TryGetValue("Authorization", out var value) ?? false) && value is string valueString)
        {
            var user = ParseToken(valueString);
            if (user != null)
            {
                // set user and indicate authentication was successful
                connection.HttpContext.User = user;
                return true;
            }
        }
        return false; // authentication failed
    }

    private ClaimsPrincipal? ParseToken(string authorizationHeaderValue)
    {
        // parse header value and return user, or null if unable
    }
}

To authorize based on information within the query string, it is recommended to derive from GraphQLHttpMiddleware<T> and override InvokeAsync, setting HttpContext.User based on the query string parameters, and then calling base.InvokeAsync. Alternatively you may override HandleAuthorizeAsync which will execute for GET/POST requests, and HandleAuthorizeWebSocketConnectionAsync for WebSocket requests. Note that InvokeAsync will execute even if the protocol is disabled in the options via disabling HandleGet or similar; HandleAuthorizeAsync and HandleAuthorizeWebSocketConnectionAsync will not.

Authentication schemes

By default the role and policy requirements are validated against the current user as defined by HttpContext.User. This is typically set by ASP.NET Core's authentication middleware and is based on the default authentication scheme set during the call to AddAuthentication in Startup.cs. You may override this behavior by specifying a different authentication scheme via the AuthenticationSchemes option. For instance, if you wish to authenticate using JWT authentication when Cookie authentication is the default, you may specify the scheme as follows:

app.UseGraphQL("/graphql", config =>
{
    // specify a specific authentication scheme to use
    config.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
});

This will overwrite the HttpContext.User property when handling GraphQL requests, which will in turn set the IResolveFieldContext.User property to the same value (unless being overridden via an IWebSocketAuthenticationService implementation as shown above). So both endpoint authorization and field authorization will perform role and policy checks against the same authentication scheme.

UI configuration

There are four UI middleware projects included; Altair, GraphiQL, Playground and Voyager. See review the following methods for configuration options within each of the 4 respective NuGet packages:

app.UseGraphQLAltair();
app.UseGraphQLGraphiQL();
app.UseGraphQLPlayground();
app.UseGraphQLVoyager();

// or

endpoints.MapGraphQLAltair();
endpoints.MapGraphQLGraphiQL();
endpoints.MapGraphQLPlayground();
endpoints.MapGraphQLVoyager();

CORS configuration

ASP.NET Core supports CORS requests independently of GraphQL, including CORS pre-flight requests. To configure your application for CORS requests, add AddCors() and UseCors() into the application pipeline.

builder.Services.AddCors();

app.UseCors(policy => {
    // configure default policy here
});

To configure GraphQL to use a named CORS policy, configure the application to use endpoint routing and call RequireCors() on the endpoint configuration builder.

// ...
builder.Services.AddRouting();
builder.Services.AddCors(builder =>
{
    // configure named and/or default policies here
});

var app = builder.Build();
app.UseDeveloperExceptionPage();
app.UseWebSockets();
app.UseRouting();
app.UseCors();
app.UseEndpoints(endpoints =>
{
    // configure the graphql endpoint with the specified CORS policy
    endpoints.MapGraphQL()
        .RequireCors("MyCorsPolicy");
});
await app.RunAsync();

Response compression

ASP.NET Core supports response compression independently of GraphQL, with brotli and gzip support automatically based on the compression formats listed as supported in the request headers. To configure your application for response compression, configure your Program/Startup file as follows:

// add and configure the service
builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true; // may lead to CRIME and BREACH attacks
    options.MimeTypes = new[] { "application/json", "application/graphql-response+json" };
})

// place this first/early in the pipeline
app.UseResponseCompression();

In order to compress GraphQL responses, the application/graphql-response+json content type must be added to the MimeTypes option. You may choose to enable other content types as well.

Please note that enabling response compression over HTTPS can lead to CRIME and BREACH attacks. These side-channel attacks typically affects sites that rely on cookies for authentication. Please read this and this for more details.

ASP.NET Core 2.1 / .NET Framework 4.8

You may choose to use the .NET Core 2.1 runtime or the .NET Framework 4.8 runtime. This library has been tested with .NET Core 2.1 and .NET Framework 4.8.

The only additional requirement is that you must add this code in your Startup.cs file:

services.AddHostApplicationLifetime();

Besides that requirement, all features are supported in exactly the same manner as when using ASP.NET Core 3.1+. You may find differences in the ASP.NET Core runtime, such as CORS implementation differences, which are outside the scope of this project.

Please note that a serializer reference is not included for these projects within GraphQL.Server.Transports.AspNetCore; you will need to reference either GraphQL.NewtonsoftJson or GraphQL.SystemTextJson, or reference GraphQL.Server.All which includes GraphQL.NewtonsoftJson for ASP.NET Core 2.1 projects. This is because Newtonsoft.Json is the default serializer for ASP.NET Core 2.1 rather System.Text.Json. When using GraphQL.NewtonsoftJson, you will need to call AddNewtonsoftJson() rather than AddSystemTextJson() while configuring GraphQL.NET.

Microsoft support policy

Please note that .NET Core 2.1 is currently out of support by Microsoft. .NET Framework 4.8 is supported, and ASP.NET Core 2.1 is supported when run on .NET Framework 4.8. Please see these links for more information:

Advanced configuration

For more advanced configurations, see the overloads and configuration options available for the various builder methods, listed below. Methods and properties contain XML comments to provide assistance while coding with your IDE.

Builder interface Method Description
IGraphQLBuilder AddUserContextBuilder Sets up a delegate to create the UserContext for each GraphQL request.
IApplicationBuilder UseGraphQL Adds the GraphQL middleware to the HTTP request pipeline.
IEndpointRouteBuilder MapGraphQL Adds the GraphQL middleware to the HTTP request pipeline.

A number of the methods contain optional parameters or configuration delegates to allow further customization. Please review the overloads of each method to determine which options are available. In addition, many methods have more descriptive XML comments than shown above.

Configuration options

Below are descriptions of the options available when registering the HTTP middleware. Note that the HTTP middleware options are configured via the UseGraphQL or MapGraphQL methods allowing for different options for each configured endpoint.

GraphQLHttpMiddlewareOptions

Property Description Default value
AuthorizationRequired Requires HttpContext.User to represent an authenticated user. False
AuthorizedPolicy If set, requires HttpContext.User to pass authorization of the specified policy.
AuthorizedRoles If set, requires HttpContext.User to be a member of any one of a list of roles.
DefaultResponseContentType Sets the default response content type used within responses. application/graphql-response+json; charset=utf-8
EnableBatchedRequests Enables handling of batched GraphQL requests for POST requests when formatted as JSON. True
ExecuteBatchedRequestsInParallel Enables parallel execution of batched GraphQL requests. True
HandleGet Enables handling of GET requests. True
HandlePost Enables handling of POST requests. True
HandleWebSockets Enables handling of WebSockets requests. True
MaximumFileSize Sets the maximum file size allowed for GraphQL multipart requests. unlimited
MaximumFileCount Sets the maximum number of files allowed for GraphQL multipart requests. unlimited
ReadExtensionsFromQueryString Enables reading extensions from the query string. True
ReadFormOnPost Enables parsing of form data for POST requests (may have security implications). True
ReadQueryStringOnPost Enables parsing the query string on POST requests. True
ReadVariablesFromQueryString Enables reading variables from the query string. True
ValidationErrorsReturnBadRequest When enabled, GraphQL requests with validation errors have the HTTP status code set to 400 Bad Request. True
WebSockets Returns a set of configuration properties for WebSocket connections.

GraphQLWebSocketOptions

Property Description Default value
ConnectionInitWaitTimeout The amount of time to wait for a GraphQL initialization packet before the connection is closed. 10 seconds
KeepAliveTimeout The amount of time to wait between sending keep-alive packets. disabled
DisconnectionTimeout The amount of time to wait to attempt a graceful teardown of the WebSockets protocol. 10 seconds
DisconnectAfterErrorEvent Disconnects a subscription from the client if the subscription source dispatches an OnError event. True
DisconnectAfterAnyError Disconnects a subscription from the client if there are any GraphQL errors during a subscription. False

Multi-schema configuration

You may use the generic versions of the various builder methods to map a URL to a particular schema.

var app = builder.Build();
app.UseDeveloperExceptionPage();
app.UseGraphQL<DogSchema>("/dogs/graphql");
app.UseGraphQL<CatSchema>("/cats/graphql");
await app.RunAsync();

Different global authorization settings for different transports (GET/POST/WebSockets)

You may register the same endpoint multiple times if necessary to configure GET connections with certain authorization options, and POST connections with other authorization options.

var app = builder.Build();
app.UseDeveloperExceptionPage();
app.UseGraphQL("/graphql", options =>
{
    options.HandleGet = true;
    options.HandlePost = false;
    options.HandleWebSockets = false;
    options.AuthorizationRequired = false;
});
app.UseGraphQL("/graphql", options =>
{
    options.HandleGet = false;
    options.HandlePost = true;
    options.HandleWebSockets = true;
    options.AuthorizationRequired = true;   // require authentication for POST/WebSocket connections
});
await app.RunAsync();

Since POST and WebSockets can be used for query requests, it is recommended not to do the above, but instead add the authorization validation rule and add authorization metadata on the Mutation and Subscription portions of your schema, as shown below:

builder.Services.AddGraphQL(b => b
    .AddSchema<MySchema>()
    .AddSystemTextJson()
    .AddAuthorizationRule()); // add authorization validation rule

var app = builder.Build();
app.UseDeveloperExceptionPage();
app.UseGraphQL();
await app.RunAsync();

// demonstration of code-first schema; also possible with schema-first or type-first schemas
public class MySchema : Schema
{
    public MySchema(IServiceProvider provider, MyQuery query, MyMutation mutation) : base(provider)
    {
        Query = query;
        Mutation = mutation;

        mutation.Authorize(); // require authorization for any mutation request
    }
}

Customizing middleware behavior

GET/POST requests are handled directly by the GraphQLHttpMiddleware. For WebSocket requests an WebSocketConnection instance is created to dispatch incoming messages and send outgoing messages. Depending on the WebSocket sub-protocols supported by the client, the proper implementation of IOperationMessageProcessor is created to act as a state machine, processing incoming messages and sending outgoing messages through the WebSocketConnection instance.

GraphQLHttpMiddleware

The base middleware functionality is contained within GraphQLHttpMiddleware, with code to perform execution of GraphQL requests in the derived class GraphQLHttpMiddleware<TSchema>. The classes are organized as follows:

  • InvokeAsync is the entry point to the middleware. For WebSocket connection requests, execution is immediately passed to HandleWebSocketAsync.
  • Methods that start with Handle are passed the HttpContext and RequestDelegate instance, and may handle the request or pass execution to the RequestDelegate thereby skipping this execution handler. This includes methods to handle execution of single or batch queries or returning error conditions.
  • Methods that start with Write are for writing responses to the output stream.
  • Methods that start with Execute are for executing GraphQL requests.

A list of methods are as follows:

Method Description
InvokeAsync Entry point of the middleware
HandleRequestAsync Handles a single GraphQL request.
HandleBatchRequestAsync Handles a batched GraphQL request.
HandleWebSocketAsync Handles a WebSocket connection request.
BuildUserContextAsync Builds the user context based on a HttpContext.
ExecuteRequestAsync Executes a GraphQL request.
ExecuteScopedRequestAsync Executes a GraphQL request with a scoped service provider.
SelectResponseContentType Selects a content-type header for the JSON-formatted GraphQL response.
WriteErrorResponseAsync Writes the specified error message as a JSON-formatted GraphQL response, with the specified HTTP status code.
WriteJsonResponseAsync Writes the specified object (usually a GraphQL response) as JSON to the HTTP response stream.
Error handling method Description
HandleBatchedRequestsNotSupportedAsync Writes a '400 Batched requests are not supported.' message to the output.
HandleContentTypeCouldNotBeParsedErrorAsync Writes a '415 Invalid Content-Type header: could not be parsed.' message to the output.
HandleDeserializationErrorAsync Writes a '400 JSON body text could not be parsed.' message to the output.
HandleInvalidContentTypeErrorAsync Writes a '415 Invalid Content-Type header: non-supported type.' message to the output.
HandleInvalidHttpMethodErrorAsync Indicates that an unsupported HTTP method was requested. Executes the next delegate in the chain by default.
HandleWebSocketSubProtocolNotSupportedAsync Writes a '400 Invalid WebSocket sub-protocol.' message to the output.

Below is a sample of custom middleware to change the response content type to application/json, regardless of the value of the HTTP 'Accept' header or default value set in the options:

class MyMiddleware<TSchema> : GraphQLHttpMiddleware<TSchema>
    where TSchema : ISchema
{
    public MyMiddleware(
        RequestDelegate next,
        IGraphQLTextSerializer serializer,
        IDocumentExecuter<TSchema> documentExecuter,
        IServiceScopeFactory serviceScopeFactory,
        GraphQLHttpMiddlewareOptions options,
        IHostApplicationLifetime hostApplicationLifetime)
        : base(next, serializer, documentExecuter, serviceScopeFactory, options, hostApplicationLifetime)
    {
    }

    protected override string SelectResponseContentType(HttpContext context)
        => "application/json";
}

app.UseGraphQL<MyMiddleware<ISchema>>("/graphql", new GraphQLHttpMiddlewareOptions());

Be sure to derive from GraphQLHttpMiddleware<TSchema> rather than GraphQLHttpMiddleware as shown above for multi-schema compatibility.

WebSocket handler classes

The WebSocket handling code is organized as follows:

Interface / Class Description
IWebSocketConnection Provides methods to send a message to a client or close the connection.
IOperationMessageProcessor Handles incoming messages from the client.
GraphQLWebSocketOptions Provides configuration options for WebSocket connections.
WebSocketConnection Standard implementation of a message pump for OperationMessage messages across a WebSockets connection. Implements IWebSocketConnection and delivers messages to a specified IOperationMessageProcessor.
BaseSubscriptionServer Abstract implementation of IOperationMessageProcessor, a message handler for OperationMessage messages. Provides base functionality for managing subscriptions and requests.
GraphQLWs.SubscriptionServer Implementation of IOperationMessageProcessor for the graphql-transport-ws sub-protocol.
SubscriptionsTransportWs.SubscriptionServer Implementation of IOperationMessageProcessor for the graphql-ws sub-protocol.
IWebSocketAuthorizationService Allows authorization of GraphQL requests for WebSocket connections.

Typically if you wish to change functionality or support another sub-protocol you will need to perform the following:

  1. Derive from either SubscriptionServer class, modifying functionality as needed, or to support a new protocol, derive from BaseSubscriptionServer.
  2. Derive from GraphQLHttpMiddleware<T> and override CreateMessageProcessor and/or SupportedWebSocketSubProtocols as needed.
  3. Change the app.AddGraphQL() call to use your custom middleware, being sure to include an instance of the options class that your middleware requires (typically GraphQLHttpMiddlewareOptions).

There exists a few additional classes to support the above. Please refer to the source code of GraphQLWs.SubscriptionServer if you are attempting to add support for another protocol.

Additional notes / FAQ

Service scope

By default, a dependency injection service scope is created for each GraphQL execution in cases where it is possible that multiple GraphQL requests may be executing within the same service scope:

  1. A batched GraphQL request is executed.
  2. A GraphQL request over a WebSocket connection is executed.

However, field resolvers for child fields of subscription nodes will not by default execute with a service scope. Rather, the context.RequestServices property will contain a reference to a disposed service scope that cannot be used.

To solve this issue, please configure the scoped subscription execution strategy from the GraphQL.MicrosoftDI package as follows:

services.AddGraphQL(b => b
    .AddAutoSchema<Query>()
    .AddSystemTextJson()
    // configure queries for serial execution (optional)
    .AddExecutionStrategy<SerialExecutionStrategy>(OperationType.Query)
    // configure subscription field resolvers for scoped serial execution (parallel is optional)
    .AddScopedSubscriptionExecutionStrategy());

For single GET / POST requests, the service scope from the underlying HTTP context is used.

User context builder

The user context builder interface is executed only once, within the dependency injection service scope of the original HTTP request. For batched requests, the same user context instance is passed to each GraphQL execution. For WebSocket requests, the same user context instance is passed to each GraphQL subscription and data event resolver execution.

As such, do not create objects within the user context that rely on having the same dependency injection service scope as the field resolvers. Since WebSocket connections are long-lived, using scoped services within a user context builder will result in those scoped services having a matching long lifetime. You may wish to alleviate this by creating a service scope temporarily within your user context builder.

For applications that service multiple schemas, you may register IUserContextBuilder<TSchema> to create a user context for a specific schema. This is useful when you need to create a user context that is specific to a particular schema.

Mutations within GET requests

For security reasons and pursuant to current recommendations, mutation GraphQL requests are rejected over HTTP GET connections. Derive from GraphQLHttpMiddleware<T> and override ExecuteRequestAsync to prevent injection of the validation rules that enforce this behavior.

As would be expected, subscription requests are only allowed over WebSocket channels.

Handling form data for POST requests

The GraphQL over HTTP specification does not outline a procedure for transmitting GraphQL requests via HTTP POST connections using a Content-Type of application/x-www-form-urlencoded or multipart/form-data. Allowing the processing of such requests could be advantageous in avoiding CORS preflight requests when sending GraphQL queries from a web browser. Nevertheless, enabling this feature may give rise to security risks when utilizing cookie authentication, since transmitting cookies with these requests does not trigger a pre-flight CORS check. As a consequence, GraphQL.NET might execute a request and potentially modify data even when the CORS policy prohibits it, regardless of whether the sender has access to the response. This situation exposes the system to security vulnerabilities, which should be carefully evaluated and mitigated to ensure the safe handling of GraphQL requests and maintain the integrity of the data.

This functionality is activated by default to maintain backward compatibility, but it can be turned off by setting the ReadFormOnPost value to false. The next major version of GraphQL.NET Server will have this feature disabled by default, enhancing security measures.

Keep in mind that CORS pre-flight requests are also not executed for GET requests, potentially presenting a security risk. However, GraphQL query operations usually do not alter data, and mutations are refused. Additionally, the response is not expected to be readable in the browser (unless CORS checks are successful), which helps alleviate this concern.

GraphQL.NET Server supports two formats of application/x-www-form-urlencoded or multipart/form-data requests:

  1. The following keys are read from the form data and used to populate the GraphQL request:

    • query: The GraphQL query string.
    • operationName: The name of the operation to execute.
    • variables: A JSON-encoded object containing the variables for the operation.
    • extensions: A JSON-encoded object containing the extensions for the operation.
  2. The following keys are read from the form data and used to populate the GraphQL request:

    • operations: A JSON-encoded object containing the GraphQL request, in the same format as typical requests sent via application/json. This can be a single object or an array of objects if batching is enabled.
    • map: An optional JSON-encoded map of file keys to file objects. This is used to map attached files into the GraphQL request's variables property. See the section below titled 'File uploading/downloading' and the GraphQL multipart request specification for additional details. Since application/x-www-form-urlencoded cannot transmit files, this feature is only available for multipart/form-data requests.

Excessive OperationCanceledExceptions

When hosting a WebSockets endpoint, it may be common for clients to simply disconnect rather than gracefully terminating the connection — most specifically when the client is a web browser. If you log exceptions, you may notice an OperationCanceledException logged any time this occurs.

In some scenarios you may wish to log these exceptions — for instance, when the GraphQL endpoint is used in server-to-server communications — but if you wish to ignore these exceptions, simply call app.UseIgnoreDisconnections(); immediately after any exception handling or logging configuration calls. This will consume any OperationCanceledExceptions when HttpContext.RequestAborted is signaled — for a WebSocket request or any other request.

var app = builder.Build();
app.UseDeveloperExceptionPage();
app.UseIgnoreDisconnections();
app.UseWebSockets();
app.UseGraphQL();

File uploading/downloading

A common question is how to upload or download files attached to GraphQL data. For instance, storing and retrieving photographs attached to product data.

One common technique is to encode the data as Base64 and transmitting as a custom GraphQL scalar (encoded as a string value within the JSON transport). This may not be ideal, but works well for smaller files. It can also couple with response compression (details listed above) to reduce the impact of the Base64 encoding.

Another technique is to get or store the data out-of-band. For responses, this can be as simple as a Uri pointing to a location to retrieve the data, especially if the data are photographs used in a SPA client application. This may have additional security complications, especially when used with JWT bearer authentication. This answer often works well for GraphQL queries, but may not be desired during uploads (mutations).

An option for uploading is to upload file data alongside a mutation with the multipart/form-data content type as described by the GraphQL multipart request specification. Uploaded files are mapped into the GraphQL request's variables as IFormFile objects. You can use the provided FormFileGraphType scalar graph type in your GraphQL schema to access these files. The AddFormFileGraphType() builder extension method adds this scalar to the DI container and configures a CLR type mapping for it to be used for IFormFile objects.

services.AddGraphQL(b => b
    .AddAutoSchema<Query>()
    .AddFormFileGraphType()
    .AddSystemTextJson());

Please see the 'Upload' sample for a demonstration of this technique. Note that using the FormFileGraphType scalar requires that the uploaded files be sent only via the multipart/form-data content type as attached files. If you wish to also allow clients to send files as base-64 encoded strings, you can write a custom scalar better suited to your needs.

Native AOT support

GraphQL.NET Server fully supports Native AOT publishing with .NET 8.0 and later. See ASP.NET Core support for Native AOT for a list of features supported by .NET 8.0. However, GraphQL.NET only provides limited support for Native AOT publishing due to its extensive use of reflection. Please see GraphQL.NET Ahead-of-time compilation for more information.

Samples

The following samples are provided to show how to integrate this project with various typical ASP.NET Core scenarios.

Name Framework Description
Authorization .NET 8 Minimal Based on the VS template, demonstrates authorization functionality with cookie-based authentication
Basic .NET 8 Minimal Demonstrates simplest possible implementation
Complex .NET 3.1 / 6 / 8 Demonstrates older Program/Startup files and various configuration options, and multiple UI endpoints
Controller .NET 8 Minimal MVC implementation; does not include WebSocket support
Cors .NET 8 Minimal Demonstrates configuring a GraphQL endpoint to use a specified CORS policy
EndpointRouting .NET 8 Minimal Demonstrates configuring GraphQL through endpoint routing
Jwt .NET 8 Minimal Demonstrates authenticating GraphQL requests with a JWT bearer token over HTTP POST and WebSocket connections
MultipleSchemas .NET 8 Minimal Demonstrates configuring multiple schemas within a single server
Net48 .NET Core 2.1 / .NET 4.8 Demonstrates configuring GraphQL on .NET 4.8 / Core 2.1
Pages .NET 8 Minimal Demonstrates configuring GraphQL on top of a Razor Pages template
Upload .NET 8 Minimal Demonstrates uploading files via the multipart/form-data content type

Most of the above samples rely on a sample "Chat" schema. Below are some basic requests you can use to test the schema:

Queries

Return number of messages

{
  count
}

Return last message

{
  lastMessage {
    id
    message
    from
    sent
  }
}

Mutations

Add a message

mutation {
  addMessage(message: { message: "Hello", from: "John Doe" }) {
    id
  }
}

Clear all messages

mutation {
  clearMessages
}

Subscriptions

subscription {
  newMessages {
    id
    message
    from
    sent
  }
}

Contributors

This project exists thanks to all the people who contribute.

PRs are welcome! Looking for something to work on? The list of open issues is a great place to start. You can help the project by simply responding to some of the asked questions.

server's People

Contributors

alexcmoss avatar benmccallum avatar boulc avatar dependabot[bot] avatar dsilence avatar gha-zund avatar imolorhe avatar jeffw-wherethebitsroam avatar joemcbride avatar johnrutherford avatar kamsar avatar kenrim626 avatar mungojam avatar nicholashmoss avatar pekkah avatar phillip-haydon avatar rajmondburgaj avatar rehansaeed avatar ricardo-salgado-tekever avatar ronnelsantiago avatar rose-a avatar runeanielsen avatar sahb1239 avatar shane32 avatar sharppanda avatar simoncropp avatar sonic198 avatar sungam3r avatar superslowloris avatar wakemaster39 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

server's Issues

Closing websocket

Currently managing the lifetime of the websocket connection is not handled "correctly". This should be improved.

Update README

Update readme instructions to match the current version. Before update see samples for example usage.

Logging

I think we should add logging using .NET Core Logging for the following:

  • Queries
  • Errors

Target .NET Standard

Hi, any reason why you are choosing to target .NET Core? Why not target .NET Standard instead? That way it is also supported in .NET Framework

ReceiveAsync and SendAsync can be called simultaneously, but at most one outstanding operation for each of them is allowed at the same time

This is just a sneaking suspicion, but I'd loved to get a discussion going so I might be able to track down the errors I'm experiencing using this library.
When we saturate the Web Socket connection with a lot of outbound traffic we sometimes get this exception:

System.InvalidOperationException: TODO, RESX: net_Websockets_AlreadyOneOutstandingOperation;

This is although the wrong Exception Message, currently beeing fixed in dotnet core and is tracked in aspnet/WebSockets#207

The actual Exception Message is "There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can be called simultaneously, but at most one outstanding operation for each of them is allowed at the same time."

When this happens the Web Socket connection is not torn down correctly, but still open between the Client and Server. But no data is no longer coming through as the server side WebSocket client is in a bad state.

As stated in the WebSocket SendAsync-method documentation https://msdn.microsoft.com/en-us/library/system.net.websockets.websocket.sendasync(v=vs.110).aspx
"This operation will not block. The returned Task object will complete after the data has been sent on the WebSocket. Exactly one send and one receive is supported on each WebSocket object in parallel."

So why would this happen?

I suspect the culprit might be in SubscriptionHandle.cs
public void OnNext(object value) { var json = _documentWriter.Write(value); _messageWriter.WriteMessageAsync(new OperationMessage { Id = Op.Id, Type = MessageTypes.GQL_DATA, Payload = JObject.Parse(json) }).GetAwaiter().GetResult(); } where I believe you shortcut the async nature of WriteMessageAsync by calling GetAwaiter().GetResult(), which would allow multiple outstanding operations at a time.

I would love to contribute a fix to this, but I am currently a bit stuck. So any insight would be appreciated.

SignalR (Core) transport

It would be nice to have an Apollo Link for client side using the new SignalR and have a transport compatible with that client.

UserContext or authorization for WebSockets?

I saw in this comment how to implement authentication. That's more or less working, but how do I implement authorization (determining whether a user is actually authorized for a particular subscription)?

For my HTTP requests, my controller code sets the GraphQL UserContext with user information that my Query and Mutation objects can then use. (I'm using my own controller code instead of GraphQL .NET Server's HTTP middleware.) However, for WebSockets, I don't see any way for a IOperationMessageListener to access or set a context for the Query/Mutation/Subscription objects to use.

Ideas or suggestions?

Thanks!

Even though the Subscription has stopped, the Server keeps on sending data to the Client

Problem:
Data for stopped subscriptions continues to flow to the Client.

Version:
Master branch

How to reproduce:
We are using the Apollo client with React, and the faulty behavior occurs when we unmount and remount components that subscribe to data. We attached an image that shows the issue in the Network Panel in Chrome

Suggested fix:
So far we have not debugged the codebase, but just wanted to raise the issue. You might immediately know what is going on. Let us know if you need help to further investigate this issue. We can provide a small test project that reproduces the problem.

stream-after-stop

Building usercontext using service

Right now the userContext is built using the property public Func<HttpContext, object> BuildUserContext { get; set; } in GraphQLHttpOptions.

In order to request services you have to RequestServices property on the HttpContext in order to get the IServiceProvider. I would however recommend another approach a service was dependency injected into the GraphQLHttpMiddleware such that this service can use inversion of control.

I will submit a PR later for this issue.

No Settings & UserContext

At the current moment, there is no possibility to use settings in the middleware, like in the example of the GraphQL project.
Currently, I have hacked my way through the project to get this working (I added it to all Contexts etc), but it would be nice to have a clean way to get this implemented.

Question: Authentication over Websocket

We are currently trying to authenticate the clients before allowing them to set up subscriptions. A common way of doing this seems to be to add a token to the payload on the initial connection ( connection_init):

{"type": "connection_init", "payload": {"token": "123456"}}}

This approach is seen here

We have tried to access the payload through OperationMessageContext in the BuildUserContext delegate, but we are not able to get the payload for the initial connection, just the subsequent connections after the ack is received. The intention is to build a usercontext there and do further checks in the validation rules before allowing the subscription.

Is this currently possible in the library or is there another preferred way of doing an initial authentication?

No .Net 4.5 support

Hey,

Would it be possible to add support for .Net framework. Currently this only supports .net core, thanks.

WebSocketReaderPipeline goes into infinite loop of WebSocketException when connection is closed without a proper handshake

Hi,
In my case it's very easy to reproduce:

  1. Start Server & WebApp and subscribe using Apollo graphql.
  2. Refresh the page.

Server will start throwing:

'System.Net.WebSockets.WebSocketException' in System.Private.CoreLib.dll

Which even caught in the try/catch block doesn't properly close that connection but goes into infinite loop.

What helps:
Changing line 73:

while (!source.Completion.IsCompleted || !source.Completion.IsCanceled || !_socket.CloseStatus.HasValue)

to:

while (!source.Completion.IsCompleted && !source.Completion.IsCanceled && !source.Completion.IsFaulted && !this._socket.CloseStatus.HasValue)

How do we use this with Entity Framework?

The example Chat.cs uses an ISubject which works fine.

However for me project I get the data from the server using Entity Framework Core.

public class AppSubscription : ObjectGraphType<object>
{
    public AppSubscription(ICommentService commentService)
    {
        AddField(new EventStreamFieldType
        {
            Name = "commentAdded",
            Type = typeof(CommentPayload),
            Resolver = new FuncFieldResolver<Comment.Models.Comment>(c => {
                return (Comment.Models.Comment)c.Source;
            }),
            Subscriber = new EventStreamResolver<Comment.Models.Comment>(c => {
                var comments = repository.GetComments().ToObservable;

                return comments;
            }),
        });
    }
}

public class CommentsRepository {
    public virtual IQueryable<Comment> GetComments()
    {
        return Context.Set<Comment>();
    }
}

The above gives the exception, it seems to call OnCompleted before unSubscribe is assigned which results in Unsubscribe.Dispose() throwing a null reference error:

System.NullReferenceException: Object reference not set to an instance of an object.
at GraphQL.Server.Transports.WebSockets.SubscriptionHandle.OnCompleted()
at System.Reactive.AnonymousSafeObserver1.OnCompleted() at System.Reactive.Linq.ObservableImpl.Merge1.MergeConcurrent.OnCompleted()
at System.Reactive.Linq.ObservableImpl.Select2._.OnCompleted() at System.Reactive.Linq.ObservableImpl.Catch2..OnCompleted()
at System.Reactive.Linq.ObservableImpl.SelectMany2.SelectManyImpl.OnCompleted() at System.Reactive.Linq.ObservableImpl.Select2.
.OnCompleted()
at System.Reactive.Linq.ObservableImpl.ToObservable1._.LoopRec(State state, Action1 recurse)
at System.Reactive.Concurrency.Scheduler.InvokeRec1[TState](IScheduler scheduler, Pair2 pair) at System.Reactive.Concurrency.ScheduledItem2.InvokeCore()
at System.Reactive.Concurrency.CurrentThreadScheduler.Trampoline.Run(SchedulerQueue1 queue) at System.Reactive.Concurrency.CurrentThreadScheduler.Schedule[TState](TState state, TimeSpan dueTime, Func3 action)
at System.Reactive.Concurrency.LocalScheduler.Schedule[TState](TState state, Func3 action) at System.Reactive.Producer1.SubscribeRaw(IObserver1 observer, Boolean enableSafeguard) at GraphQL.Server.Transports.WebSockets.SubscriptionHandle..ctor(OperationMessage op, IObservable1 stream, IJsonMessageWriter messageWriter, IDocumentWriter documentWriter)
at GraphQL.Server.Transports.WebSockets.SubscriptionProtocolHandler1.<>c__DisplayClass14_0.<AddSubscription>b__0(String connectionId) at System.Collections.Concurrent.ConcurrentDictionary2.AddOrUpdate(TKey key, Func2 addValueFactory, Func3 updateValueFactory)
at GraphQL.Server.Transports.WebSockets.SubscriptionProtocolHandler`1.d__14.MoveNext()

If I replace the above code with the example code it works fine, so it seems to be a problem with how I am using it with EF:

public class AppSubscription : ObjectGraphType<object>
{
    private readonly ISubject<Comment.Models.Comment> _messageStream = new ReplaySubject<Comment.Models.Comment>(1);

    public AppSubscription()
    {
        AddField(new EventStreamFieldType
        {
            Name = "commentAdded",
            Type = typeof(CommentPayload),
            Resolver = new FuncFieldResolver<Comment.Models.Comment>(c => {
                return (Comment.Models.Comment)c.Source;
            }),
            Subscriber = new EventStreamResolver<Comment.Models.Comment>(c => {
                var comments = _messageStream.AsObservable();

                return comments.ToObservable();
            }),
        });
    }
}

Subscription examples not working

First, thanks for this package.

Browser: Latest Chrome

I cloned the examples and ran samples.server through visual studio.

I followed the examples with 2 different windows. The query works fine, the mutation updates the query successful when the page is reloaded as expected but the subscriptions don't work. They just hang on 'listening' with no changes in the response window.

Console error:

client.js:363 WebSocket connection to 'ws://localhost:60340/graphql' failed: Error during WebSocket handshake: Unexpected response code: 500

Playground endpoint http/https

When loading Playground through a https uri, I get the following errors:

Mixed Content: The page at 'https://<my graphql api playground>' was loaded over HTTPS, but requested an insecure resource 'http://<my graphql api>'. 
This request has been blocked; the content must be served over HTTPS.

I think the source can be found here:
https://github.com/graphql-dotnet/server/blob/master/src/Ui.Playground/Internal/playground.cshtml#L54
But I don't know how to make the check to see if the endpoint should be http or https.

[Docs] Updates?

Just following the install guide and there's a few things that may be outdated or I need help with.

  • Looks like it's being released on the nuget gallery now, do we need the myget feed?
  • Looks like dotnet add command requires the package specifier now. dotnet add package ...
  • Looks like the UI (GraphiQL and Playground) aren't included in the nuget package. I couldn't use them as the setup suggested. Will they become available in this package or even a new one?

Repo rename

@joemcbride maybe rename this repo to graphql-dotnet-server as it closer matches the functionality. It already contains a middlware for both normal json queries and subscription transport. I'll separate the middleware for running normal queries to it's own project and nuget package. That should help with some of the requests for "builtin" middlware on main repo.

Ui.Abstractions

In order to simplyficate the process of creating new UI packages.
We should create a lib that uses all this kind of abstractions.
Most of the code is the same except the *.cshtml

Not using UseMiddleWare api

The middleware is not currently using the UseMiddleWare-api, which I think it should be.
This can be solved in the 'Startup.Configure'-function, the following way:

app.UseMiddleware<<ChainCompanySchema>>();

And next to that, having this function in the ITransport-interface (and inheriting classes):
async Task Invoke(HttpContext context)

Issue with Websockets and Firefox

I was getting the following error in most of the browser .
image

Chrome and opera allows to ignore but not in IE and Firefox . There is a possible fix given in the following URL .
Can I Provide a pull request for the same.

Union of two libraries

Hi, i have been working on integrating the GraphQL lib in AspNetCore https://github.com/deinok/graphql-server-aspnetcore

We are using this in production in our org and i think that it would be a great idea merge the two libs.
Taking the best of the two. For Example:

  • My version have GraphiQL ready.
  • Your version have a good Subscription support.
  • Etc...

Also will be happy to help maintaining this library.

Handle protocol messages

  • public const string GQL_CONNECTION_INIT = "connection_init"; // Client -> Server
  • public const string GQL_CONNECTION_ACK = "connection_ack"; // Server -> Client
  • public const string GQL_CONNECTION_ERROR = "connection_error"; // Server -> Client
  • public const string GQL_CONNECTION_KEEP_ALIVE = "ka"; // Server -> Client
  • public const string GQL_CONNECTION_TERMINATE = "connection_terminate"; // Client -> Server
  • public const string GQL_START = "start"; // Client -> Server
  • public const string GQL_DATA = "data"; // Server -> Client
  • public const string GQL_ERROR = "error"; // Server -> Client
  • public const string GQL_COMPLETE = "complete"; // Server -> Client
  • public const string GQL_STOP = "stop"; // Client -> Server

WebSockets stop responding if browser closes connection

We noticed that the WebSockets middleware would stop responding (and enter an endless loop of errors) if a client closed its connection.

It looks like the problem is here. Unless I'm mistaken, the loop should abort if completed, canceled, or closed, so each || should be &&.

nuget publish

Now that 3.0.0 is out. Would it be possible to publish it to nuget?

Question: When working with Scoped services

This is not an issue, just a general question I hope someone can provide some insight for.

We are currently trying to create a Graphql endpoint with an Entity Framework based service layer. Furthermore we try to utilize Dependency Injection in order to inject the dbContext in our Mutation-, Query- and Subscription-implementations. Using Dependency Injection has although proven difficult as the injected dbContext is Scoped by nature. This seems to conflict with how this library require(?) that everything is registered as a Singleton. That said, I believe I've solved it for the HTTP-middleware by creating a custom HTTP-Middleware where the Schema is resolved in the Invoke-method instead of the constructor. The WebSocket-Middleware is although a bit more complicated, and we've not found any elegant solutions to this.

So any advice on using Subscriptions together with a Schema that relies on scoped dependencies will be much appreciated.

Nice work here!

Not really an issue, other than an issue of this being AWESOME.

Ran the sample, worked great. Kudos! 👍

Mutations over Websocket

According to the Apollo web socket transport implementation, a websocket is suppose to be able to handle Queries and Mutation in addition to Subscriptions. I was playing around with attempting to send a mutation over the websocket channel but the response back from this is always a GQL_ERROR package with the payload of both the data and the errors being null, which makes it very difficult to track down where the error actually occurs.

app.UseWebSockets()

From when I last used websockets, I believe the app.UseWebSockets() adds it using the "feature" collection. So I believe app.UseWebSockets() can be called multiple times and it will only be added once. So you should be able to add that to your endpoint extension so users don't have to do it themselves.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.