Asp.Net Core 3.0 MVC - 文化 Cookie 的脚手架身份页面路由

Asp.Net Core 3.0 MVC - Scaffolded Identity Page Routing for Culture Cookie

我已经在我的 Asp.Net Core MVC 3.0 应用程序中实现了这个用于本地化的 nuget 包,我发现它很棒。

https://github.com/LazZiya/ExpressLocalization

除了我的脚手架身份页面之外的所有页面都工作正常。

当我导航到我的脚手架 Register.cshtml 页面时,我遇到了一个问题,我的本地化 cookie 没有被放入 URL。但是,如果我手动将文化(例如 deen 等)键入 URL,则本地化工作正常。

例如,当我点击注册 link时,URL应该是

但它总是

Startup.cs

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.EntityFrameworkCore;
using MyApp.Data;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using LazZiya.ExpressLocalization;
using Microsoft.AspNetCore.Localization;
using MyApp.LocalizationResources;
using MyApp.Models;

namespace MyApp
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection")));
            services.AddDefaultIdentity<ApplicationUser>( options => { options.SignIn.RequireConfirmedAccount = true; }).AddEntityFrameworkStores<ApplicationDbContext>();
            services.AddControllersWithViews();
            services.AddRazorPages();

            var cultures = new []
            {
                new CultureInfo("de"),
                new CultureInfo("en"),
            };

            services.AddControllersWithViews()
                .AddExpressLocalization<LocSource>(ops =>
                    {
                        ops.UseAllCultureProviders = false;
                        ops.ResourcesPath = "LocalizationResources";
                        ops.RequestLocalizationOptions = o =>
                        {
                            o.SupportedCultures = cultures;
                            o.SupportedUICultures = cultures;
                            o.DefaultRequestCulture = new RequestCulture("en");
                        };
                    });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");

                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();
            app.UseRequestLocalization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{culture=en}/{controller=Home}/{action=Index}/{id?}");
                endpoints.MapControllerRoute(
                    name: "Features",
                    pattern: "{culture=en}/{controller=Features}/{action=Features}/{id?}");
                endpoints.MapControllerRoute(
                    name: "About",
                    pattern: "{culture=en}/{controller=About}/{action=About}/{id?}");
                endpoints.MapControllerRoute(
                    name: "Help",
                    pattern: "{culture=en}/{controller=Help}/{action=Help}/{id?}");
                endpoints.MapRazorPages();
            });
        }
    }
}

