Search code examples
asp.net-mvcasp.net-mvc-5postal

Value cannot be null. Parameter name: httpContext


I encountered this error message "Value cannot be null. Parameter name: httpContext" when I tried to send email by using Postal.mvc5.

This error happened when tried to call Postal.EmailService.CreateMailMessage(Email email).

Anyone one can help on this? Thanks.

The following are the error details.

at System.Web.HttpContextWrapper..ctor(HttpContext httpContext)
at System.Web.Optimization.Styles.get_Context()
at System.Web.Optimization.Styles.get_Manager()
at System.Web.Optimization.Styles.RenderFormat(String tagFormat, String[] paths)
at System.Web.Optimization.Styles.Render(String[] paths)
at ASP._Page_Views_Shared__Layout_cshtml.Execute() in c:\XXX\Views\Shared\_Layout.cshtml:line 19
at System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
at System.Web.Mvc.WebViewPage.ExecutePageHierarchy()
at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer)
at System.Web.WebPages.WebPageBase.<>c__DisplayClass3.<RenderPageCore>b__2(TextWriter writer)
at System.Web.WebPages.HelperResult.WriteTo(TextWriter writer)
at System.Web.WebPages.WebPageExecutingBase.WriteTo(TextWriter writer, HelperResult content)
at System.Web.WebPages.WebPageBase.Write(HelperResult result)
at System.Web.WebPages.WebPageBase.RenderSurrounding(String partialViewName, Action`1 body)
at System.Web.WebPages.WebPageBase.PopContext()
at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
at System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance)
at System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer)
at Postal.EmailViewRenderer.RenderView(IView view, ViewDataDictionary viewData, ControllerContext controllerContext, ImageEmbedder imageEmbedder)
at Postal.EmailViewRenderer.Render(Email email, String viewName)
at Postal.EmailService.CreateMailMessage(Email email)

ForgotPassword trigger send email via Email Helper:

public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
    {
        if (ModelState.IsValid)
        {
            var user = await UserManager.FindByNameAsync(model.Email);
            if (user == null)
            {
                // Don't reveal that the user does not exist or is not confirmed
                return View("ForgotPasswordConfirmation");
            }

            // Check if email confirm required
            if (CacheHelper.Settings.EmailConfirmedRequired && !(await UserManager.IsEmailConfirmedAsync(user.Id)))
            {
                return View("ForgotPasswordConfirmation");
            }                

            string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
            var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);

            var emailTemplateQuery = await _emailTemplateService.Query(x => x.Slug.ToLower() == "forgotpassword").SelectAsync();
            var emailTemplate = emailTemplateQuery.Single();

            dynamic email = new Postal.Email("Email");
            email.To = CacheHelper.Settings.EmailContact;
            email.From = CacheHelper.Settings.EmailContact;
            email.Subject = emailTemplate.Subject;
            email.Body = emailTemplate.Body;
            email.CallbackUrl = callbackUrl;
            EmailHelper.SendEmail(email);

            return RedirectToAction("ForgotPasswordConfirmation", "Account");
        }

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

Email Helper class: Error encountered in EmailService.CreateMailMessage(email);

        public static IEmailService EmailService = Postal.Email.CreateEmailService();

    public static void SendEmail(Email email, bool preMailer = true)
    {

        Task.Factory.StartNew(() =>
        {
            try
            {
                //skip email if there is no settings
                if (string.IsNullOrEmpty(CacheHelper.Settings.SmtpHost) && string.IsNullOrEmpty(CacheHelper.Settings.SmtpPassword))
                    return;

                //Error encountered when call create mail message
                var message = EmailService.CreateMailMessage(email);

                using (var smtpClient = new SmtpClient())
                {
                    smtpClient.UseDefaultCredentials = false;

                    // set credential if there is one
                    if (!string.IsNullOrEmpty(CacheHelper.Settings.SmtpUserName) && !string.IsNullOrEmpty(CacheHelper.Settings.SmtpPassword))
                    {
                        var credential = new NetworkCredential
                        {
                            UserName = CacheHelper.Settings.SmtpUserName,
                            Password = CacheHelper.Settings.SmtpPassword
                        };
                        smtpClient.Credentials = credential;
                    }
                    smtpClient.Host = CacheHelper.Settings.SmtpHost;
                    smtpClient.EnableSsl = CacheHelper.Settings.SmtpSSL;

                    if (CacheHelper.Settings.SmtpPort.HasValue)
                        smtpClient.Port = CacheHelper.Settings.SmtpPort.Value;

                    //moving CSS to inline style attributes, to gain maximum E-mail client compatibility.
                    if (preMailer)
                        message.Body = PreMailer.Net.PreMailer.MoveCssInline(message.Body).Html;

                    smtpClient.Send(message);
                }
            }
            catch (Exception ex)
            {
                Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
            }
        });
    }

Solution

  • I had some issues when trying to send email using SmtpClient. In most cases you cannot get it to work unless you have your own e-mail server (e.g. only works in production) since sending is only allowed on the host/server to prevent spam. The context exception can be related to the problem mentioned above. I think you should try something similar to this (simplified):

    public static bool Email(string subject, string fromEmail, string toEmail, string fromName, string toName)
    {
       bool result = false;
       bool useDefaultCredentials = true;
    
       var credentials = new NetworkCredential
       {
          UserName = CacheHelper.Settings.SmtpUserName,
          Password = CacheHelper.Settings.SmtpPassword
       };
    
       if (!string.IsNullOrEmpty(CacheHelper.Settings.SmtpUserName) && !string.IsNullOrEmpty(CacheHelper.Settings.SmtpPassword))
       {
          useDefaultCredentials = false;
       }
    
       SmtpClient smtp = new SmtpClient()
       {
          Host = CacheHelper.Settings.SmtpHost,
          Port = CacheHelper.Settings.SmtpPort.HasValue ? CacheHelper.Settings.SmtpPort.Value : 25,
          EnableSsl = CacheHelper.Settings.SmtpSSL,
          DeliveryMethod = SmtpDeliveryMethod.Network,
          UseDefaultCredentials = useDefaultCredentials,
          Credentials = useDefaultCredentials ? CredentialCache.DefaultNetworkCredentials : credentials,
          Timeout = 10000
       };
    
       MailAddress from = new MailAddress(fromEmail, fromName);
       MailAddress to = new MailAddress(toEmail, toName);
       string body = PreMailer.Net.PreMailer.MoveCssInline(message.Body).Html;
    
       using (MailMessage message = new MailMessage(from, to) { Subject = subject, IsBodyHtml = true, Body = body })
       {
          smtp.Send(message); //you might need to use try catch here (it can blow up)
          result = true;
       }
       smtp.Dispose();
    
       return result;
    }