11using System ;
2+ using System . ComponentModel ;
23using System . Linq ;
34using System . Threading . Tasks ;
45using JsonApiDotNetCore . Configuration ;
56using JsonApiDotNetCore . Internal ;
67using JsonApiDotNetCore . Internal . Contracts ;
78using JsonApiDotNetCore . Managers . Contracts ;
89using Microsoft . AspNetCore . Http ;
10+ using Microsoft . AspNetCore . Http . Features ;
911using Microsoft . AspNetCore . Routing ;
1012using Microsoft . Extensions . Primitives ;
1113
@@ -21,6 +23,7 @@ public class CurrentRequestMiddleware
2123 private ICurrentRequest _currentRequest ;
2224 private IResourceGraph _resourceGraph ;
2325 private IJsonApiOptions _options ;
26+ private RouteValueDictionary _routeValues ;
2427 private IControllerResourceMapping _controllerResourceMapping ;
2528
2629 public CurrentRequestMiddleware ( RequestDelegate next )
@@ -39,12 +42,15 @@ public async Task Invoke(HttpContext httpContext,
3942 _controllerResourceMapping = controllerResourceMapping ;
4043 _resourceGraph = resourceGraph ;
4144 _options = options ;
45+ _routeValues = httpContext . GetRouteData ( ) . Values ;
4246 var requestResource = GetCurrentEntity ( ) ;
4347 if ( requestResource != null )
4448 {
45- _currentRequest . SetRequestResource ( GetCurrentEntity ( ) ) ;
49+ _currentRequest . SetRequestResource ( requestResource ) ;
4650 _currentRequest . IsRelationshipPath = PathIsRelationship ( ) ;
47- _currentRequest . BasePath = GetBasePath ( _currentRequest . GetRequestResource ( ) . ResourceName ) ;
51+ _currentRequest . BasePath = GetBasePath ( requestResource . ResourceName ) ;
52+ _currentRequest . BaseId = GetBaseId ( ) ;
53+ _currentRequest . RelationshipId = GetRelationshipId ( ) ;
4854 }
4955
5056 if ( IsValid ( ) )
@@ -53,50 +59,127 @@ public async Task Invoke(HttpContext httpContext,
5359 }
5460 }
5561
56- private string GetBasePath ( string entityName )
62+ private string GetBaseId ( )
5763 {
58- var r = _httpContext . Request ;
59- if ( _options . RelativeLinks )
64+ var resource = _currentRequest . GetRequestResource ( ) ;
65+ var individualComponents = SplitCurrentPath ( ) ;
66+ if ( individualComponents . Length < 2 )
6067 {
61- return GetNamespaceFromPath ( r . Path , entityName ) ;
68+ return null ;
6269 }
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 ;
6478 }
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 ) ;
6588
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 ( )
67100 {
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
72113 {
73- if ( pathSpan [ i ] . Equals ( delimiter ) )
114+ var converter = TypeDescriptor . GetConverter ( idType ) ;
115+ if ( converter != null )
74116 {
75- var nextPosition = i + 1 ;
76- if ( pathSpan . Length > i + entityNameSpan . Length )
117+ if ( ! converter . IsValid ( value ) )
77118 {
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 ) )
80124 {
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 )
86126 {
87- return pathSpan . Slice ( 0 , i ) . ToString ( ) ;
127+ throw new JsonApiException ( 500 , "The base ID is an integer, and it is negative." ) ;
88128 }
89129 }
90130 }
91131 }
92132 }
133+ catch ( NotSupportedException )
134+ {
135+
136+ }
93137
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 ;
95178 }
96179
97180 protected bool PathIsRelationship ( )
98181 {
99- var actionName = ( string ) _httpContext . GetRouteData ( ) . Values [ "action" ] ;
182+ var actionName = ( string ) _routeValues [ "action" ] ;
100183 return actionName . ToLower ( ) . Contains ( "relationships" ) ;
101184 }
102185
@@ -124,7 +207,9 @@ private bool IsValidAcceptHeader(HttpContext context)
124207 foreach ( var acceptHeader in acceptHeaders )
125208 {
126209 if ( ContainsMediaTypeParameters ( acceptHeader ) == false )
210+ {
127211 continue ;
212+ }
128213
129214 FlushResponse ( context , 406 ) ;
130215 return false ;
@@ -165,16 +250,21 @@ private void FlushResponse(HttpContext context, int statusCode)
165250 /// <returns></returns>
166251 private ResourceContext GetCurrentEntity ( )
167252 {
168- var controllerName = ( string ) _httpContext . GetRouteValue ( "controller" ) ;
253+ var controllerName = ( string ) _routeValues [ "controller" ] ;
169254 if ( controllerName == null )
255+ {
170256 return null ;
257+ }
171258 var resourceType = _controllerResourceMapping . GetAssociatedResource ( controllerName ) ;
172259 var requestResource = _resourceGraph . GetResourceContext ( resourceType ) ;
173260 if ( requestResource == null )
261+ {
174262 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+ {
177266 _currentRequest . RequestRelationship = requestResource . Relationships . Single ( r => r . PublicRelationshipName == ( string ) relationshipName ) ;
267+ }
178268 return requestResource ;
179269 }
180270 }
0 commit comments