Skip to content

Handle parameters in Accepts mediatypes in DefaultProblemDetailsWriter #54158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

MythoclastBM
Copy link
Contributor

@MythoclastBM MythoclastBM commented Feb 21, 2024

Address #52577

Fixed issue where DefaultProblemDetailWriter doesn't handle media type subsets. Added test cases including media type subsets. The corresponds with Issue#52577 tagged help wanted.

@ghost ghost added the area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions label Feb 21, 2024
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Feb 21, 2024
@MythoclastBM
Copy link
Contributor Author

#52577

_problemDetailsJsonMediaType.IsSubsetOf(acceptHeaderValue))
//One of the media types needs to be checked if it's a subset of the accepted header value
//Application/json is a subset of */* but */* is not a subset of application/json
if(acceptHeaderValue.IsSubsetOf(_jsonMediaType) || acceptHeaderValue.IsSubsetOf(_problemDetailsJsonMediaType) || _jsonMediaType.IsSubsetOf(acceptHeaderValue))
Copy link
Member

@adityamandaleeka adityamandaleeka Feb 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason you left the third clause (_jsonMediaType.IsSubsetOf(acceptHeaderValue)) in this check?

Copy link
Contributor Author

@MythoclastBM MythoclastBM Feb 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. If the accepted media header is a superset of jsonMediaHeader it will return false when it should return true. With the absence of the check: the test for case */* will fail.

@@ -600,6 +600,8 @@ await writer.WriteAsync(new ProblemDetailsContext()
[InlineData("application/*")]
Copy link
Member

@adityamandaleeka adityamandaleeka Feb 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we meant to be able to write this at all then? cc @captainsafia

Seems like this shouldn't work.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the RFC, it looks like applicaton/*is a content-type that we should match against.

The asterisk "" character is used to group media types into ranges, with "/" indicating all media types and "type/" indicating all subtypes of that type. The media-range can include media type parameters that are applicable to that range.

I believe some of the weirdness here might be related to the way that comparisons happen for parameters in the IsSubsetOf extension method. Previously, it was checking to see if all the parameters defined in acceptsHeaderValue appeared in _jsonMediaType. Since _jsonMediaType has no parameters defined, this would always return false, hence the inversion eeded.

We need to retain the _jsonMediaType.IsSubsetOf(acceptHeaderValue) because MediaTypeHeaderValue handles comparisons for subtypes differently (it requires the argument provided to be more permissive).

TL;DR: I think the issue here is the confusing IsSubsetOf APIs on MediaTypeHeaderValue.

@adityamandaleeka
Copy link
Member

Thanks @MythoclastBM! I left some comments. Apart from some minor style stuff, I'm curious why you left an extra clause in the check that was there before. I assumed you'd just flip the two existing ones.

@captainsafia captainsafia changed the title fixed DefaultProblemDetailsWriter Doesn't Handle Media Type Subsets #52577 Fix DefaultProblemDetailsWriter doesn't handle media type subsets Feb 28, 2024
@@ -600,6 +600,8 @@ await writer.WriteAsync(new ProblemDetailsContext()
[InlineData("application/*")]
[InlineData("application/json")]
[InlineData("application/problem+json")]
[InlineData("application/json; charset=utf-8")]
[InlineData("application/json; v=1.0")]
public void CanWrite_ReturnsTrue_WhenJsonAccepted(string contentType)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To cover our bases, can we add a check for */* as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already do.

@captainsafia captainsafia changed the title Fix DefaultProblemDetailsWriter doesn't handle media type subsets Handle parameters in Accepts mediatypes in DefaultProblemDetailsWriter Mar 4, 2024
@captainsafia captainsafia merged commit a103cb9 into dotnet:main Mar 4, 2024
@dotnet-policy-service dotnet-policy-service bot added this to the 9.0-preview3 milestone Mar 4, 2024
@tore-hammervoll
Copy link

Will we see this change in a patch release of .Net 8? Or do we have to wait for .Net 9?

@captainsafia
Copy link
Member

@tore-hammervoll This change isn't being back ported. We typically only backport things if there is a big enough demand for it. If this is blocking you, let me know.

@tore-hammervoll
Copy link

We got around most cases of this issue by ensuring we write the problems with DefaultApiProblemDetailsWriter instead of DefaultProblemDetailsWriter. This handles content negotiation in a much more consistent way. The missing piece of the puzzle was passing the original endpoint metadata on to the IProblemDetailsService.TryWrite, from our custom IExceptionHandler implementations similarly to the way it's done in ExceptionHandlerMiddlewareImpl:

AdditionalMetadata = exceptionHandlerFeature.Endpoint?.Metadata,

The only case I've discovered that falls back to the DefaultProblemDetailsWriter now is requests to paths that don't hit any controller action. These errors will then use the lacking content negotiation of this implementation, and may result in an empty body or a simple textual error. If a problem details response is written, it will also be missing the traceId field due to #54325. We don't use minimal apis yet, only controllers still, so this issue is very minor for us.

@captainsafia
Copy link
Member

The only case I've discovered that falls back to the DefaultProblemDetailsWriter now is requests to paths that don't hit any controller action.

I see. If this becomes a serious problem, you can file an issue on the repo and we can make a decision around backporting with that specific context in mind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants