Skip to content

Version 5.6.0 accepted content type regression when followSpec == true #443

@samyonr

Description

@samyonr

The new 5.6.0 version has some degradation in dealing with the accepted content type, when followSpec == true (sorry for that, I've missed all these cases myself):

  1. Running dotnet graphql update -u http://localhost:5656/graphql (see here: https://chillicream.com/docs/strawberryshake/v13/tooling), sets a single accepted content type (i.e. .Count == 1), but it's actually multiple values, comma separated. In other words, context.Request.Headers.Accept == "application/graphql-response+json, application/json, text/event-stream"
    So it should be split, and each sub-string should be checked.
  2. Postman with introspection sends this accepted content type: */* (indicating all media types). That also isn't accepted well, and 406 is received.

I've solved it locally with:

var acceptedContentType = context.Request.Headers.Accept;
if (followSpec)
{
	if (acceptedContentType.Count > 0 && acceptedContentType.Any(ct => ct == "*/*"))
	{
		acceptedContentType = $"{APP_GQL_TYPE_START}";
	}
	// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md
	// "May reply with error if not supplied" choosing not to
	else if (
		acceptedContentType.Count > 0 // each accept header can have multiple values, comma separated
		&& !acceptedContentType.Any(ct =>
			ct is not null && ct.Split(",",
					StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)
				.Any(h => h.StartsWith(APP_JSON_TYPE_START, StringComparison.InvariantCulture)))
		&& !acceptedContentType.Any(ct => ct is not null && ct
			.Split(",", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).Any(h =>
				h.StartsWith(APP_GQL_TYPE_START, StringComparison.InvariantCulture)))
	)
	{
		context.Response.StatusCode = StatusCodes.Status406NotAcceptable;
		return;
	}
}

then:

if (followSpec)
{
	var requestedType = acceptedContentType.FirstOrDefault(ct =>
		ct?.Split(",", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)
			.FirstOrDefault(t =>
				t.StartsWith(APP_JSON_TYPE_START, StringComparison.InvariantCulture) ||
				t.StartsWith(APP_GQL_TYPE_START, StringComparison.InvariantCulture)) != null
	);
	requestedType = requestedType?.Split(",",
			StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)
		.FirstOrDefault(t =>
			t.StartsWith(APP_JSON_TYPE_START, StringComparison.InvariantCulture) ||
			t.StartsWith(APP_GQL_TYPE_START, StringComparison.InvariantCulture));
	context.Response.ContentType = requestedType ?? $"{APP_GQL_TYPE_START}; charset=utf-8";
}
else
{
	context.Response.ContentType = $"{APP_JSON_TYPE_START}; charset=utf-8";
}

I'm sure the above can be prettified, but in general, I'm checking if any of the received accepted content-type headers (so far, only one has been received) is */*. If yes, I use application/graphql-response+json, hopefully, that's generally correct. If not, I'm checking each one (again, so far, only one is received in my setup), splitting it by ",", removing spaces, and then I do the original logic on each sub-section.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions