How to access current route using Blazor component
How to access current route using Blazor component
This title is misleading and you should change it. You can't access the
the current route in order to do that task, as the current route does
not exist yet
I have searched up this question multiple times and each solution has
not worked for me, using HttpContext has not worked either.
Again, the solution is not by using the current route as no route has
already been created. Your components are not yet created, so no route
is available
HttpContext is not available in Blazor, most of the time, if at all...
Solution:
Add this code to the MainLayout component
Define a property of type Type. This property will be populated with
the Type ( component) that is going to be createdYou can retrieve the type of the component from the OnParametersSet
method of the MainLayout componentWhen you start your project, the Type is Index
(Note that it is returned in the form:<projectname>.Pages.Index)
If you type in your browser the url of your app + "/counter",
as for instance:https://localhost:44373/counter
, you'll get<projectname>.Pages.Counter
@code{
public Type PageType { get; set; }
protected override void OnParametersSet()
{
PageType = (this.Body.Target as RouteView)?.RouteData.PageType;
Console.WriteLine("This is your type (current type)" +
PageType);
}
}
At the top of the MainLayout.razor file the <NavMenu />
is instantiated. Add to it an attribute parameter named PageType, as follows:
<NavMenu PageType="@PageType" />
The PageType
on the left side should be defined in the NavMenu component. As you can see it is bound to the PageType
property defined in MainLayout.
Now you should define the parameter property named PageType
in NavMenu like this:
NavMenu.razor
@code {
[Parameter]
public Type PageType { get; set; }
}
Now, you have the current Type (component) available in your NavMenu
component, and you can use it as you please.
It is essential that you understand how components are rendered... which comes first, next etc. And much much more...
But hey, you're going to have tough time managing your task... The following code snippet demonstrates, very roughly that what you intend to do is workable( My code is fine and does the job, and the issues you're going to have are not related to it).
NavMenu.razor
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
@switch (PageType.ToString())
{
case "BlazorApp4.Pages.Index":
@*<header>
<div>
<nav>
<ul class="main-menu">
<li id="CurPage">Home</li>
<li class="menu-item">Shop</li>
<li class="menu-item">FAQ</li>
<li class="menu-item">About</li>
</ul>
</nav>
</div>
</header>*@
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</li>
</ul>
break;
case "Shop":
<header>
<div>
<nav>
<ul class="main-menu">
<li class="menu-item">Home</li>
<li id="CurPage">Shop</li>
<li class="menu-item">FAQ</li>
<li class="menu-item">About</li>
</ul>
</nav>
</div>
</header>
break;
case "FAQ":
<header>
<div>
<nav>
<ul class="main-menu">
<li class="menu-item">Home</li>
<li Class="menu-item">Shop</li>
<li id="CurPage">FAQ</li>
<li class="menu-item">About</li>
</ul>
</nav>
</div>
</header>
break;
case "About":
<header>
<div>
<nav>
<ul class="main-menu">
<li class="menu-item">Home</li>
<li class="menu-item">Shop</li>
<li class="menu-item">FAQ</li>
<li id="CurPage">About</li>
</ul>
</nav>
</div>
</header>
break;
}
</div>
The code above is going to display the default links to the default template pages, when you start your page. (Index page). Comment out the code in the case "BlazorApp4.Pages.Index:
, and uncomment your code (...) located under case "BlazorApp4.Pages.Index":
Lo and behold, total darkness... here comes your pains...work hard...good luck.
How to Get the Base Url of a Server-Side Blazor App
Getting it inside a page is easy:
@inject NavigationManager Navigator
<p>@Navigator.BaseUri</p>
But you can't use a NavigationManager
in the Startup
class.
Blazor retrieve relative url
It's not Blazor specific, we have the Uri class:
var uri = new Uri(@"https://www.mywebsite.com/someoverview");
var relativePath = uri.LocalPath; // or .PathAndQuery
or for the current Blazor page:
@inject NavigationManager Navigator
<a href="@Navigator.ToBaseRelativePath(Navigator.Uri)">here</a>
but do note that ToBaseRelativePath() does not include a leading /
, it is different from LocalPath. You can mix & match.
How to get component type from location/url/route
You can add an OnParametersSet method in your MainLayout component...
Also add: @using System.Reflection;
protected override void OnParametersSet()
{
// Get the Component Type from the route data
var component = (this.Body.Target as RouteView)?.RouteData.PageType;
// Get a list of all the interfaces implemented by the component.
// It should be: IComponent, IHandleEvent, IHandleAfterRender,
// Unless your routable component has derived from ComponentBase,
// and added interface implementations of its own
var allInterfaces = component.GetInterfaces();
}
Blazor - How to get layout @Body path?
Blazor provides the NavigationManager
for this purpose (see Blazor routing):
@inject NavigationManager NavigationManager;
protected override void OnInitialized()
{
breadCrumbDatas = breadCrumbService.GetProperList();
// Get everything after the domain + '/' (e.g. after "https://example.com/")
path = NavigationManager.Uri.Substring(Navigator.BaseUri.Length);
// You can also listen for a path change by subscribing to
// NavigationManager.LocationChanged.
}
A couple notes/suggestions:
- Since
NavigationManager
can be injected, you don't need to pass it down to yourBreadCrumbs
component as a parameter (unless you are using the component for something other than URL paths). You can directly inject it into yourBreadCrumbs
component:
@inject NavigationManager NavigationManager;
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
@Body
isn't a component: it is aRenderFragment
.@Body
doesn't have a path, per se; pages (designated with@page
) do. Routing is managed by App.razor and configured in Startup.cs.
Logging URL in Blazor Server using Nlog
Right now, it doesn't seem to be possible to use NLog layout renderers such as ${aspnet-request-url}
to log the correct request url for Blazor Server applications, but you can still make it work by appending the request URL to the logger yourself by using the ${event-properties}
layout renderer (see code below).
The reason is that ${aspnet-request-url}
layout renderer searches HttpContext.Request
to get the request URL (NLog AspNetRequestUrlRenderer.cs), which works fine for other asp.net core projects, but Blazor handles things differently.
In-case of Blazor Server applications, clients only send an HTTP request to the application just once (to https://localhost/_blazor
), in which they download all the necessary files (html / css / js / images) and Blazor establishes a SignalR connection with the client. Once a SignalR
connection is established, it uses transport methods other than HTTP (usually WebSockets) for communicating between the client-server and no HTTP request is made after that. All subsequent requests are made through SignalR connection and therefore, you always get https://localhost/_blazor
in ${aspnet-request-url}
layout renderer.
As per the documentation, the defined way to get current requests URI is to inject NavigationManager
in your Blazor components. You can get the Uri
property from NavigationManager
which gives you an absolute URI in string format. NLog loggers can be enriched with context information and custom properties (using WithProperty
or WithProperties
method) which can then be logged using Event Properties layout renderer. So what you can do is attach the NavigationManager.Uri
to your logger and then access that property by defining it in your layouts in NLog.config.
Solution #01 Example:
/* Counter.razor */
@page "/counter"
@inject NavigationManager navigationManager
/* Define Logger */
NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
/* Attaching a property to a logger returns a new enriched logger */
var loggerWithProps = _logger.WithProperty("navigation-manager-uri", navigationManager.Uri);
/* Use this logger which has request url attached in key 'navigation-manager-uri' */
loggerWithProps.Info("Sample message which contains NavManager.Uri property");
Now in your NLog.config
file, you can access the request uri using the layout renderer ${event-properties:item=navigation-manager-uri}
. Your config file will look like this:
<!-- NLog.config -->
<targets>
<target xsi:type="Console" layout="${event-properties:item=navigation-manager-uri} ${message}">
<!-- Or skip logging this property if it is empty (uses ${when} condition) -->
<!-- <target xsi:type="Console" layout="${when:when='${event-properties:item=navigation-manager-uri}'!='':Inner=${event-properties:item=navigation-manager-uri} }${message}"> -->
</target>
</targets>
which gives the output logs:
https://localhost:7123/counter Sample message which contains NavManager.Uri property
[Recommended] Solution #02 Example (thanks @Rolf Kristensen)
Instead of enriching a logger by attaching a property to it (which creates 2 loggers, one with your property and one without it) and using ${event-properties}
layout renderer, you can simple add NavigationManager.Uri
into HttpContext.Items
in OnInitialized
lifecycle hook. Since NLog has HttpContext Item Layout Renderer which can access HttpContext.Items
, you can access it in NLog.config
by using ${aspnet-item:variable=YOUR_VARIABLE_NAME}
/* Counter.razor */
@page "/counter"
/* @* Inject NavigationManager to get current URI *@ */
@inject NavigationManager navigationManager
/* @* Inject IHttpContextAccessor to get access to current HttpContext *@ */
@inject IHttpContextAccessor httpContextAccessor
@code {
/* Define a simple logger */
NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
protected override void OnInitialized()
{
/* Add NavigationManager.Uri into HttpContext Items */
/* This will then be accessible in NLog.config like ${aspnet-item:variable=navigation-manager-uri} */
httpContextAccessor?.HttpContext?.Items.Add("navigation-manager-uri", navigationManager.Uri);
/* Or make it fail safe so that it doesn't give 'key' already present error when switching to multiple pages */
// if (httpContextAccessor is not null && httpContextAccessor.HttpContext is not null && httpContextAccessor.HttpContext.Items.ContainsKey("navigation-manager-uri"))
// httpContextAccessor?.HttpContext?.Items.Remove("navigation-manager-uri");
// httpContextAccessor?.HttpContext?.Items.Add("navigation-manager-uri", navigationManager.Uri);
/* Now logging anything will have correct URL displayed in logs */
/* Use this logger everywhere in this component */
logger.Info("This will have correct URL attached from NavigationManager.Uri");
}
}
Now in your NLog.config
file, you can access the request URI which you pushed to HttpContext.Items
using the layout renderer ${aspnet-item:variable=navigation-manager-uri}
. Your config file will look like this:
<!-- NLog.config -->
<targets>
<target xsi:type="Console" layout="${aspnet-item:variable=navigation-manager-uri} ${message}">
<!-- Or skip logging this property if it is empty (uses ${when} condition) -->
<!-- <target xsi:type="Console" layout="${when:when='${aspnet-item:variable=navigation-manager-uri}'!='':Inner=${aspnet-item:variable=navigation-manager-uri} }${message}"> -->
</target>
</targets>
which gives the output logs:
https://localhost:7123/counter This will have correct URL attached from NavigationManager.Uri
This solution is easier because all you need to do is add 3 lines of code to make it work and NLog handles the rest.
- Inject NavigationManager
- Inject IHttpContextAccessor
- Add
NavigationManager.Uri
intoHttpContext.Items
Right now these are the only ways to log the request URLs for Blazor applications with NLog. See NLog Issue - Provide support for logging Blazor request information for more information.
Related Topics
Declaration of Anonymous Types List
How to Add Parameters into a Webrequest
How to List All Sub Directories in a Directory
Iterating Over JSON Object in C#
Webapi Formdata Upload (To Db) with Extra Parameters
How to Ignore a Property When Serializing Using the Datacontractserializer
How to Check If System.Net.Webclient.Downloaddata Is Downloading a Binary File
Should I Worry About "This Async Method Lacks 'Await' Operators and Will Run Synchronously" Warning
How to Put a Usercontrol into Visual Studio Toolbox
Adding Scripting Functionality to .Net Applications
How to Restrict Access to Nested Class Member to Enclosing Class
Multipleactiveresultsets=True or Multiple Connections
Generate C# Project Using Cmake
How to Get Property Change Notifications with Ef 4.X Dbcontext Generator
How to Download a Nuget Package Without Nuget.Exe or Visual Studio Extension