.Net Core Entity Framework 电子邮件确认“单击此处”链接不更新“EmailConfirmed”数据库属性

Posted

技术标签:

【中文标题】.Net Core Entity Framework 电子邮件确认“单击此处”链接不更新“EmailConfirmed”数据库属性【英文标题】:.Net Core Entity Framework Email Confirmation 'Click Here' link does not update 'EmailConfirmed' DB property 【发布时间】:2021-08-29 20:14:33 【问题描述】:

我已按照此处 Microsoft 的说明在我的 .Net 5.0 应用中为我的用户注册电子邮件确认设置​​了 SendGrid:http://go.microsoft.com/fwlink/?LinkID=532713

在用户单击注册确认电子邮件中的确认链接之前,一切正常。

这个问题是由我的确认链接中的杂散放大器引起的。我正在尝试了解它的来源以及如何删除它。

当新用户在Register.cshtml 页面上单击“提交”时,他们会成功定向到RegisterConfirmation.cshtml 页面,并且电子邮件会在他们的收件箱中收到。

实际行为:

用户单击电子邮件中的链接并点击ConfirmEmail 页面。

用户被重定向到/Index页面。

数据库中的EmailConfirmed bool 未更新。

如果我在控制器中注释掉到 /Index 的重定向,则会收到如下所示的空值错误。

//if (userId == null || code == null)
//
//    return RedirectToPage("/Index", new  culture );
//

但是,查询选项卡显示 userId 和 code 是正确的。

预期行为:

用户点击电子邮件中的链接。

数据库中的EmailConfirmed 布尔值IS已更新。

用户看到带有成功消息的ConfirmEmail 页面。

ConfirmEmail.cshtml.cs

    using System;
snip...
    
    namespace MyApp.Areas.Identity.Pages.Account
    
        [AllowAnonymous]
        public class ConfirmEmailModel : PageModel
        
            private readonly UserManager<ApplicationUser> _userManager;
            private readonly ISharedCultureLocalizer _loc;
            private readonly string culture;
            readonly ConfirmEmailPageLocSourceNames _locSourceConfirmEmailPageNameReferenceLibrary = new ConfirmEmailPageLocSourceNames();
            readonly SharedCrossPageLocSourceNames _locSourceSharedCrossPageNameReferenceLibrary = new SharedCrossPageLocSourceNames();
    
            public ConfirmEmailModel(UserManager<ApplicationUser> userManager, ISharedCultureLocalizer loc)
            
                _userManager = userManager;
                _loc = loc;
                culture = System.Globalization.CultureInfo.CurrentCulture.Name;
            
    
            [TempData]
            public string StatusMessage  get; set; 
snip...
    
            public async Task<IActionResult> OnGetAsync(string userId, string code)
            
snip...
    
                if (userId == null || code == null)
                
                    return RedirectToPage("/Index", new  culture );
                
    
                var user = await _userManager.FindByIdAsync(userId);
                if (user == null)
                
                    var msg = _loc.GetLocalizedString("Unable to load user with ID '0'.", userId);
                    return NotFound(msg);
                
    
                code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));
                var result = await _userManager.ConfirmEmailAsync(user, code);
                if (result.Succeeded)
                
                    var msg = _loc.GetLocalizedString("Thank you for confirming your email.");
                    TempData.Success(msg);
                
                else
                
                    var msg = _loc.GetLocalizedString("Error confirming your email.");
                    TempData.Danger(msg);
                
                return Page();
            
        
    

RegisterConfirmation.cshtml.cs

using Microsoft.AspNetCore.Authorization;
snip...

namespace MyApp.Areas.Identity.Pages.Account

    [AllowAnonymous]
    public class RegisterConfirmationModel : PageModel
    
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly IEmailSender _sender;
        readonly RegisterConfPageLocSourceNames _locSourceRegisterConfPageNameReferenceLibrary = new RegisterConfPageLocSourceNames();
        readonly SharedCrossPageLocSourceNames _locSourceSharedCrossPageNameReferenceLibrary = new SharedCrossPageLocSourceNames();

        public RegisterConfirmationModel(UserManager<ApplicationUser> userManager, IEmailSender sender)
        
            _userManager = userManager;
            _sender = sender;
        

        public string Email  get; set; 
 snip...

        public async Task<IActionResult> OnGetAsync(string email)
        
            PageTabTitle = _locSourceRegisterConfPageNameReferenceLibrary.GetLocSourcePageTabTitleNameReferenceForRegisterConfPage();
 snip...

            if (email == null)
            
                return RedirectToPage("/Index");
            

            var user = await _userManager.FindByEmailAsync(email);
            if (user == null)
            
                return NotFound($"Unable to load user with email 'email'.");
            

            Email = email;

            return Page();
        
    

Register.cshtml.cs

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

            var mailHeader = _loc.GetLocalizedString("Confirm your GatheringForGood email");
            var mailBody = _loc.GetLocalizedString(CultureInfo.CurrentCulture.Name, "Please confirm your GatheringForGood account by <a href='0'>clicking here</a>.", HtmlEncoder.Default.Encode(callbackUrl));
                await _emailSender.SendEmailAsync(Input.Email, mailHeader, mailBody);

