88 using Microsoft . AspNetCore . Builder ;
99 using Microsoft . AspNetCore . Http ;
1010 using Microsoft . AspNetCore . Http . Features ;
11+ using Microsoft . AspNetCore . Mvc ;
1112 using Microsoft . AspNetCore . Routing ;
13+ using Microsoft . Extensions . DependencyInjection ;
14+ using Microsoft . Extensions . Options ;
15+ using Utilities . Extensions ;
1216
1317 /// <summary>
1418 /// Mock of <see cref="IApplicationBuilder"/>. Used for extracting registered routes.
@@ -20,6 +24,8 @@ public class ApplicationBuilderMock : IApplicationBuilder
2024
2125 private readonly IList < Func < RequestDelegate , RequestDelegate > > components = new List < Func < RequestDelegate , RequestDelegate > > ( ) ;
2226
27+ private bool endpointsEnabled = false ;
28+
2329 /// <summary>
2430 /// Initializes a new instance of the <see cref="ApplicationBuilderMock"/> class.
2531 /// </summary>
@@ -33,13 +39,15 @@ public ApplicationBuilderMock(IServiceProvider serviceProvider)
3339
3440 this . Routes = new RouteCollection ( ) ;
3541 this . ApplicationServices = serviceProvider ;
42+
43+ this . CheckForEndpointRouting ( serviceProvider ) ;
3644 }
3745
3846 /// <summary>
3947 /// Initializes a new instance of the <see cref="ApplicationBuilderMock"/> class.
4048 /// </summary>
4149 /// <param name="builder">Application builder to copy properties from.</param>
42- public ApplicationBuilderMock ( IApplicationBuilder builder )
50+ public ApplicationBuilderMock ( IApplicationBuilder builder )
4351 => this . Properties = builder . Properties ;
4452
4553 /// <summary>
@@ -69,15 +77,16 @@ public IServiceProvider ApplicationServices
6977 /// </summary>
7078 /// <value>Result of <see cref="RouteCollection"/> type.</value>
7179 public RouteCollection Routes { get ; set ; }
72-
80+
7381 /// <summary>
7482 /// Extracts registered routes from the provided middleware, if such are found.
7583 /// </summary>
7684 /// <param name="middleware">Middleware delegate.</param>
7785 /// <returns>The same <see cref="IApplicationBuilder"/>.</returns>
7886 public IApplicationBuilder Use ( Func < RequestDelegate , RequestDelegate > middleware )
7987 {
80- this . ExtractRoutes ( middleware ) ;
88+ this . ExtractEndpointRoutes ( middleware ) ;
89+ this . ExtractLegacyRoutes ( middleware ) ;
8190
8291 this . components . Add ( middleware ) ;
8392 return this ;
@@ -109,12 +118,89 @@ public RequestDelegate Build()
109118 return app ;
110119 }
111120
112- private T GetProperty < T > ( string key )
113- => this . Properties . TryGetValue ( key , out var value ) ? ( T ) value : default ( T ) ;
121+ private T GetProperty < T > ( string key )
122+ => this . Properties . TryGetValue ( key , out var value ) ? ( T ) value : default ;
114123
115124 private void SetProperty < T > ( string key , T value ) => this . Properties [ key ] = value ;
116125
117- private void ExtractRoutes ( Func < RequestDelegate , RequestDelegate > middleware )
126+ private void CheckForEndpointRouting ( IServiceProvider serviceProvider )
127+ {
128+ var options = serviceProvider . GetService < IOptions < MvcOptions > > ( ) ? . Value ;
129+
130+ this . endpointsEnabled = options ? . EnableEndpointRouting ?? false ;
131+ }
132+
133+ private void ExtractEndpointRoutes ( Func < RequestDelegate , RequestDelegate > middleware )
134+ {
135+ var middlewareTypeField = middleware
136+ . Target
137+ . GetType ( )
138+ . GetTypeInfo ( )
139+ . DeclaredFields
140+ . FirstOrDefault ( m => m . Name == "middleware" ) ;
141+
142+ if ( ! ( middlewareTypeField ? . GetValue ( middleware . Target ) is Type middlewareType )
143+ || middlewareType . Name != "EndpointMiddleware" )
144+ {
145+ return ;
146+ }
147+
148+ var routeOptions = this . ApplicationServices . GetService < IOptions < RouteOptions > > ( ) ? . Value ;
149+
150+ if ( routeOptions == null )
151+ {
152+ return ;
153+ }
154+
155+ var routeBuilder = new RouteBuilder ( this )
156+ {
157+ DefaultHandler = new RouteHandler ( c => Task . CompletedTask )
158+ } ;
159+
160+ var endpointDataSources = routeOptions . Exposed ( ) . EndpointDataSources ;
161+
162+ foreach ( EndpointDataSource endpointDataSource in endpointDataSources )
163+ {
164+ var routeEndpoints = endpointDataSource
165+ . Endpoints
166+ . OfType < RouteEndpoint > ( )
167+ . Where ( e => e . DisplayName . StartsWith ( "Route: " ) )
168+ . OrderBy ( e => e . Order ) ;
169+
170+ foreach ( var routeEndpoint in routeEndpoints )
171+ {
172+ var routeName = routeEndpoint
173+ . Metadata
174+ . OfType < RouteNameMetadata > ( )
175+ . FirstOrDefault ( )
176+ ? . RouteName ;
177+
178+ if ( routeName == null )
179+ {
180+ return ;
181+ }
182+
183+ routeBuilder . MapRoute (
184+ name : routeName ,
185+ routeEndpoint . RoutePattern . RawText ) ;
186+ }
187+ }
188+
189+ var attributeRoutingType = WebFramework . Internals . AttributeRouting ;
190+ var createAttributeMegaRouteMethod = attributeRoutingType . GetMethod ( "CreateAttributeMegaRoute" ) ;
191+ var router = ( IRouter ) createAttributeMegaRouteMethod . Invoke ( null , new [ ] { this . ApplicationServices } ) ;
192+
193+ routeBuilder . Routes . Insert ( 0 , router ) ;
194+
195+ var routes = routeBuilder . Routes ;
196+
197+ for ( int i = 0 ; i < routes . Count ; i ++ )
198+ {
199+ this . Routes . Add ( routes [ i ] ) ;
200+ }
201+ }
202+
203+ private void ExtractLegacyRoutes ( Func < RequestDelegate , RequestDelegate > middleware )
118204 {
119205 var middlewareArguments = middleware
120206 . Target
0 commit comments