Search code examples
c#asp.net-coreasp.net-core-mvcsendgrid

How to set variables for dynamic SendGrid templates?


I know how i add the Template msg.SetTemplateId("d-xxxxxxxxxxxxxxxx");

But how i can add the {HtmlEncoder.Default.Encode(callbackUrl)} from Register.cs in the Email Dynamic Template from Sendgrid?

I already implemented a Email service and receive Emails from SendGrid with my Account Activation Link.

Email Controller:

public class SendGridEmailSender : IEmailSender
{

    public Task SendEmailAsync(string email, string subject, string htmlMessage)
    {
        var sendGridKey = @"APIKEY";
        return Execute(sendGridKey, subject, htmlMessage, email);
    }

    public Task Execute(string apiKey, string subject, string message, string email)
    {
        
        var client = new SendGridClient(apiKey);
        var msg = new SendGridMessage()
        {
            From = new SendGrid.Helpers.Mail.EmailAddress("[email protected]", "Webmaster"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message
        };
        msg.AddTo(new SendGrid.Helpers.Mail.EmailAddress(email));
        //msg.SetTemplateId("d-xxxxxxxxxxxxxxxx");
        // Disable click tracking.
        // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
        msg.SetClickTracking(false, false);

        return client.SendEmailAsync(msg);
    }

}

Register.cs

public class RegisterModel : PageModel
{
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly ILogger<RegisterModel> _logger;
    private readonly IEmailSender _emailSender;

    public RegisterModel(
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> signInManager,
        ILogger<RegisterModel> logger,
        IEmailSender emailSender)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _logger = logger;
        _emailSender = emailSender;
    }
    [TempData]
    public string confirmLink { get; set; }

    [BindProperty]
    public InputModel Input { get; set; }

    public string ReturnUrl { get; set; }

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

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

        [Required]
        [Display(Name = "First Name")]
        public string FirstName { get; set; }

        [Required]
        [Display(Name = "Last Name")]
        public string LastName { get; set; }

        [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)
    {
        ReturnUrl = returnUrl;
        ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
    }

    public async Task<IActionResult> OnPostAsync(string returnUrl = null)
    {
        returnUrl = returnUrl ?? Url.Content("~/");
        ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
        if (ModelState.IsValid)
        {
            var user = new ApplicationUser { UserName = Input.UserName, Email = Input.Email, FirstName = Input.FirstName, LastName = Input.LastName };
            var result = await _userManager.CreateAsync(user, Input.Password);
            if (result.Succeeded)
            {
                _logger.LogInformation("User created a new account with password.");
                await _userManager.AddToRoleAsync(user, Models.Roles.Basic.ToString());
                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 = code, returnUrl = returnUrl },
                    protocol: Request.Scheme);
                
                //
                await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                    $"Please confirm your account <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>."); 

                if (_userManager.Options.SignIn.RequireConfirmedAccount)
                {
                    return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
                }
                else
                {
                    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();
    }
}

Solution

  • Let Sendgrid Dynamic templates do the magic for you. From your SendgridEmailSender , pass the generated callbackURL as a string parameter.Then

    To pass variables to the dynamic template on sendgrid

    var msg = new SendGridMessage()
        {
            From = new SendGrid.Helpers.Mail.EmailAddress("[email protected]", "Webmaster"),
            Subject = subject,
            PlainTextContent = message,
            HtmlContent = message,
            //this line passes key-value pairs to sendgrid
            new Personalization{ TemplateData=new {callbackURL=callbackURL} }
    
        };
    

    From Sendgrid Console edit your Dynamic Template Html to have this line

    Please confirm your account <a href="{{callbackUrl}}">clicking here</a>.");
    

    this will transfer your generated callbackURL to your dynamic template in sendgrid in a clean manner