@@ -36,7 +36,8 @@ public void AddEndpoints(
3636 HashSet < string > routeNames ,
3737 ActionDescriptor action ,
3838 IReadOnlyList < ConventionalRouteEntry > routes ,
39- IReadOnlyList < Action < EndpointBuilder > > conventions )
39+ IReadOnlyList < Action < EndpointBuilder > > conventions ,
40+ bool createInertEndpoints )
4041 {
4142 if ( endpoints == null )
4243 {
@@ -63,6 +64,27 @@ public void AddEndpoints(
6364 throw new ArgumentNullException ( nameof ( conventions ) ) ;
6465 }
6566
67+ if ( createInertEndpoints )
68+ {
69+ var requestDelegate = CreateRequestDelegate ( ) ;
70+ var builder = new InertEndpointBuilder ( )
71+ {
72+ DisplayName = action . DisplayName ,
73+ RequestDelegate = requestDelegate ,
74+ } ;
75+ AddActionDataToBuilder (
76+ builder ,
77+ routeNames ,
78+ action ,
79+ routeName : null ,
80+ dataTokens : null ,
81+ suppressLinkGeneration : false ,
82+ suppressPathMatching : false ,
83+ conventions ,
84+ Array . Empty < Action < EndpointBuilder > > ( ) ) ;
85+ endpoints . Add ( builder . Build ( ) ) ;
86+ }
87+
6688 if ( action . AttributeRouteInfo == null )
6789 {
6890 // Check each of the conventional patterns to see if the action would be reachable.
@@ -81,18 +103,22 @@ public void AddEndpoints(
81103
82104 // We suppress link generation for each conventionally routed endpoint. We generate a single endpoint per-route
83105 // to handle link generation.
84- var builder = CreateEndpoint (
106+ var requestDelegate = CreateRequestDelegate ( ) ;
107+ var builder = new RouteEndpointBuilder ( requestDelegate , updatedRoutePattern , route . Order )
108+ {
109+ DisplayName = action . DisplayName ,
110+ } ;
111+ AddActionDataToBuilder (
112+ builder ,
85113 routeNames ,
86114 action ,
87- updatedRoutePattern ,
88115 route . RouteName ,
89- route . Order ,
90116 route . DataTokens ,
91117 suppressLinkGeneration : true ,
92118 suppressPathMatching : false ,
93119 conventions ,
94120 route . Conventions ) ;
95- endpoints . Add ( builder ) ;
121+ endpoints . Add ( builder . Build ( ) ) ;
96122 }
97123 }
98124 else
@@ -109,18 +135,22 @@ public void AddEndpoints(
109135 throw new InvalidOperationException ( "Failed to update route pattern with required values." ) ;
110136 }
111137
112- var endpoint = CreateEndpoint (
138+ var requestDelegate = CreateRequestDelegate ( ) ;
139+ var builder = new RouteEndpointBuilder ( requestDelegate , updatedRoutePattern , action . AttributeRouteInfo . Order )
140+ {
141+ DisplayName = action . DisplayName ,
142+ } ;
143+ AddActionDataToBuilder (
144+ builder ,
113145 routeNames ,
114146 action ,
115- updatedRoutePattern ,
116147 action . AttributeRouteInfo . Name ,
117- action . AttributeRouteInfo . Order ,
118148 dataTokens : null ,
119149 action . AttributeRouteInfo . SuppressLinkGeneration ,
120150 action . AttributeRouteInfo . SuppressPathMatching ,
121151 conventions ,
122152 perRouteConventions : Array . Empty < Action < EndpointBuilder > > ( ) ) ;
123- endpoints . Add ( endpoint ) ;
153+ endpoints . Add ( builder . Build ( ) ) ;
124154 }
125155 }
126156
@@ -262,49 +292,17 @@ private static (RoutePattern resolvedRoutePattern, IDictionary<string, string> r
262292 return ( attributeRoutePattern , resolvedRequiredValues ?? action . RouteValues ) ;
263293 }
264294
265- private RouteEndpoint CreateEndpoint (
295+ private void AddActionDataToBuilder (
296+ EndpointBuilder builder ,
266297 HashSet < string > routeNames ,
267298 ActionDescriptor action ,
268- RoutePattern routePattern ,
269299 string routeName ,
270- int order ,
271300 RouteValueDictionary dataTokens ,
272301 bool suppressLinkGeneration ,
273302 bool suppressPathMatching ,
274303 IReadOnlyList < Action < EndpointBuilder > > conventions ,
275304 IReadOnlyList < Action < EndpointBuilder > > perRouteConventions )
276305 {
277-
278- // We don't want to close over the retrieve the Invoker Factory in ActionEndpointFactory as
279- // that creates cycles in DI. Since we're creating this delegate at startup time
280- // we don't want to create all of the things we use at runtime until the action
281- // actually matches.
282- //
283- // The request delegate is already a closure here because we close over
284- // the action descriptor.
285- IActionInvokerFactory invokerFactory = null ;
286-
287- RequestDelegate requestDelegate = ( context ) =>
288- {
289- var routeData = new RouteData ( ) ;
290- routeData . PushState ( router : null , context . Request . RouteValues , dataTokens ) ;
291-
292- var actionContext = new ActionContext ( context , routeData , action ) ;
293-
294- if ( invokerFactory == null )
295- {
296- invokerFactory = context . RequestServices . GetRequiredService < IActionInvokerFactory > ( ) ;
297- }
298-
299- var invoker = invokerFactory . CreateInvoker ( actionContext ) ;
300- return invoker . InvokeAsync ( ) ;
301- } ;
302-
303- var builder = new RouteEndpointBuilder ( requestDelegate , routePattern , order )
304- {
305- DisplayName = action . DisplayName ,
306- } ;
307-
308306 // Add action metadata first so it has a low precedence
309307 if ( action . EndpointMetadata != null )
310308 {
@@ -399,8 +397,47 @@ private RouteEndpoint CreateEndpoint(
399397 {
400398 perRouteConventions [ i ] ( builder ) ;
401399 }
400+ }
401+
402+ private static RequestDelegate CreateRequestDelegate ( )
403+ {
404+ // We don't want to close over the retrieve the Invoker Factory in ActionEndpointFactory as
405+ // that creates cycles in DI. Since we're creating this delegate at startup time
406+ // we don't want to create all of the things we use at runtime until the action
407+ // actually matches.
408+ //
409+ // The request delegate is already a closure here because we close over
410+ // the action descriptor.
411+ IActionInvokerFactory invokerFactory = null ;
412+
413+ return ( context ) =>
414+ {
415+ var endpoint = context . GetEndpoint ( ) ;
416+ var dataTokens = endpoint . Metadata . GetMetadata < IDataTokensMetadata > ( ) ;
402417
403- return ( RouteEndpoint ) builder . Build ( ) ;
418+ var routeData = new RouteData ( ) ;
419+ routeData . PushState ( router : null , context . Request . RouteValues , new RouteValueDictionary ( dataTokens ? . DataTokens ) ) ;
420+
421+ // Don't close over the ActionDescriptor, that's not valid for pages.
422+ var action = endpoint . Metadata . GetMetadata < ActionDescriptor > ( ) ;
423+ var actionContext = new ActionContext ( context , routeData , action ) ;
424+
425+ if ( invokerFactory == null )
426+ {
427+ invokerFactory = context . RequestServices . GetRequiredService < IActionInvokerFactory > ( ) ;
428+ }
429+
430+ var invoker = invokerFactory . CreateInvoker ( actionContext ) ;
431+ return invoker . InvokeAsync ( ) ;
432+ } ;
433+ }
434+
435+ private class InertEndpointBuilder : EndpointBuilder
436+ {
437+ public override Endpoint Build ( )
438+ {
439+ return new Endpoint ( RequestDelegate , new EndpointMetadataCollection ( Metadata ) , DisplayName ) ;
440+ }
404441 }
405442 }
406443}
0 commit comments