@@ -20,6 +20,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
20
20
internal class ActionEndpointFactory
21
21
{
22
22
private readonly RoutePatternTransformer _routePatternTransformer ;
23
+ private readonly RequestDelegate _requestDelegate ;
23
24
24
25
public ActionEndpointFactory ( RoutePatternTransformer routePatternTransformer )
25
26
{
@@ -29,14 +30,16 @@ public ActionEndpointFactory(RoutePatternTransformer routePatternTransformer)
29
30
}
30
31
31
32
_routePatternTransformer = routePatternTransformer ;
33
+ _requestDelegate = CreateRequestDelegate ( ) ;
32
34
}
33
35
34
36
public void AddEndpoints (
35
37
List < Endpoint > endpoints ,
36
38
HashSet < string > routeNames ,
37
39
ActionDescriptor action ,
38
40
IReadOnlyList < ConventionalRouteEntry > routes ,
39
- IReadOnlyList < Action < EndpointBuilder > > conventions )
41
+ IReadOnlyList < Action < EndpointBuilder > > conventions ,
42
+ bool createInertEndpoints )
40
43
{
41
44
if ( endpoints == null )
42
45
{
@@ -63,6 +66,26 @@ public void AddEndpoints(
63
66
throw new ArgumentNullException ( nameof ( conventions ) ) ;
64
67
}
65
68
69
+ if ( createInertEndpoints )
70
+ {
71
+ var builder = new InertEndpointBuilder ( )
72
+ {
73
+ DisplayName = action . DisplayName ,
74
+ RequestDelegate = _requestDelegate ,
75
+ } ;
76
+ AddActionDataToBuilder (
77
+ builder ,
78
+ routeNames ,
79
+ action ,
80
+ routeName : null ,
81
+ dataTokens : null ,
82
+ suppressLinkGeneration : false ,
83
+ suppressPathMatching : false ,
84
+ conventions ,
85
+ Array . Empty < Action < EndpointBuilder > > ( ) ) ;
86
+ endpoints . Add ( builder . Build ( ) ) ;
87
+ }
88
+
66
89
if ( action . AttributeRouteInfo == null )
67
90
{
68
91
// Check each of the conventional patterns to see if the action would be reachable.
@@ -81,18 +104,21 @@ public void AddEndpoints(
81
104
82
105
// We suppress link generation for each conventionally routed endpoint. We generate a single endpoint per-route
83
106
// to handle link generation.
84
- var builder = CreateEndpoint (
107
+ var builder = new RouteEndpointBuilder ( _requestDelegate , updatedRoutePattern , route . Order )
108
+ {
109
+ DisplayName = action . DisplayName ,
110
+ } ;
111
+ AddActionDataToBuilder (
112
+ builder ,
85
113
routeNames ,
86
114
action ,
87
- updatedRoutePattern ,
88
115
route . RouteName ,
89
- route . Order ,
90
116
route . DataTokens ,
91
117
suppressLinkGeneration : true ,
92
118
suppressPathMatching : false ,
93
119
conventions ,
94
120
route . Conventions ) ;
95
- endpoints . Add ( builder ) ;
121
+ endpoints . Add ( builder . Build ( ) ) ;
96
122
}
97
123
}
98
124
else
@@ -109,18 +135,21 @@ public void AddEndpoints(
109
135
throw new InvalidOperationException ( "Failed to update route pattern with required values." ) ;
110
136
}
111
137
112
- var endpoint = CreateEndpoint (
138
+ var builder = new RouteEndpointBuilder ( _requestDelegate , updatedRoutePattern , action . AttributeRouteInfo . Order )
139
+ {
140
+ DisplayName = action . DisplayName ,
141
+ } ;
142
+ AddActionDataToBuilder (
143
+ builder ,
113
144
routeNames ,
114
145
action ,
115
- updatedRoutePattern ,
116
146
action . AttributeRouteInfo . Name ,
117
- action . AttributeRouteInfo . Order ,
118
147
dataTokens : null ,
119
148
action . AttributeRouteInfo . SuppressLinkGeneration ,
120
149
action . AttributeRouteInfo . SuppressPathMatching ,
121
150
conventions ,
122
151
perRouteConventions : Array . Empty < Action < EndpointBuilder > > ( ) ) ;
123
- endpoints . Add ( endpoint ) ;
152
+ endpoints . Add ( builder . Build ( ) ) ;
124
153
}
125
154
}
126
155
@@ -262,49 +291,17 @@ private static (RoutePattern resolvedRoutePattern, IDictionary<string, string> r
262
291
return ( attributeRoutePattern , resolvedRequiredValues ?? action . RouteValues ) ;
263
292
}
264
293
265
- private RouteEndpoint CreateEndpoint (
294
+ private void AddActionDataToBuilder (
295
+ EndpointBuilder builder ,
266
296
HashSet < string > routeNames ,
267
297
ActionDescriptor action ,
268
- RoutePattern routePattern ,
269
298
string routeName ,
270
- int order ,
271
299
RouteValueDictionary dataTokens ,
272
300
bool suppressLinkGeneration ,
273
301
bool suppressPathMatching ,
274
302
IReadOnlyList < Action < EndpointBuilder > > conventions ,
275
303
IReadOnlyList < Action < EndpointBuilder > > perRouteConventions )
276
304
{
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
-
308
305
// Add action metadata first so it has a low precedence
309
306
if ( action . EndpointMetadata != null )
310
307
{
@@ -399,8 +396,47 @@ private RouteEndpoint CreateEndpoint(
399
396
{
400
397
perRouteConventions [ i ] ( builder ) ;
401
398
}
399
+ }
402
400
403
- return ( RouteEndpoint ) builder . Build ( ) ;
401
+ private static RequestDelegate CreateRequestDelegate ( )
402
+ {
403
+ // We don't want to close over the Invoker Factory in ActionEndpointFactory as
404
+ // that creates cycles in DI. Since we're creating this delegate at startup time
405
+ // we don't want to create all of the things we use at runtime until the action
406
+ // actually matches.
407
+ //
408
+ // The request delegate is already a closure here because we close over
409
+ // the action descriptor.
410
+ IActionInvokerFactory invokerFactory = null ;
411
+
412
+ return ( context ) =>
413
+ {
414
+ var endpoint = context . GetEndpoint ( ) ;
415
+ var dataTokens = endpoint . Metadata . GetMetadata < IDataTokensMetadata > ( ) ;
416
+
417
+ var routeData = new RouteData ( ) ;
418
+ routeData . PushState ( router : null , context . Request . RouteValues , new RouteValueDictionary ( dataTokens ? . DataTokens ) ) ;
419
+
420
+ // Don't close over the ActionDescriptor, that's not valid for pages.
421
+ var action = endpoint . Metadata . GetMetadata < ActionDescriptor > ( ) ;
422
+ var actionContext = new ActionContext ( context , routeData , action ) ;
423
+
424
+ if ( invokerFactory == null )
425
+ {
426
+ invokerFactory = context . RequestServices . GetRequiredService < IActionInvokerFactory > ( ) ;
427
+ }
428
+
429
+ var invoker = invokerFactory . CreateInvoker ( actionContext ) ;
430
+ return invoker . InvokeAsync ( ) ;
431
+ } ;
432
+ }
433
+
434
+ private class InertEndpointBuilder : EndpointBuilder
435
+ {
436
+ public override Endpoint Build ( )
437
+ {
438
+ return new Endpoint ( RequestDelegate , new EndpointMetadataCollection ( Metadata ) , DisplayName ) ;
439
+ }
404
440
}
405
441
}
406
442
}
0 commit comments