-
Notifications
You must be signed in to change notification settings - Fork 158
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
add omit-values supports #657
base: release-8.x
Are you sure you want to change the base?
Conversation
/// </summary> | ||
/// <param name="request">The <see cref="HttpRequest"/> instance to access.</param> | ||
/// <returns>The <see cref="OmitValuesKind"/> from the request.</returns> | ||
public static OmitValuesKind GetOmitValuesKind(this HttpRequest request) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this indicate that it is a header value, or even a Prefer Header. Something like GetOmitValueHeaderKind
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Emm, actually it's not only from request header, it's also from ODataOption setting if there's no setting.
But, I am curious which one is a higher priority. @mikepizzo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about GetOmitValuesPreferenceKind
, or even just GetOmitValuesPreference
with a rename on the enum to OmitValuesPreference
?
src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs
Outdated
Show resolved
Hide resolved
…s.cs Co-authored-by: Christof Sprenger <[email protected]>
@@ -71,6 +72,44 @@ public static ODataOptions ODataOptions(this HttpRequest request) | |||
return request.HttpContext.ODataOptions(); | |||
} | |||
|
|||
/// <summary> | |||
/// Returns the <see cref="OmitValuesKind"/> from the request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency with the method name, I'd suggest a small rename:
/// Returns the <see cref="OmitValuesKind"/> from the request. | |
/// Gets the <see cref="OmitValuesKind"/> from the request. |
/// Returns the <see cref="OmitValuesKind"/> from the request. | ||
/// </summary> | ||
/// <param name="request">The <see cref="HttpRequest"/> instance to access.</param> | ||
/// <returns>The <see cref="OmitValuesKind"/> from the request.</returns> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also document the exception here?
/// <returns>The <see cref="OmitValuesKind"/> from the request.</returns> | |
/// <returns>The <see cref="OmitValuesKind"/> from the request.</returns> | |
/// <exception cref="ArgumentNullException"><paramref name="request"/> is <see langword="null"/>.</exception> |
/// <returns>The <see cref="OmitValuesKind"/> from the request.</returns> | ||
public static OmitValuesKind GetOmitValuesKind(this HttpRequest request) | ||
{ | ||
if (request == null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For constant comparisons, I'd suggest using constant pattern matching on new code:
if (request == null) | |
if (request is null) |
throw Error.ArgumentNull(nameof(request)); | ||
} | ||
|
||
// The 'Prefer' header from client has the top priority |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of having code blocks with comments such as this, I'd suggest extracting each "strategy" into at least a local function, and then calling them in the explicit fallback order, something like:
return GetOmitValuePreferenceFromHeaders() ??
GetOmitValuePreferenceFromODataConfiguration();
This not only cleanly isolates each "strategy" into a named method (most likely removing the need for the comment itself), but also models the fallback logic in a more clean and direct way that is more apparent to the reader.
/// <summary> | ||
/// Not set, unknown | ||
/// </summary> | ||
Unknown, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered just not having this entry at all? Since it is completely invalid/not part of the spec, I don't think it is a good idea to even map it.
If there are any places where the value is optional, a nullable OmitValuesKind
could be used.
prefer_applied = values.FirstOrDefault(); | ||
} | ||
|
||
string omitValuesHead = omitValuesKind == OmitValuesKind.Nulls ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
string omitValuesHead = omitValuesKind == OmitValuesKind.Nulls ? | |
string omitValuesHeaderValue = omitValuesKind == OmitValuesKind.Nulls ? |
|
||
string omitValuesHead = omitValuesKind == OmitValuesKind.Nulls ? | ||
"omit-values=nulls" : | ||
"omit-values=defaults"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of handling these with raw strings, have you considered creating a small model and/or factory method for it?
Imagine something like
public sealed class PreferHeader
{
public static PreferHeader OmitValues(OmitValuesKind kind)
{
return new("omit-values", kind.ToText())
}
}
This would remove the need to ever handle raw strings. You can parse values into this model and use factory methods to create strongly typed instances for each preference OData has.
} | ||
else | ||
{ | ||
response.Headers[PreferAppliedHeaderName] = $"{prefer_applied},{omitValuesHead}"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any way to avoid this manual string concatenation by relying on the StringValues
type itself to do it?
We could probably "add" the omit setting to the existing StringValues
instance and then write that directly.
// If there are many "Preference-Applied" headers, pick up the first one. | ||
prefer_applied = values.FirstOrDefault(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate why we are picking the first one? Shouldn't we just append ours to the end of the existing list no matter how many there are?
/// <summary> | ||
/// Gets or sets a omit values kind for the property value serialization. | ||
/// </summary> | ||
public OmitValuesKind OmitValuesKind { get; set; } = OmitValuesKind.Unknown; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per other comments, I think it would be better if this was a nullable:
public OmitValuesKind OmitValuesKind { get; set; } = OmitValuesKind.Unknown; | |
public OmitValuesKind? OmitValuesKind { get; set; }; |
if (preferHeader.Contains("omit-values=nulls", StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
return OmitValuesKind.Nulls; | ||
} | ||
|
||
if (preferHeader.Contains("omit-values=defaults", StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
return OmitValuesKind.Defaults; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a possibility for both 'omit-values=defaults' and 'omit-values=nulls' to be added as headers in a request? In that case won't one of them be ignored?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add some tests
OData specs has the following at: http://docs.oasis-open.org/odata/odata/v4.01/cs01/part1-protocol/odata-v4.01-cs01-part1-protocol.html#_Toc505771137
This PR is used to discuss the service side support for omit_values prefer header.