简体   繁体   中英

Find the Blazor component class corresponding to a given path

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

  • The use of reflection has already broken the original answer. An update needs to be made to use routes.GetType().GetMethod("Route", BindingFlags.Instance | BindingFlags.Public).Invoke(routes, new[] { context });
  • The path needs to be relative or it will not detect the page type

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM