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.
GraphQL request and responses are typically in JSON format even though the GraphQL Spec doesn't mandate any format. All data fetching and uploading can be done easily with GraphQL and responses can also use GZIP for compression. One thing GraphQL lacks (or rather doesn't have a standard implementation for) is File Uploads. Why/Why not?
While quite poorly documented in the GraphQL.NET docs (at the time of writing), functionality for Relay compatible endpoints has been implemented. Some of this logic resides in the main graphql-dotnet package, while other tools and helpers reside in the relay package.
The GraphQL connection endpoints can be compared with traditional Field based ones. They show that the api offers access to a certain type of data. The major difference is that a Connection graph types comes with a bit more complexity to handle the added complexity of pagination.
GraphQL has become quite popular due to its various features fixing under/over fetching issues. It also allows for easy caching, federation, non-versioning APIs, subscriptions etc,.
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With