Fix #30 Client-side Routing Broken in Preview6

This commit is contained in:
Shaun Walker 2019-06-19 20:47:58 -04:00
parent 59a715e468
commit a7e53486b3

View File

@ -8,6 +8,8 @@
@using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Routing
@inject SiteState SiteState @inject SiteState SiteState
@inject IUriHelper UriHelper @inject IUriHelper UriHelper
@inject INavigationInterception NavigationInterception
@inject IComponentContext ComponentContext
@inject IJSRuntime jsRuntime @inject IJSRuntime jsRuntime
@inject IAliasService AliasService @inject IAliasService AliasService
@inject ITenantService TenantService @inject ITenantService TenantService
@ -17,342 +19,350 @@
@inject IModuleService ModuleService @inject IModuleService ModuleService
@inject IModuleDefinitionService ModuleDefinitionService @inject IModuleDefinitionService ModuleDefinitionService
@inject IThemeService ThemeService @inject IThemeService ThemeService
@implements IHandleAfterRender
@DynamicComponent @DynamicComponent
@code { @code {
[CascadingParameter] PageState PageState { get; set; } [CascadingParameter] PageState PageState { get; set; }
[Parameter] Action<PageState> OnStateChange { get; set; } [Parameter] Action<PageState> OnStateChange { get; set; }
RenderFragment DynamicComponent { get; set; } PageState pagestate;
private string _absoluteUri; RenderFragment DynamicComponent { get; set; }
PageState pagestate;
protected override void OnInit() string _absoluteUri;
bool _navigationInterceptionEnabled;
protected override void OnInit()
{
_absoluteUri = UriHelper.GetAbsoluteUri();
UriHelper.OnLocationChanged += OnLocationChanged;
DynamicComponent = builder =>
{ {
_absoluteUri = UriHelper.GetAbsoluteUri(); if (pagestate != null)
UriHelper.OnLocationChanged += OnLocationChanged;
DynamicComponent = builder =>
{ {
if (pagestate != null) builder.OpenComponent(0, Type.GetType(Constants.DefaultPage));
{ builder.CloseComponent();
builder.OpenComponent(0, Type.GetType(Constants.DefaultPage));
builder.CloseComponent();
}
};
}
public void Dispose()
{
UriHelper.OnLocationChanged -= OnLocationChanged;
}
protected override async Task OnParametersSetAsync()
{
if (PageState == null)
{
await Refresh();
} }
} };
}
private async Task Refresh() public void Dispose()
{ {
List<ModuleDefinition> moduledefinitions; UriHelper.OnLocationChanged -= OnLocationChanged;
List<Models.Theme> themes; }
List<Alias> aliases;
Alias alias;
Site site;
List<Page> pages;
Page page;
User user;
List<Module> modules;
bool reload = false; protected override async Task OnParametersSetAsync()
if (PageState == null) {
{ if (PageState == null)
aliases = await AliasService.GetAliasesAsync();
alias = null;
}
else
{
aliases = PageState.Aliases;
alias = PageState.Alias;
}
if (alias == null || GetAlias(_absoluteUri, aliases).Name != alias.Name)
{
alias = GetAlias(_absoluteUri, aliases);
SiteState.Alias = alias; // set state for services
reload = true;
}
if (PageState == null || reload == true)
{
moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync();
themes = await ThemeService.GetThemesAsync();
site = await SiteService.GetSiteAsync(alias.SiteId);
}
else
{
moduledefinitions = PageState.ModuleDefinitions;
themes = PageState.Themes;
site = PageState.Site;
}
if (site != null || reload == true)
{
var interop = new Interop(jsRuntime);
string userid = await interop.GetCookie("user");
user = null;
if (PageState == null || reload == true)
{
if (!string.IsNullOrEmpty(userid))
{
user = await UserService.GetUserAsync(int.Parse(userid));
}
}
else
{
user = PageState.User;
}
if (!string.IsNullOrEmpty(userid))
{
if (user != null && user.UserId != int.Parse(userid))
{
user = await UserService.GetUserAsync(int.Parse(userid));
}
// this is a hack for server-side Blazor where JSInterop is not working OnInit() which means the userid is not being retrieved from the cookie on the initial render and is not being loaded into PageState
if (user == null)
{
user = await UserService.GetUserAsync(int.Parse(userid));
}
}
else
{
user = null;
}
string path = new Uri(_absoluteUri).PathAndQuery.Substring(1);
if (path.EndsWith("/")) { path = path.Substring(0, path.Length - 1); }
if (alias.Path != "")
{
path = path.Replace(alias.Path, "");
if (path.StartsWith("/")) { path = path.Substring(1); }
}
Dictionary<string, string> querystring = ParseQueryString(path);
if (querystring.ContainsKey("reload"))
{
reload = true;
}
if (PageState == null || reload == true)
{
pages = await PageService.GetPagesAsync(site.SiteId);
}
else
{
pages = PageState.Pages;
}
if (path.IndexOf("?") != -1)
{
path = path.Substring(0, path.IndexOf("?"));
}
if (PageState == null || reload == true)
{
page = pages.Where(item => item.Path == path).FirstOrDefault();
}
else
{
page = PageState.Page;
}
if (page.Path != path)
{
page = pages.Where(item => item.Path == path).FirstOrDefault();
reload = true;
}
if (page != null)
{
// check if user is authorized to view page
if (UserService.IsAuthorized(user, page.ViewPermissions))
{
pagestate = new PageState();
pagestate.ModuleDefinitions = moduledefinitions;
pagestate.Themes = themes;
pagestate.Aliases = aliases;
pagestate.Alias = alias;
pagestate.Site = site;
pagestate.Pages = pages;
pagestate.Page = page;
pagestate.User = user;
pagestate.Uri = new Uri(_absoluteUri, UriKind.Absolute);
pagestate.QueryString = querystring;
pagestate.ModuleId = -1;
pagestate.Control = "";
if (querystring.ContainsKey("mid"))
{
pagestate.ModuleId = int.Parse(querystring["mid"]);
}
if (querystring.ContainsKey("ctl"))
{
pagestate.Control = querystring["ctl"];
}
if (PageState != null && (PageState.ModuleId != pagestate.ModuleId || PageState.Control != pagestate.Control))
{
reload = true;
}
if (PageState == null || reload == true)
{
modules = await ModuleService.GetModulesAsync(page.PageId);
modules = ProcessModules(modules, moduledefinitions, pagestate.Control, page.Panes);
}
else
{
modules = PageState.Modules;
}
pagestate.Modules = modules;
OnStateChange?.Invoke(pagestate);
}
else
{
// user is not authorized to view page
}
}
else
{
// page does not exist
}
}
else
{
// site does not exist
}
}
private async void OnLocationChanged(object sender, LocationChangedEventArgs args)
{
_absoluteUri = args.Location;
await LocationChanged();
}
public async Task LocationChanged()
{ {
await Refresh(); await Refresh();
} }
}
private Dictionary<string, string> ParseQueryString(string path) private async Task Refresh()
{
List<ModuleDefinition> moduledefinitions;
List<Models.Theme> themes;
List<Alias> aliases;
Alias alias;
Site site;
List<Page> pages;
Page page;
User user;
List<Module> modules;
bool reload = false;
if (PageState == null)
{ {
Dictionary<string, string> querystring = new Dictionary<string, string>(); aliases = await AliasService.GetAliasesAsync();
alias = null;
}
else
{
aliases = PageState.Aliases;
alias = PageState.Alias;
}
if (alias == null || GetAlias(_absoluteUri, aliases).Name != alias.Name)
{
alias = GetAlias(_absoluteUri, aliases);
SiteState.Alias = alias; // set state for services
reload = true;
}
if (PageState == null || reload == true)
{
moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync();
themes = await ThemeService.GetThemesAsync();
site = await SiteService.GetSiteAsync(alias.SiteId);
}
else
{
moduledefinitions = PageState.ModuleDefinitions;
themes = PageState.Themes;
site = PageState.Site;
}
if (site != null || reload == true)
{
var interop = new Interop(jsRuntime);
string userid = await interop.GetCookie("user");
user = null;
if (PageState == null || reload == true)
{
if (!string.IsNullOrEmpty(userid))
{
user = await UserService.GetUserAsync(int.Parse(userid));
}
}
else
{
user = PageState.User;
}
if (!string.IsNullOrEmpty(userid))
{
if (user != null && user.UserId != int.Parse(userid))
{
user = await UserService.GetUserAsync(int.Parse(userid));
}
// this is a hack for server-side Blazor where JSInterop is not working OnInit() which means the userid is not being retrieved from the cookie on the initial render and is not being loaded into PageState
if (user == null)
{
user = await UserService.GetUserAsync(int.Parse(userid));
}
}
else
{
user = null;
}
string path = new Uri(_absoluteUri).PathAndQuery.Substring(1);
if (path.EndsWith("/")) { path = path.Substring(0, path.Length - 1); }
if (alias.Path != "")
{
path = path.Replace(alias.Path, "");
if (path.StartsWith("/")) { path = path.Substring(1); }
}
Dictionary<string, string> querystring = ParseQueryString(path);
if (querystring.ContainsKey("reload"))
{
reload = true;
}
if (PageState == null || reload == true)
{
pages = await PageService.GetPagesAsync(site.SiteId);
}
else
{
pages = PageState.Pages;
}
if (path.IndexOf("?") != -1) if (path.IndexOf("?") != -1)
{ {
foreach (string kvp in path.Substring(path.IndexOf("?") + 1).Split('&')) path = path.Substring(0, path.IndexOf("?"));
{
if (kvp != "")
{
if (kvp.Contains("="))
{
string[] pair = kvp.Split('=');
querystring.Add(pair[0], pair[1]);
}
else
{
querystring.Add(kvp, "true"); // default querystring when no value is provided
}
}
}
}
return querystring;
}
private List<Module> ProcessModules(List<Module> modules, List<ModuleDefinition> moduledefinitions, string control, string panes)
{
ModuleDefinition moduledefinition;
if (control == "")
{
control = Constants.DefaultControl;
} }
Dictionary<string, int> paneindex = new Dictionary<string, int>(); if (PageState == null || reload == true)
foreach (Module module in modules)
{ {
// set the type based on the template and action page = pages.Where(item => item.Path == path).FirstOrDefault();
moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionName == module.ModuleDefinitionName).FirstOrDefault(); }
if (moduledefinition != null) else
{
page = PageState.Page;
}
if (page.Path != path)
{
page = pages.Where(item => item.Path == path).FirstOrDefault();
reload = true;
}
if (page != null)
{
// check if user is authorized to view page
if (UserService.IsAuthorized(user, page.ViewPermissions))
{ {
string typename = moduledefinition.ControlTypeTemplate; pagestate = new PageState();
if (moduledefinition.ControlTypeRoutes != "") pagestate.ModuleDefinitions = moduledefinitions;
pagestate.Themes = themes;
pagestate.Aliases = aliases;
pagestate.Alias = alias;
pagestate.Site = site;
pagestate.Pages = pages;
pagestate.Page = page;
pagestate.User = user;
pagestate.Uri = new Uri(_absoluteUri, UriKind.Absolute);
pagestate.QueryString = querystring;
pagestate.ModuleId = -1;
pagestate.Control = "";
if (querystring.ContainsKey("mid"))
{ {
foreach (string route in moduledefinition.ControlTypeRoutes.Split(';')) pagestate.ModuleId = int.Parse(querystring["mid"]);
{ }
if (route.StartsWith(control + "=")) if (querystring.ContainsKey("ctl"))
{ {
typename = route.Replace(control + "=", ""); pagestate.Control = querystring["ctl"];
} }
} if (PageState != null && (PageState.ModuleId != pagestate.ModuleId || PageState.Control != pagestate.Control))
{
reload = true;
} }
module.ModuleType = typename.Replace("{Control}", control);
}
// ensure module's pane exists in current page and if not, assign it to the Admin pane if (PageState == null || reload == true)
if (!panes.Contains(module.Pane)) {
{ modules = await ModuleService.GetModulesAsync(page.PageId);
module.Pane = Constants.AdminPane; modules = ProcessModules(modules, moduledefinitions, pagestate.Control, page.Panes);
} }
else
{
modules = PageState.Modules;
}
pagestate.Modules = modules;
// calculate module position within pane OnStateChange?.Invoke(pagestate);
if (paneindex.ContainsKey(module.Pane))
{
paneindex[module.Pane] += 1;
} }
else else
{ {
paneindex.Add(module.Pane, 0); // user is not authorized to view page
} }
module.PaneModuleIndex = paneindex[module.Pane];
} }
else
foreach (Module module in modules)
{ {
module.PaneModuleCount = paneindex[module.Pane] + 1; // page does not exist
} }
return modules;
} }
else
private Alias GetAlias(string absoluteUri, List<Alias> aliases)
{ {
// site does not exist
string aliasname;
Alias alias = null;
Uri uri = new Uri(absoluteUri);
if (uri.Segments.Count() > 1)
{
// check if first path segment is an alias ( ie. a subfolder - www.domain.com/subfolder )
aliasname = uri.Authority + "/" + uri.Segments[1];
if (aliasname.EndsWith("/")) { aliasname = aliasname.Substring(0, aliasname.Length - 1); }
alias = aliases.Where(item => item.Name == aliasname).FirstOrDefault();
}
if (alias == null)
{
aliasname = uri.Authority;
alias = aliases.Where(item => item.Name == aliasname).FirstOrDefault();
}
if (alias == null && aliases.Count > 0)
{
// use first alias if Uri does not exist
alias = aliases.FirstOrDefault();
}
alias.Scheme = uri.Scheme;
return alias;
} }
}
private async void OnLocationChanged(object sender, LocationChangedEventArgs args)
{
_absoluteUri = args.Location;
await Refresh();
}
Task IHandleAfterRender.OnAfterRenderAsync()
{
if (!_navigationInterceptionEnabled && ComponentContext.IsConnected)
{
_navigationInterceptionEnabled = true;
return NavigationInterception.EnableNavigationInterceptionAsync();
}
return Task.CompletedTask;
}
private Dictionary<string, string> ParseQueryString(string path)
{
Dictionary<string, string> querystring = new Dictionary<string, string>();
if (path.IndexOf("?") != -1)
{
foreach (string kvp in path.Substring(path.IndexOf("?") + 1).Split('&'))
{
if (kvp != "")
{
if (kvp.Contains("="))
{
string[] pair = kvp.Split('=');
querystring.Add(pair[0], pair[1]);
}
else
{
querystring.Add(kvp, "true"); // default querystring when no value is provided
}
}
}
}
return querystring;
}
private List<Module> ProcessModules(List<Module> modules, List<ModuleDefinition> moduledefinitions, string control, string panes)
{
ModuleDefinition moduledefinition;
if (control == "")
{
control = Constants.DefaultControl;
}
Dictionary<string, int> paneindex = new Dictionary<string, int>();
foreach (Module module in modules)
{
// set the type based on the template and action
moduledefinition = moduledefinitions.Where(item => item.ModuleDefinitionName == module.ModuleDefinitionName).FirstOrDefault();
if (moduledefinition != null)
{
string typename = moduledefinition.ControlTypeTemplate;
if (moduledefinition.ControlTypeRoutes != "")
{
foreach (string route in moduledefinition.ControlTypeRoutes.Split(';'))
{
if (route.StartsWith(control + "="))
{
typename = route.Replace(control + "=", "");
}
}
}
module.ModuleType = typename.Replace("{Control}", control);
}
// ensure module's pane exists in current page and if not, assign it to the Admin pane
if (!panes.Contains(module.Pane))
{
module.Pane = Constants.AdminPane;
}
// calculate module position within pane
if (paneindex.ContainsKey(module.Pane))
{
paneindex[module.Pane] += 1;
}
else
{
paneindex.Add(module.Pane, 0);
}
module.PaneModuleIndex = paneindex[module.Pane];
}
foreach (Module module in modules)
{
module.PaneModuleCount = paneindex[module.Pane] + 1;
}
return modules;
}
private Alias GetAlias(string absoluteUri, List<Alias> aliases)
{
string aliasname;
Alias alias = null;
Uri uri = new Uri(absoluteUri);
if (uri.Segments.Count() > 1)
{
// check if first path segment is an alias ( ie. a subfolder - www.domain.com/subfolder )
aliasname = uri.Authority + "/" + uri.Segments[1];
if (aliasname.EndsWith("/")) { aliasname = aliasname.Substring(0, aliasname.Length - 1); }
alias = aliases.Where(item => item.Name == aliasname).FirstOrDefault();
}
if (alias == null)
{
aliasname = uri.Authority;
alias = aliases.Where(item => item.Name == aliasname).FirstOrDefault();
}
if (alias == null && aliases.Count > 0)
{
// use first alias if Uri does not exist
alias = aliases.FirstOrDefault();
}
alias.Scheme = uri.Scheme;
return alias;
}
} }