ASP.NET Core MVC route based localization with areas - asp.net-mvc

I have created a project in ASP.NET Core and wanted the language to be detected based on the url:
https://localhost:7090/en
If controllers and actions are used in the url, everything works as planned. However, when the registration or login page is called, it does not work (default asp.net identity registration page).
Works: https://localhost:7090/en/Home/Index
Does not work: https://localhost:7090/en/Identity/Account/Register
In the startup, I configured the following for MVC routing:
builder.Services
.AddLocalization()
.AddMvc(options => options.EnableEndpointRouting = false);
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = CultureHelper.GetSupportedCultures();
options.DefaultRequestCulture = new RequestCulturne("en");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
var provider = new RouteDataRequestCultureProvider
{
RouteDataStringKey = "culture",
UIRouteDataStringKey = "culture",
Options = options
};
options.RequestCultureProviders = new[] { provider };
});
builder.Services.Configure<RouteOptions>(options =>
{
options.ConstraintMap.Add("culture", typeof(LanguageRouteConstraint));
});
var options = app.Services.GetService<IOptions<RequestLocalizationOptions>>();
app.UseRequestLocalization(options.Value);
app.UseMvc(routes =>
{
app.MapRazorPages();
routes.MapRoute(
name: "LocalizedDefault",
template: "{culture:culture}/{controller=Home}/{action=Index}/{id?}"
);
});
The language constraint is then used to set the CurrentCulture and CurrentUICulture.
Here is a code snippet for calling the login/register pages:
<li class="nav-item">
<a class="nav-link" asp-area="Identity" asp-page="/Account/Register">
#Language.register
</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-area="Identity" asp-page="/Account/Login">
#Language.login
</a>
</li>
I've tried pretty much everything I've found on Google, but nothing seems to work....
I just think knowing that it does not work because of the pages
[UPDATE]
I was able to get the url to be valid with the following code:
builder.Services
.AddLocalization(options => options.ResourcesPath = "Resources")
.AddMvc(options => options.EnableEndpointRouting = false)
.AddRazorPagesOptions(options =>
{
options.Conventions.Add(new LanguageRouteModelConversion());
});
public class LanguageRouteModelConversion : IPageRouteModelConvention
{
public void Apply(PageRouteModel pageRouteModel)
{
var selectorModels = new List<SelectorModel>();
foreach (var selector in pageRouteModel.Selectors.ToList())
{
var template = selector.AttributeRouteModel?.Template;
selectorModels.Add(new SelectorModel()
{
AttributeRouteModel = new AttributeRouteModel
{
Template = "/{culture}/" + template
}
});
}
foreach (var model in selectorModels)
pageRouteModel.Selectors.Add(model);
}
}
But i still don't know how to call the page properly

You said you have no Account folder in Identity directory. If so then how you do you expect register,login page router will work?
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<YourDbContextName>();
Then Scaffold Register, Login, LogOut page etc. After scaffolded, you will find a separate Account folder in Pages folder. and Your Account folder actually contains login,register page. It should resolve your issue.

builder.Services
.AddLocalization(options => options.ResourcesPath = "Resources")
.AddMvc(options => options.EnableEndpointRouting = false)
.AddRazorPagesOptions(options =>
{
options.Conventions.AddAreaFolderRouteModelConvention("Identity", "/", pageRouteModel =>
{
foreach (var selectorModel in pageRouteModel.Selectors)
selectorModel.AttributeRouteModel.Template = "{culture:culture}/" + selectorModel.AttributeRouteModel.Template;
});
});
This did the trick for me, i finally figured it out...

Add the culture param to the URL asp-route-culture=...
#{ var culture= System.Globalization.CultureInfo.CurrentCulture.Name }
<li class="nav-item">
<a class="nav-link" asp-area="Identity" asp-page="/Account/Register" asp-route-culture="#culture">
#Language.register
</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-area="Identity" asp-page="/Account/Login" asp-route-culture="#culture">
#Language.login
</a>
</li>

Related

Why does HttpContext.User.Identity.Name contain the wrong name?