Register.cshtml.cs (我认为这里有问题,因为我的 MVC 页面没有空值 returnUrl

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using MyApp.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using LocSourceNameReferenceLibrary;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Http;

namespace MyApp.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class RegisterModel : PageModel
    {
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly ILogger<RegisterModel> _logger;
        private readonly IEmailSender _emailSender;
        RegisterPageLocSourceNames _locSourceRegisterPageNameReferenceLibrary = new RegisterPageLocSourceNames();
        SharedCrossPageLocSourceNames _locSourceSharedCrossPageNameReferenceLibrary = new SharedCrossPageLocSourceNames();

        public RegisterModel(
            UserManager<ApplicationUser> userManager,
            SignInManager<ApplicationUser> signInManager,
            ILogger<RegisterModel> logger,
            IEmailSender emailSender)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _logger = logger;
            _emailSender = emailSender;
        }

        [BindProperty]
        public InputModel Input { get; set; }
        public string ReturnUrl { get; set; }
        public string PageTabTitle { get; set; }
        public string Title { get; set; }
        public string SubTitle { get; set; }
        public string Heading { get; set; }
        public string ServiceHeading { get; set; }
        public string EmailHeading { get; set; }
        public string PasswordHeading { get; set; }
        public string ConfirmPassword { get; set; }
        public string RegisterButtonName { get; set; }
        public string NavBarHome { get; set; }
        public string NavBarFeatures { get; set; }
        public string NavBarAbout { get; set; }
        public string NavBarHelpCenter { get; set; }

        public IList<AuthenticationScheme> ExternalLogins { get; set; }

        public class InputModel
        {
            [Required]
            [EmailAddress]
            [Display(Name = "Email")]
            public string Email { get; set; }

            [Required]
            [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
            [DataType(DataType.Password)]
            [Display(Name = "Password")]
            public string Password { get; set; }

            [DataType(DataType.Password)]
            [Display(Name = "Confirm Password")]
            [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
            public string ConfirmPassword { get; set; }
        }

        public async Task OnGetAsync(string returnUrl = null)
        {
            PageTabTitle = _locSourceRegisterPageNameReferenceLibrary.GetLocSourcePageTabTitleNameReferenceForRegisterPage();
            Title = _locSourceRegisterPageNameReferenceLibrary.GetLocSourceTitleNameReferenceForRegisterPage();
            SubTitle = _locSourceRegisterPageNameReferenceLibrary.GetLocSourceSubtitleNameReferenceForRegisterPage();
            Heading = _locSourceRegisterPageNameReferenceLibrary.GetLocSourceHeadingNameReferenceForRegisterPage();
            ServiceHeading = _locSourceRegisterPageNameReferenceLibrary.GetLocSourceServiceHeadingNameReferenceForRegisterPage();
            EmailHeading = _locSourceRegisterPageNameReferenceLibrary.GetLocSourceEmailHeadingNameReferenceForRegisterPage();
            PasswordHeading = _locSourceRegisterPageNameReferenceLibrary.GetLocSourcePasswordHeadingNameReferenceForRegisterPage();
            ConfirmPassword = _locSourceRegisterPageNameReferenceLibrary.GetLocSourceConfirmPasswordHeadingNameReferenceForRegisterPage();
            RegisterButtonName = _locSourceRegisterPageNameReferenceLibrary.GetLocSourceRegisterButtonNameReferenceForRegisterPage();
            NavBarHome = _locSourceRegisterPageNameReferenceLibrary.GetLocSourceNavBarHomeReferenceForRegisterPage();
            NavBarFeatures = _locSourceRegisterPageNameReferenceLibrary.GetLocSourceNavBarFeaturesReferenceForRegisterPage();
            NavBarAbout = _locSourceRegisterPageNameReferenceLibrary.GetLocSourceNavBarAboutReferenceForRegisterPage();
            NavBarHelpCenter = _locSourceRegisterPageNameReferenceLibrary.GetLocSourceNavBarHelpCenterReferenceForRegisterPage();

            ReturnUrl = returnUrl;
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
        }

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl ??= Url.Content("~/");
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {
                var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email };
                var result = await _userManager.CreateAsync(user, Input.Password);
                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");

                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { area = "Identity", userId = user.Id, code },
                        protocol: Request.Scheme);

                    await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                        $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        return RedirectToPage("RegisterConfirmation", new { email = Input.Email });
                    }
                    else
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(returnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }
    }
}

区域 > 身份 > 页面 > 帐户 > _ViewImports.cshtml

@using MyApp
@using MyApp.Areas.Identity.Pages.Account
@using LazZiya.ExpressLocalization
@inject ISharedCultureLocalizer _loc
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, LazZiya.TagHelpers
@addTagHelper *, LazZiya.ExpressLocalization

我还根据 these instructions 添加了 RouteTemplateModelConvention.cs class,但我认为它没有任何作用。它在评论中说它应该被自动调用。我已经放在根目录了 ~/RouteTemplateModelConvention.cs

这是我的文件夹结构:

我猜是因为身份页面的脚手架自动使用 Razor 页面进行注册和登录,所以我的 MVC 本地化实现不适用于这些。

但是...如果我手动将区域性("en" 或 "de")添加到浏览器 URL,它 工作,它只是没有自动拾取它。

我已经尝试了多种变体来使它正常工作,但我确信它很简单,我在 cookie 的路由中丢失了...有什么想法吗?

更新:

我尝试在我的共享 _Layout.cshtml 文件中使用 asp-route-culture=“@CultureInfo.CurrentCulture.Name” 将文化参数添加到 <a></a> link,但是我收到一条错误消息说 @CultureInfo 在当前上下文中不存在。

更新:

好的进展... 我将 @using System.Globalization 命名空间添加到我的布局文件中。 现在 URL 是 https://localhost:44305/"de"/Identity/Account/Register 我想弄清楚为什么引号在 URL 中。有什么想法吗?

使用 asp-route-culture="@CultureInfo.CurrentCulture.Name" 应该可以解决问题。

在您的代码中,您似乎使用了引号“...” 它们应该是引号 "..." :)