In a Blazor server app, how can I find the class (return as Type
) of the razor component that matches a given path (in string
, say "/controller/action"
)? I don't want to navigate to the actual page; I only wish to find the corresponding class and perform some reflection-type stuff with it. It appears to me that the way Blazor handles route is very different from that of MVC and I couldn't quite find any documentation that helps.
You can use reflection to find all components with a RouteAttribute
attribute. @page
directive are transformed into RouteAttribute
.
var pageTypeList = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => t.GetCustomAttribute(typeof(Microsoft.AspNetCore.Components.RouteAttribute)) != null);
Note: This is more of a comment for the accepted answer, but I don't have the rep.
TL;DR - RouteData.PageType
will solve the original problem for most simple use cases. If you are experiencing inconsistent results with this - use an updated version of Mu-Tsun Tsai's answer, clarified below.
I had a similar situation that lead me here. I used Mu-Tsun Tsai's answer . While I was doing this, I found that the RouteData.PageType
provides a similar value - which probably works in most simple cases.
Unfortunately, my case needed that PageType in order to determine if the navigation was valid - and if not, then navigate to a default page. I suspect that the nested navigation caused a race condition, that wasn't picked up by RouteData.PageType
- for some reason, it returned the PageType of the referring page, instead of the PageType of the destination page in limited and inconsistent circumstances.
I ultimately reverted to Mu-Tsun Tsai's answer because it is able to determine the type based on the destination page.
Caveats
routes.GetType().GetMethod("Route", BindingFlags.Instance | BindingFlags.Public).Invoke(routes, new[] { context });
After carefully studying the source code provided by agua from mars, I came up with the following solution. First step is to obtain the Router
object created by the Blazor framework, and I do so by using CascadingValue
in my App.razor
:
<Router AppAssembly="@typeof(Program).Assembly" @ref="router">
<Found Context="routeData">
<CascadingValue Value="@router">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
...
</RouteView>
</CascadingValue>
</Found>
<NotFound>
...
</NotFound>
</Router>
@code {
private Router router;
}
So now in any page of my app I can obtain the Router
object using CascadingParameterAttribute
. Next comes reflection (since a lot of things involved here are defined as internal
):
public Type Find(Router router, string path) {
var assm = typeof(Router).Assembly;
var routes = typeof(Router).GetProperty("Routes", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(router);
var type = assm.GetTypes().FirstOrDefault(t => t.Name == "RouteContext");
var context = Activator.CreateInstance(type, new[] { path });
routes.GetType().GetMethod("Route", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(routes, new[] { context });
return type.GetProperty("Handler").GetValue(context) as Type;
}
And this function achieves exactly what I need.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.