Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/visual-studio-2012/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net core 未发送Core3/React确认电子邮件_.net Core_Asp.net Identity_Asp.net Core 3.0 - Fatal编程技术网

.net core 未发送Core3/React确认电子邮件

.net core 未发送Core3/React确认电子邮件,.net-core,asp.net-identity,asp.net-core-3.0,.net Core,Asp.net Identity,Asp.net Core 3.0,此问题适用于具有外部身份提供程序的core3/react项目,创建如下 dotnet new react --auth Individual --use-local-db --output conf 并修改为支持外部身份提供程序。该包已添加 dotnet add package Microsoft.AspNetCore.Authentication.MicrosoftAccount 并且启动被修改 services.AddAuthentication() .AddMicrosoftAccou

此问题适用于具有外部身份提供程序的core3/react项目,创建如下

dotnet new react --auth Individual --use-local-db --output conf
并修改为支持外部身份提供程序。该包已添加

dotnet add package Microsoft.AspNetCore.Authentication.MicrosoftAccount
并且启动被修改

services.AddAuthentication()
.AddMicrosoftAccount(options =>
{
    options.ClientId = Configuration["Authentication:Microsoft:ClientId"];
    options.ClientSecret = Configuration["Authentication:Microsoft:ClientSecret"];
    options.CallbackPath = "/signin-microsoft";
})
之后,我通过注册为用户来测试我的工作。没有抛出错误,但承诺的确认电子邮件从未到达

按照说明末尾的故障排除建议,我在IEmailSender实现的SendEmailAsync方法的开头设置了一个断点,并重复了这个练习。未命中断点

如果我通过更新数据库手动确认帐户

  • 我可以登录
  • “忘记密码”链接将我带到密码恢复页面,使用该链接将点击我的断点并成功发送一封带有有效链接的密码重置电子邮件
显然,我的IEmailSender实现工作正常,并且已正确注册。它与示例代码不完全相同,因为我有自己的Exchange服务器,并且没有使用SendGrid,但它成功地发送了一封电子邮件以重置密码,我可以无障碍地重复多次

尽管这可能是问题的原因,但这是我的实现

public class SmtpEmailSender : IEmailSender
{
    public SmtpEmailSender(IOptions<SmtpOptions> options)
    {
        this.smtpOptions = options.Value;
    }
    private SmtpOptions smtpOptions { get; }
    public Task SendEmailAsync(string email, string subject, string htmlMessage)
    {
        var smtp = new SmtpClient();
        if (!smtpOptions.ValidateCertificate)
        {
            smtp.ServerCertificateValidationCallback = (s, c, h, e) => true;
        }
        smtp.Connect(smtpOptions.Host, smtpOptions.Port, SecureSocketOptions.Auto);
        if (smtpOptions.Authenticate)
        {
            smtp.Authenticate(smtpOptions.Username, smtpOptions.Password);
        }
        var message = new MimeMessage()
        {
            Subject = subject,
            Body = new BodyBuilder() { HtmlBody = htmlMessage }.ToMessageBody()
        };
        message.From.Add(new MailboxAddress(smtpOptions.Sender));
        message.To.Add(new MailboxAddress(email));
        return smtp.SendAsync(FormatOptions.Default, message).ContinueWith(antecedent =>
        {
            smtp.Disconnect(true);
            smtp.Dispose();
        });
    }
}
SmptOptions只是从appsettings.json中拖出并注入到ctor中的设置。很明显,这一方面是有效的,否则密码重置电子邮件将不起作用

注册不会有任何问题,因为应用程序停止生成需要阅读并遵循我链接的帐户确认说明的消息

为了确定问题是否是由于我的代码无意中产生的副作用造成的,我创建了一个
iemailssender

public class DummyEmailSender : IEmailSender
{
    private readonly ILogger logger;

