1
1
using System ;
2
+ using System . ComponentModel ;
2
3
using System . Linq ;
3
4
using System . Threading . Tasks ;
4
5
using JsonApiDotNetCore . Configuration ;
5
6
using JsonApiDotNetCore . Internal ;
6
7
using JsonApiDotNetCore . Internal . Contracts ;
7
8
using JsonApiDotNetCore . Managers . Contracts ;
8
9
using Microsoft . AspNetCore . Http ;
10
+ using Microsoft . AspNetCore . Http . Features ;
9
11
using Microsoft . AspNetCore . Routing ;
10
12
using Microsoft . Extensions . Primitives ;
11
13
@@ -21,6 +23,7 @@ public class CurrentRequestMiddleware
21
23
private ICurrentRequest _currentRequest ;
22
24
private IResourceGraph _resourceGraph ;
23
25
private IJsonApiOptions _options ;
26
+ private RouteValueDictionary _routeValues ;
24
27
private IControllerResourceMapping _controllerResourceMapping ;
25
28
26
29
public CurrentRequestMiddleware ( RequestDelegate next )
@@ -39,12 +42,15 @@ public async Task Invoke(HttpContext httpContext,
39
42
_controllerResourceMapping = controllerResourceMapping ;
40
43
_resourceGraph = resourceGraph ;
41
44
_options = options ;
45
+ _routeValues = httpContext . GetRouteData ( ) . Values ;
42
46
var requestResource = GetCurrentEntity ( ) ;
43
47
if ( requestResource != null )
44
48
{
45
- _currentRequest . SetRequestResource ( GetCurrentEntity ( ) ) ;
49
+ _currentRequest . SetRequestResource ( requestResource ) ;
46
50
_currentRequest . IsRelationshipPath = PathIsRelationship ( ) ;
47
- _currentRequest . BasePath = GetBasePath ( _currentRequest . GetRequestResource ( ) . ResourceName ) ;
51
+ _currentRequest . BasePath = GetBasePath ( requestResource . ResourceName ) ;
52
+ _currentRequest . BaseId = GetBaseId ( ) ;
53
+ _currentRequest . RelationshipId = GetRelationshipId ( ) ;
48
54
}
49
55
50
56
if ( IsValid ( ) )
@@ -53,50 +59,127 @@ public async Task Invoke(HttpContext httpContext,
53
59
}
54
60
}
55
61
56
- private string GetBasePath ( string entityName )
62
+ private string GetBaseId ( )
57
63
{
58
- var r = _httpContext . Request ;
59
- if ( _options . RelativeLinks )
64
+ var resource = _currentRequest . GetRequestResource ( ) ;
65
+ var individualComponents = SplitCurrentPath ( ) ;
66
+ if ( individualComponents . Length < 2 )
60
67
{
61
- return GetNamespaceFromPath ( r . Path , entityName ) ;
68
+ return null ;
62
69
}
63
- return $ "{ r . Scheme } ://{ r . Host } { GetNamespaceFromPath ( r . Path , entityName ) } ";
70
+ var indexOfResource = individualComponents . ToList ( ) . FindIndex ( c => c == resource . ResourceName ) ;
71
+ var baseId = individualComponents . ElementAtOrDefault ( indexOfResource + 1 ) ;
72
+ if ( baseId == null )
73
+ {
74
+ return null ;
75
+ }
76
+ CheckIdType ( baseId , resource . IdentityType ) ;
77
+ return baseId ;
64
78
}
79
+ private string GetRelationshipId ( )
80
+ {
81
+ var resource = _currentRequest . GetRequestResource ( ) ;
82
+ if ( ! _currentRequest . IsRelationshipPath )
83
+ {
84
+ return null ;
85
+ }
86
+ var components = SplitCurrentPath ( ) ;
87
+ var toReturn = components . ElementAtOrDefault ( 4 ) ;
65
88
66
- internal static string GetNamespaceFromPath ( string path , string entityName )
89
+ if ( toReturn == null )
90
+ {
91
+ return null ;
92
+ }
93
+ var relType = _currentRequest . RequestRelationship . RightType ;
94
+ var relResource = _resourceGraph . GetResourceContext ( relType ) ;
95
+ var relIdentityType = relResource . IdentityType ;
96
+ CheckIdType ( toReturn , relIdentityType ) ;
97
+ return toReturn ;
98
+ }
99
+ private string [ ] SplitCurrentPath ( )
67
100
{
68
- var entityNameSpan = entityName . AsSpan ( ) ;
69
- var pathSpan = path . AsSpan ( ) ;
70
- const char delimiter = '/' ;
71
- for ( var i = 0 ; i < pathSpan . Length ; i ++ )
101
+ var path = _httpContext . Request . Path . Value ;
102
+ var ns = $ "/{ GetNameSpace ( ) } ";
103
+ var nonNameSpaced = path . Replace ( ns , "" ) ;
104
+ nonNameSpaced = nonNameSpaced . Trim ( '/' ) ;
105
+ var individualComponents = nonNameSpaced . Split ( '/' ) ;
106
+ return individualComponents ;
107
+ }
108
+
109
+
110
+ private void CheckIdType ( string value , Type idType )
111
+ {
112
+ try
72
113
{
73
- if ( pathSpan [ i ] . Equals ( delimiter ) )
114
+ var converter = TypeDescriptor . GetConverter ( idType ) ;
115
+ if ( converter != null )
74
116
{
75
- var nextPosition = i + 1 ;
76
- if ( pathSpan . Length > i + entityNameSpan . Length )
117
+ if ( ! converter . IsValid ( value ) )
77
118
{
78
- var possiblePathSegment = pathSpan . Slice ( nextPosition , entityNameSpan . Length ) ;
79
- if ( entityNameSpan . SequenceEqual ( possiblePathSegment ) )
119
+ throw new JsonApiException ( 500 , $ "We could not convert the id '{ value } '") ;
120
+ }
121
+ else
122
+ {
123
+ if ( idType == typeof ( int ) )
80
124
{
81
- // check to see if it's the last position in the string
82
- // or if the next character is a /
83
- var lastCharacterPosition = nextPosition + entityNameSpan . Length ;
84
-
85
- if ( lastCharacterPosition == pathSpan . Length || pathSpan . Length >= lastCharacterPosition + 2 && pathSpan [ lastCharacterPosition ] . Equals ( delimiter ) )
125
+ if ( ( int ) converter . ConvertFromString ( value ) < 0 )
86
126
{
87
- return pathSpan . Slice ( 0 , i ) . ToString ( ) ;
127
+ throw new JsonApiException ( 500 , "The base ID is an integer, and it is negative." ) ;
88
128
}
89
129
}
90
130
}
91
131
}
92
132
}
133
+ catch ( NotSupportedException )
134
+ {
135
+
136
+ }
93
137
94
- return string . Empty ;
138
+ }
139
+
140
+ private string GetBasePath ( string resourceName = null )
141
+ {
142
+ var r = _httpContext . Request ;
143
+ if ( _options . RelativeLinks )
144
+ {
145
+ return GetNameSpace ( resourceName ) ;
146
+ }
147
+ var ns = GetNameSpace ( resourceName ) ;
148
+ var customRoute = GetCustomRoute ( r . Path . Value , resourceName ) ;
149
+ var toReturn = $ "{ r . Scheme } ://{ r . Host } /{ ns } ";
150
+ if ( customRoute != null )
151
+ {
152
+ toReturn += $ "/{ customRoute } ";
153
+ }
154
+ return toReturn ;
155
+ }
156
+
157
+ private object GetCustomRoute ( string path , string resourceName )
158
+ {
159
+ var ns = GetNameSpace ( ) ;
160
+ var trimmedComponents = path . Trim ( '/' ) . Split ( '/' ) . ToList ( ) ;
161
+ var resourceNameIndex = trimmedComponents . FindIndex ( c => c == resourceName ) ;
162
+ var newComponents = trimmedComponents . Take ( resourceNameIndex ) . ToArray ( ) ;
163
+ var customRoute = string . Join ( '/' , newComponents ) ;
164
+ if ( customRoute == ns )
165
+ {
166
+ return null ;
167
+ }
168
+ else
169
+ {
170
+ return customRoute ;
171
+ }
172
+ }
173
+
174
+ private string GetNameSpace ( string resourceName = null )
175
+ {
176
+
177
+ return _options . Namespace ;
95
178
}
96
179
97
180
protected bool PathIsRelationship ( )
98
181
{
99
- var actionName = ( string ) _httpContext . GetRouteData ( ) . Values [ "action" ] ;
182
+ var actionName = ( string ) _routeValues [ "action" ] ;
100
183
return actionName . ToLower ( ) . Contains ( "relationships" ) ;
101
184
}
102
185
@@ -124,7 +207,9 @@ private bool IsValidAcceptHeader(HttpContext context)
124
207
foreach ( var acceptHeader in acceptHeaders )
125
208
{
126
209
if ( ContainsMediaTypeParameters ( acceptHeader ) == false )
210
+ {
127
211
continue ;
212
+ }
128
213
129
214
FlushResponse ( context , 406 ) ;
130
215
return false ;
@@ -165,16 +250,21 @@ private void FlushResponse(HttpContext context, int statusCode)
165
250
/// <returns></returns>
166
251
private ResourceContext GetCurrentEntity ( )
167
252
{
168
- var controllerName = ( string ) _httpContext . GetRouteValue ( "controller" ) ;
253
+ var controllerName = ( string ) _routeValues [ "controller" ] ;
169
254
if ( controllerName == null )
255
+ {
170
256
return null ;
257
+ }
171
258
var resourceType = _controllerResourceMapping . GetAssociatedResource ( controllerName ) ;
172
259
var requestResource = _resourceGraph . GetResourceContext ( resourceType ) ;
173
260
if ( requestResource == null )
261
+ {
174
262
return requestResource ;
175
- var rd = _httpContext . GetRouteData ( ) . Values ;
176
- if ( rd . TryGetValue ( "relationshipName" , out object relationshipName ) )
263
+ }
264
+ if ( _routeValues . TryGetValue ( "relationshipName" , out object relationshipName ) )
265
+ {
177
266
_currentRequest . RequestRelationship = requestResource . Relationships . Single ( r => r . PublicRelationshipName == ( string ) relationshipName ) ;
267
+ }
178
268
return requestResource ;
179
269
}
180
270
}
0 commit comments