2
2
{
3
3
using System . Collections . Generic ;
4
4
using System . Diagnostics . Contracts ;
5
+ using System . Reflection ;
5
6
using System . Web . Http . Routing ;
7
+ using static System . Reflection . BindingFlags ;
6
8
7
9
static class HttpRouteCollectionExtensions
8
10
{
@@ -11,9 +13,7 @@ internal static string GetRouteName( this HttpRouteCollection routes, IHttpRoute
11
13
Contract . Requires ( routes != null ) ;
12
14
Contract . Requires ( route != null ) ;
13
15
14
- var items = new KeyValuePair < string , IHttpRoute > [ routes . Count ] ;
15
-
16
- routes . CopyTo ( items , 0 ) ;
16
+ var items = CopyRouteEntries ( routes ) ;
17
17
18
18
foreach ( var item in items )
19
19
{
@@ -25,5 +25,61 @@ internal static string GetRouteName( this HttpRouteCollection routes, IHttpRoute
25
25
26
26
return null ;
27
27
}
28
+
29
+ static KeyValuePair < string , IHttpRoute > [ ] CopyRouteEntries ( HttpRouteCollection routes )
30
+ {
31
+ Contract . Requires ( routes != null ) ;
32
+ Contract . Ensures ( Contract . Result < KeyValuePair < string , IHttpRoute > [ ] > ( ) != null ) ;
33
+
34
+ var items = new KeyValuePair < string , IHttpRoute > [ routes . Count ] ;
35
+
36
+ try
37
+ {
38
+ routes . CopyTo ( items , 0 ) ;
39
+ }
40
+ catch ( NotSupportedException ) when ( routes . GetType ( ) . FullName == "System.Web.Http.WebHost.Routing.HostedHttpRouteCollection" )
41
+ {
42
+ var keys = GetRouteKeys ( routes ) ;
43
+
44
+ for ( var i = 0 ; i < keys . Count ; i ++ )
45
+ {
46
+ var key = keys [ i ] ;
47
+ var route = routes [ key ] ;
48
+
49
+ items [ i ] = new KeyValuePair < string , IHttpRoute > ( key , route ) ;
50
+ }
51
+ }
52
+
53
+ return items ;
54
+ }
55
+
56
+ static IReadOnlyList < string > GetRouteKeys ( HttpRouteCollection routes )
57
+ {
58
+ Contract . Requires ( routes != null ) ;
59
+ Contract . Ensures ( Contract . Result < IReadOnlyList < string > > ( ) != null ) ;
60
+
61
+ var collection = GetKeys ( routes ) ;
62
+ var keys = new string [ collection . Count ] ;
63
+
64
+ collection . CopyTo ( keys , 0 ) ;
65
+
66
+ return keys ;
67
+ }
68
+
69
+ static ICollection < string > GetKeys ( HttpRouteCollection routes )
70
+ {
71
+ Contract . Requires ( routes != null ) ;
72
+ Contract . Ensures ( Contract . Result < ICollection < string > > ( ) != null ) ;
73
+
74
+ // HACK: System.Web.Routing.RouteCollection doesn't expose the names associated with registered routes. The
75
+ // HostedHttpRouteCollection could have provided an adapter to support it, but didn't. Instead, it always throws
76
+ // NotSupportedException for the HttpRouteCollection.CopyTo method. This only happens when hosted on IIS. The
77
+ // only way to get the keys is use reflection to poke at the underlying dictionary.
78
+ var routeCollection = routes . GetType ( ) . GetField ( "_routeCollection" , Instance | NonPublic ) . GetValue ( routes ) ;
79
+ var dictionary = routeCollection . GetType ( ) . GetField ( "_namedMap" , Instance | NonPublic ) . GetValue ( routeCollection ) ;
80
+ var keys = ( ICollection < string > ) dictionary . GetType ( ) . GetRuntimeProperty ( "Keys" ) . GetValue ( dictionary , null ) ;
81
+
82
+ return keys ;
83
+ }
28
84
}
29
85
}
0 commit comments