I have a .NET 5 MVC application that periodically checks access based on the HttpContext.User.Identity.Name value. The app uses Azure AD. Sometimes as I switch between users (Signing out and Signing in), the HttpContext.User.Identity.Name value will contain the previous user's name and the current user will have the access of the previous user.
Even more strange is the the login shown is correct.
This is just the boiler plate code generated by visual studio:
#using System.Security.Principal
<ul class="navbar-nav">
#if (User.Identity.IsAuthenticated)
{
<li class="nav-item">
<span class="navbar-text text-light">Hello #User.Identity.Name!</span>
</li>
<li class="nav-item">
<a class="nav-link text-light" asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignOut">Sign out</a>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-light" asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignIn">Sign in</a>
</li>
}
</ul>
So #User.Identity.Name on the .cshtml is always correct, but HttpContext.User.Identity.Name on the controller is the previous (cached?) user.
Any clues to why the previous user gets cached?
Here is a sample of how I call it on the controller:
ViewData["User"] = HttpContext.User;
ViewData["Menu"] = await _menuService.GetReportMenuAsync(MenuService.ReportMenuItemOption.Genesis01, User.Identity.Name);
var userAccess = await _userAccessService.GetUserEntityAccessAsync(User.Identity.Name);
var model = new SomeModel
{
SearchDate = DateTime.Now.AddDays(-7),
Banks = userAccess.Banks,
ATMIds = userAccess.ATMs
};
return View(model);
This is in my StartUp:
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();

KnockoutJS with MVC Partialview

I am having problems persisting data from KO observableArray in a MVC Partialview...
I have a side navigation menu where one of the nodes is dynamically built using KOjs 'foreach', and this navigation menu should persist across the site. The menu is rendered ok on the first View page but when I navigate on other page (clicking on menu option or any way) the dynamic node is empty, although the observableArrays were fetched and loaded with the correct data.
It seems like the navigaition menu (which is a partial view) is not refreshing/reloading to render the menu nodes/options again.
Any help si much appreciated. TIA!
self.reports = ko.observableArray([]);
self.reportCategories = ko.observableArray([]);
self.getReports = function () {
App.Server.get("/.../reports/")
.then(function (vm) {
self.reports(vm.reports);
self.reportCategories(vm.categories);
});
};
self.init = function () {
self.getReports();
};
self.init();
<li>
<a class="level1" data-toggle="collapse" href="#report-submenu" aria-expanded="false" aria-controls="collapseExample">Reports</a>
<ul class="level1 collapse" id="report-submenu" data-bind="foreach: reportCategories">
<li>
<a class="level2 collapse" data-toggle="collapse" data-bind="text: label, attr: { href: '#' + value }"
aria-expanded="false" aria-controls="collapseExample"></a>
<ul class="level2 collapse" data-bind="foreach: reports, attr: { id: value }">
<li data-bind="if: category == $parent.categoryId">
<a class="level3" data-bind="text: menuName, attr: { href: reportName }"></a>
</li>
</ul>
</li>
Here's the other part of the code (a Nancy GET method):
Get["/"] = _ =>
{
var reportModel = new ReportModel();
var reports = reportService.GetList();
if (reports != null)
{
// Categories
reportModel.Categories = reports.Select(s => s.Category).Distinct().Select(c => new ReportCategoryModel()
{
CategoryId = c,
Label = c.TrimStart("Reports/".ToCharArray()),
Value = c.ToLower().Replace('/', '-')
}).ToList();
// Reports
reportModel.Reports = reports.Select(r => new ReportRecord()
{
MenuName = r.MenuName,
ReportName = r.ReportName,
Category = r.Category,
}).ToList();
}
return Response.AsJson(reportModel);
};

Umbraco Navigation - Partial View

I have a True/False editor in my page doc type and I am using if to create the site navigation.
If topNavigation Checkbox is checked display item in the navigation
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
var root = Umbraco.TypedContentAtRoot().First();
var nodes = root.Children.Where(x => x.GetPropertyValue("topNavigation") != "True");
}
<ul class="nav navbar-nav">
#foreach (var page in nodes)
{
<p>#page.GetPropertyValue("topNavigation")</p>
<li class="#(page.IsAncestorOrSelf(Model.Content) ? "current" : null)">
#page.Name <span class="glyphicon glyphicon-chevron-down"></span>
</li>
}
</ul>
#*}*#
I can't seem to compare against a true value.
This shows everything -
var nodes = root.Children.Where(x => x.GetPropertyValue("topNavigation") != "True");
..and this show nothing
var nodes = root.Children.Where(x => x.GetPropertyValue("topNavigation") == "True");
Even though the checkboxes are checked.
You should able to use GetPropertyValue<bool>
var root = Umbraco.TypedContentAtRoot().First();
var nodes = root.Children.Where(x => x.GetPropertyValue<bool>("topNavigation"));