EmailSender.cs 中的函数

public Task SendEmailAsync(string email, string subject, string message)

    return Execute(Options.SendGridKey, subject, message, email);


public Task Execute(string apiKey, string subject, string message, string email)

    var client = new SendGridClient(apiKey);
    var msg = new SendGridMessage()
    
        From = new EmailAddress("info@myemail.com", Options.SendGridUser),
        Subject = subject,
        PlainTextContent = message,
        HtmlContent = message
    ;
    msg.AddTo(new EmailAddress(email));

    // Disable click tracking.
    // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
    msg.SetClickTracking(false, false);

    return client.SendEmailAsync(msg);

更新:

在我的日志中的回调字符串中显示

userId=362c17ae-7854-42fb-91c3-efb19cc875f2&code=

但在 gmail 中收到的链接显示

userId=362c17ae-7854-42fb-91c3-efb19cc875f2&amp;code=

在 Postman 中测试,这绝对是问题所在。我需要电子邮件正文中的链接看起来像这样。注意没有放大器;

https://localhost:44305/en/Identity/Account/ConfirmEmail?userId=d62d4727-f6ce-493c-bcf3-eb85a50a914f&code=Q2ZESjhKbkE2NU5BVk85S2drRnMvV3VtZXBySVFlTHZrQlNvUU9xbUxrYWQ5NjFDV0NvZGY1eHVCK01SSHVIL3EwMjEwYk8rU1lLaHJ4UHF1VS84RjJQTThBWlY4VHZTcGcrQVpiZU9wWHFyWnlsVkFpSFVUV3lIMGJjaG14aFJKQkgxNjZoQkVNM3ZETnR2WHhoZmx0ZnhQR095azdDREJVZVdJN01CTTRCcFptejJvSURjNHloZHdxRDl0UCs0eEdic1NMK25wbnFqb0xhdHFoR3M3T3BkTElhbG5TVU9obTJaTFpvc0xUb0RINzM2UmFBTVlrakZWL2VsV0YvUEJSaE1HQT09

【问题讨论】:

您必须编写代码来进行更新。 Stack Overflow 不存在为您执行此操作。 该评论显然没有帮助。我按照 Microsoft 的标识框架电子邮件确认说明进行操作,但它不起作用。我试过自己弄清楚,但我很难过。 “实体框架”搭建的身份页面。 @DMur - 你永远不会表明你正在使用实体框架UI,其中有很多东西为你搭建了脚手架。在教程中提供第四课(或其他课程)的链接对读者的期望太高了!我认为您的问题是有效的,但值得重写... &amp;amp; 在 html 中通常非常好。发生这种情况是因为您在 Register.cshtml 的 url 上调用了 HtmlEncoder.Default.Encode.. 【参考方案1】:

看起来有值的变量是amp;code; 不是代码。你有没有机会在某个地方有 2 个 & 符号?是的 -

b3a&amp;code=Q2ZE

找出您的代码在确认电子邮件中生成此类错误链接的原因

【讨论】:

【参考方案2】:

生成的链接似乎也在确认 URL 中查询字符串参数之间的&amp; 编码:

// This is the problem
...&amp;code=Q2ZESjhKb...

//it should be lilke below
...&code=Q2ZESjhKb...

【讨论】:

链接生成代码中没有与号。上面的更新 2。 您可以在callback 字符串中找到它 Hmmm... 在我日志中的回调字符串中显示 userId=362c17ae-7854-42fb-91c3-efb19cc875f2&code= 但在 gmail 中收到的链接中显示 userId=362c17ae-7854-42fb- 91c3-efb19cc875f2&code= 检查电子邮件发送中间件(是SendGrid吗?),应该有一些选项可以对电子邮件正文进行编码或将其格式定义为html。 虽然我不知道我的日志中的 & 来自哪里。 userId 值的末尾没有 & 符号。也许它应该只是 875f2code=【参考方案3】:

已修复! 最后我只需要从我的 mailBody 字符串中删除 Html 编码。

我换了:

var mailBody = _loc.GetLocalizedString(CultureInfo.CurrentCulture.Name, "Please confirm your GatheringForGood account by <a href='0'>clicking here</a>.", HtmlEncoder.Default.Encode(callbackUrl));

与:

var mailBody = _loc.GetLocalizedString(CultureInfo.CurrentCulture.Name, "Please confirm your GatheringForGood account by <a href='" + callbackUrl + "'>clicking here</a>.");

感谢您的所有帮助。

【讨论】:

以上是关于.Net Core Entity Framework 电子邮件确认“单击此处”链接不更新“EmailConfirmed”数据库属性的主要内容,如果未能解决你的问题,请参考以下文章

C# ASP.NET Core Entity Framework Core 异步 ToQueryable 比较

根据 ASP.NET Core 和 Entity Framework Core 中的条件禁用 [必需] 属性

ASP.NET Core 和 Entity Framework Core:Linq 中的左(外)连接

dotnet跨平台微软昨天宣布正式发布.NET Core RC2和.NET Core SDK Preview 1,还有Entity Framework Core RC2

如何为 ASP.net Core 配置 Entity Framework 6

在 ASP.NET Core 中使用 Entity Framework 6