Search code examples
asp.netasp.net-mvcgraphqlrelayjsgraphql-dotnet

File Upload with Relay and graphql-dotnet


I'm trying to build a mutation within relay that includes a file. As soon as I implement the getFiles() method referenced here: https://facebook.github.io/relay/docs/api-reference-relay-mutation.html#getfiles

Relay sends a multipart request causing a 415 error from ASP.NET Core MVC.

I'm looking for a working example, similar to "How would you do file uploads in a React-Relay app?" with the graphql-dotnet library.


Solution

  • The GraphQL Endpoint wasn't accepting the multi-part form mimetype because it wasn't JSON. I was able to work with the files once I got them into graphql-dotnet via the RootObject and the context that's available in the mutation. What was throwing me off was MVC.

    So I wrote a simple Filter:

    public class RelayResourceFilter : IResourceFilter
    {
        private readonly string jsonMediaType = "application/json";
    
        public void OnResourceExecuted(ResourceExecutedContext context)
        {
        }
    
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
    
    
            if (!string.Equals(MediaTypeHeaderValue.Parse(context.HttpContext.Request.ContentType).MediaType,
                this.jsonMediaType, StringComparison.OrdinalIgnoreCase))
            {
                var encoder = JavaScriptEncoder.Create();
                var variables = encoder.Encode(context.HttpContext.Request.Form["variables"]);
                var query = encoder.Encode(context.HttpContext.Request.Form["query"]);
                var body = $"{{\"query\":\"{query}\", \"variables\":\"{variables}\"}}";
    
                byte[] requestData = Encoding.UTF8.GetBytes(body);
                context.HttpContext.Request.Body = new MemoryStream(requestData);
                context.HttpContext.Request.ContentType = this.jsonMediaType;
            }
        }
    }
    

    registered it:

    services.AddScoped<RelayResourceFilter>();
    

    and then applied it like so in the Controller:

    [ServiceFilter(typeof(RelayResourceFilter))]
        public async Task<ExecutionResult> Post([FromBody]GraphQLQuery query, bool? useErrorCode)
    {
    var files = this.Request.HasFormContentType ? this.Request.Form.Files : null;
    // ... assignment to Root Object
    }