@@ -39,41 +39,71 @@ public List<AttrAttribute> Get(RelationshipAttribute relationship = null)
39
39
_selectedRelationshipFields . TryGetValue ( relationship , out var fields ) ;
40
40
return fields ;
41
41
}
42
-
42
+
43
43
/// <inheritdoc/>
44
44
public virtual void Parse ( KeyValuePair < string , StringValues > queryParameter )
45
- {
46
- // expected: fields[TYPE ]=prop1,prop2
47
- var typeName = queryParameter . Key . Split ( QueryConstants . OPEN_BRACKET , QueryConstants . CLOSE_BRACKET ) [ 1 ] ;
45
+ { // expected: articles?fields=prop1,prop2
46
+ // articles? fields[articles ]=prop1,prop2 <-- this form in invalid UNLESS "articles" is actually a relationship on Article
47
+ // articles?fields[relationship]=prop1,prop2
48
48
var fields = new List < string > { nameof ( Identifiable . Id ) } ;
49
+ fields . AddRange ( ( ( string ) queryParameter . Value ) . Split ( QueryConstants . COMMA ) ) ;
49
50
50
- var relationship = _requestResource . Relationships . SingleOrDefault ( a => a . Is ( typeName ) ) ;
51
- if ( relationship == null && string . Equals ( typeName , _requestResource . EntityName , StringComparison . OrdinalIgnoreCase ) == false )
52
- throw new JsonApiException ( 400 , $ "fields[{ typeName } ] is invalid") ;
51
+ var keySplitted = queryParameter . Key . Split ( QueryConstants . OPEN_BRACKET , QueryConstants . CLOSE_BRACKET ) ;
53
52
54
- fields . AddRange ( ( ( string ) queryParameter . Value ) . Split ( QueryConstants . COMMA ) ) ;
55
- foreach ( var field in fields )
56
- {
57
- if ( relationship != default )
58
- {
59
- var relationProperty = _contextEntityProvider . GetContextEntity ( relationship . DependentType ) ;
60
- var attr = relationProperty . Attributes . SingleOrDefault ( a => a . Is ( field ) ) ;
61
- if ( attr == null )
62
- throw new JsonApiException ( 400 , $ "'{ relationship . DependentType . Name } ' does not contain '{ field } '.") ;
53
+ if ( keySplitted . Count ( ) == 1 )
54
+ { // input format: fields=prop1,prop2
55
+ foreach ( var field in fields )
56
+ RegisterRequestResourceField ( field ) ;
57
+ }
58
+ else
59
+ { // input format: fields[articles]=prop1,prop2
60
+ string navigation = keySplitted [ 1 ] ;
61
+ // it is possible that the request resource has a relationship
62
+ // that is equal to the resource name, like with self-referering data types (eg directory structures)
63
+ // if not, no longer support this type of sparse field selection.
64
+ if ( navigation == _requestResource . EntityName && ! _requestResource . Relationships . Any ( a => a . Is ( navigation ) ) )
65
+ throw new JsonApiException ( 400 , $ "Use \" ?fields=...\" instead of \" fields[{ navigation } ]\" :" +
66
+ $ " the square bracket navigations is now reserved " +
67
+ $ "for relationships only. See https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/555#issuecomment-543100865") ;
63
68
64
- if ( ! _selectedRelationshipFields . TryGetValue ( relationship , out var registeredFields ) )
65
- _selectedRelationshipFields . Add ( relationship , registeredFields = new List < AttrAttribute > ( ) ) ;
66
- registeredFields . Add ( attr ) ;
67
- }
68
- else
69
- {
70
- var attr = _requestResource . Attributes . SingleOrDefault ( a => a . Is ( field ) ) ;
71
- if ( attr == null )
72
- throw new JsonApiException ( 400 , $ "' { _requestResource . EntityName } ' does not contain ' { field } '." ) ;
69
+ if ( navigation . Contains ( QueryConstants . DOT ) )
70
+ throw new JsonApiException ( 400 , $ "fields[ { navigation } ] is not valid: deeply nested sparse field selection is not yet supported." ) ;
71
+
72
+ var relationship = _requestResource . Relationships . SingleOrDefault ( a => a . Is ( navigation ) ) ;
73
+ if ( relationship == null )
74
+ throw new JsonApiException ( 400 , $ " \" { navigation } \" in \" fields[ { navigation } ] \" is not a valid relationship of { _requestResource . EntityName } " ) ;
75
+
76
+ foreach ( var field in fields )
77
+ RegisterRelatedResourceField ( field , relationship ) ;
73
78
74
- ( _selectedFields = _selectedFields ?? new List < AttrAttribute > ( ) ) . Add ( attr ) ;
75
- }
76
79
}
77
80
}
81
+
82
+ /// <summary>
83
+ /// Registers field selection queries of the form articles?fields[author]=first-name
84
+ /// </summary>
85
+ private void RegisterRelatedResourceField ( string field , RelationshipAttribute relationship )
86
+ {
87
+ var relationProperty = _contextEntityProvider . GetContextEntity ( relationship . DependentType ) ;
88
+ var attr = relationProperty . Attributes . SingleOrDefault ( a => a . Is ( field ) ) ;
89
+ if ( attr == null )
90
+ throw new JsonApiException ( 400 , $ "'{ relationship . DependentType . Name } ' does not contain '{ field } '.") ;
91
+
92
+ if ( ! _selectedRelationshipFields . TryGetValue ( relationship , out var registeredFields ) )
93
+ _selectedRelationshipFields . Add ( relationship , registeredFields = new List < AttrAttribute > ( ) ) ;
94
+ registeredFields . Add ( attr ) ;
95
+ }
96
+
97
+ /// <summary>
98
+ /// Registers field selection queries of the form articles?fields=title
99
+ /// </summary>
100
+ private void RegisterRequestResourceField ( string field )
101
+ {
102
+ var attr = _requestResource . Attributes . SingleOrDefault ( a => a . Is ( field ) ) ;
103
+ if ( attr == null )
104
+ throw new JsonApiException ( 400 , $ "'{ _requestResource . EntityName } ' does not contain '{ field } '.") ;
105
+
106
+ ( _selectedFields = _selectedFields ?? new List < AttrAttribute > ( ) ) . Add ( attr ) ;
107
+ }
78
108
}
79
109
}
0 commit comments