    public DummyEmailSender(ILogger<DummyEmailSender> logger)
    {
        this.logger = logger;
    }
    public Task SendEmailAsync(string email, string subject, string htmlMessage)
    {
        logger.LogInformation($"SEND EMAIL\r\nemail={email} \r\nsubject={subject}\r\nhtmlMessage={htmlMessage}\r\n{new StackTrace().ToString().Substring(0,500)}");
        return Task.CompletedTask;
    }
}
看来它应该会起作用。我们知道什么

  • 不会抛出任何未经处理的错误,它会传递到RegisterConfirmation,后者会显示一条关于该电子邮件的消息,但该消息永远不会出现
  • CreateUser
    被调用并成功。我们知道这一点,因为用户是在数据库中创建的。所以它肯定会过去,这意味着
    ModelState
    不是空的,
    .IsValid
    是真的
  • 尽管上面有代码,但实际上并没有调用iemailssender.sendmailasync
  • 如果
    result.succeed
    为真,则会出现一条日志消息,内容类似于“用户使用Microsoft帐户提供程序创建了一个帐户”
  • 它重定向到
    https://localhost:5001/Identity/Account/RegisterConfirmation?Email=accountname@outlook.com
我看到了大多数事情的日志消息。尝试在第一次传递创建用户后进行第二次注册,但未能发送电子邮件,控制台和事件日志中将显示关于重复用户名的警告。直接在数据库中设置确认,我们可以登录,然后以交互方式删除帐户,并显示这些活动的日志

但没有显示用于确认的日志。真正让我头疼的是,它随后重定向到
https://localhost:5001/Identity/Account/RegisterConfirmation?Email=accountname@outlook.com

那太疯狂了。为了达到这个目的,
userManager.AddLoginAsync()
必须返回true,在这种情况下,下一行是向记录器写入有关创建用户帐户的内容


这毫无意义。

你应该自己发送确认电子邮件,它不会自动发送。 注册用户后:

        string token = await userManager.GenerateEmailConfirmationTokenAsync(user);
        string urltoken = Base64UrlEncoder.Encode(token);

        string link = string.Format(emailOptions.ConfirmationUrl, user.Id, urltoken);
        string body = $"<a href='{link}'>confirm</a>";
        await emailSender.SendEmailAsync(user.Email, "confirmation", body);
string-token=wait-userManager.GenerateEmailConfirmationTokenAsync(用户);
字符串urltoken=Base64UrlEncoder.Encode(令牌);
string link=string.Format(emailOptions.ConfirmationUrl、user.Id、urltoken);
字符串体=$“”;
等待emailSender.SendEmailAsync(user.Email,“确认”,正文);

我创建了一个全新的项目并完成了这个练习。它工作得很好

有什么区别?失败的版本被添加到一个现有的项目中,在解决CICD问题的过程中,该项目在3.0和3.1之间反复出现了好几次。很明显,它以某种不明显的方式损坏了,这不是问题


我没有删除整个问题的唯一原因是其他人可能会掉进这个洞。

我使用的是dotnet core 3.1而不是2.x,我会更新这个问题。如果您想查看新玩具,请获取3.1 SDK并生成一个项目
dotnet new react-au Individual
,然后对其进行自定义,以添加对外部身份提供商的支持。更改这两行代码,“wait _emailStore.SetEmailAsync(…);”使用“wait_userManager.CreateAsync(user);”如果我有机会编译3.1分支,我会试试。
public override async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    // Get the information about the user from the external login provider
    var info = await _signInManager.GetExternalLoginInfoAsync();
    if (info == null)
    {
        ErrorMessage = "Error loading external login information during confirmation.";
        return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
    }

    if (ModelState.IsValid)
    {
        var user = CreateUser();

        await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
        await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);

        var result = await _userManager.CreateAsync(user);
        if (result.Succeeded)
        {
            result = await _userManager.AddLoginAsync(user, info);
            if (result.Succeeded)
            {
                _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);

                var userId = await _userManager.GetUserIdAsync(user);
                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 = userId, code = 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 account confirmation is required, we need to show the link if we don't have a real email sender
                if (_userManager.Options.SignIn.RequireConfirmedAccount)
                {
                    return RedirectToPage("./RegisterConfirmation", new { Email = Input.Email });
                }

                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    ProviderDisplayName = info.ProviderDisplayName;
    ReturnUrl = returnUrl;
    return Page();
}
        string token = await userManager.GenerateEmailConfirmationTokenAsync(user);
        string urltoken = Base64UrlEncoder.Encode(token);

        string link = string.Format(emailOptions.ConfirmationUrl, user.Id, urltoken);
        string body = $"<a href='{link}'>confirm</a>";
        await emailSender.SendEmailAsync(user.Email, "confirmation", body);