Database update notification using SignalR and Entity Framework

I'm trying to make a website using asp.net mvc 4 and EF 6 where I want to update the messagebox of adminpanel if any user sends him any message. I'm using SignalR for live notification but I'm new to SignalR and for some reason I don't get the updates live, I've to refresh the page to get the newly added message. Here are my codes,
Hub Class
public class ChatHub : Hub
{
MyProj.Models.MsgToOwner messages = new MyProj.Models.MsgToOwner();
public void SendNotify(string username, string title, string datetimeNow)
{
var newName = messages.name;
var newTitle = messages.title;
var newTime = messages.addedtime;
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
context.Clients.All.getNotify(newName, newTitle, newTime);
}
}
View for All Messages
#{
var MsgList = (List<MyProj.Models.MsgToOwner>)ViewBag.MsgList;
}
<body>
<script src="~/Scripts/jquery.signalR-1.1.4.min.js"></script>
<script src="~/signalr/hubs"></script>
<script src="~/Scripts/LiveScript.js"></script>
<ul class="nav navbar-top-links navbar-right">
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-envelope fa-fw"></i><i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-messages" id="newItem">
#foreach (var item in MsgList)
{
<li>
<a href="#">
<div>
<strong>#Html.DisplayFor(modelItem => item.title)</strong>
<span class="pull-right text-muted">
<em>#Html.DisplayFor(modelItem => item.addedtime)</em>
</span>
</div>
<div>Sent By : <strong>#Html.DisplayFor(modelItem => item.name)</strong></div>
</a>
</li>
<li class="divider"></li>
}
</ul>
</li>
</ul>
</body>
LiveScript.js (script for updating from SignalR)
$(function () {
var chat = $.connection.chatHub;
chat.client.getNotify = function (username, title, datetimeNow) {
$('#newItem').append(
'<li><div><strong>' + htmlEncode(title) + '</strong><span class="pull-right text-muted"><em>' + htmlEncode(datetimeNow) + '</em></span></div><div>Sent By : <strong>' + htmlEncode(username) + '</strong></div></li><li class="divider"></li>');
};
$.connection.hub.start().done(function () {
$('#btnSend').click(function () {
chat.server.SendNotify($('#getName').val(), $('#getTitle').val(), $('#getDate').val());
});
});
});
I must be doing something wrong here but I can't figure it out since I'm new to SignalR. How can I update the messagebox whenever a new message is saved in db? Any guidance will be helpful for me. Thanks.

How to include CSS Class in ActionLink in MVC4

My code is
#Html.ActionLink("test1", "Index", new { Area = "area1", Controller = "controller1" })
I want to include the following css class in the action link.
class="rtsLink"
Also, can we add multiple css class. If so, I need to add another css class.
class="rtsTxt"
Update:
<li class="rtsLI" id="Summary"><span class="rtsTxt">Test</span></li>
Above I am replacing with following actionlink:
<li class="rtsLI" >#Html.ActionLink("test1", "Index", new { Area = "Tools", Controller = "controller1" }, new { #class = "rtsLink rtsTxt"})</li> "
At first css is working fine. But when using Actionlink, css not working. Thanks
You can simply add a new anonymous object as the fourth parameter
#Html.ActionLink("User Security", "Index", new { Area = "Tools", Controller = "UserSecurity" }, new { #class = "rtsLink rtsTxt" })
Note that the word class is reserved in C#, so you must prefix it with an #.
Since you have a span inside anchor. You could try this.
<li class="rtsLI" id="Summary">
<a href="#Url.Action("Index", new { Area = "Tools", Controller = "UserSecurity" })"
class="rtsLink">
<span class="rtsTxt">User Security</span>
</a>
</li>

Resources