MVC Contact Form with Email

26,937

Solution 1

Sending an email will take time. It should be a thread. Put your code in a function. And make the following changes:

public void SendEmail(string toAddress, string fromAddress, 
                      string subject, string message)
{
    try
    {
        using (var mail = new MailMessage())
        {
            const string email = "[email protected]";
            const string password = "password!";

            var loginInfo = new NetworkCredential(email, password);


            mail.From = new MailAddress(fromAddress);
            mail.To.Add(new MailAddress(toAddress));
            mail.Subject = subject;
            mail.Body = message;
            mail.IsBodyHtml = true;

            try
            {
                using (var smtpClient = new SmtpClient(
                                                 "smtp.mail.yahoo.com", 465))
                {
                    smtpClient.EnableSsl = true;
                    smtpClient.UseDefaultCredentials = false;
                    smtpClient.Credentials = loginInfo;
                    smtpClient.Send(mail);
                }

            }

            finally
            {
                //dispose the client
                mail.Dispose();
            }

        }
    }
    catch (SmtpFailedRecipientsException ex)
    {
        foreach (SmtpFailedRecipientException t in ex.InnerExceptions)
        {
            var status = t.StatusCode;
            if (status == SmtpStatusCode.MailboxBusy ||
                status == SmtpStatusCode.MailboxUnavailable)
            {
                Response.Write("Delivery failed - retrying in 5 seconds.");
                System.Threading.Thread.Sleep(5000);
                //resend
                //smtpClient.Send(message);
            }
            else
            {
                Response.Write("Failed to deliver message to {0}",
                                  t.FailedRecipient);
            }
        }
    }
    catch (SmtpException Se)
    {
        // handle exception here
        Response.Write(Se.ToString());
    }

    catch (Exception ex)
    {
        Response.Write(ex.ToString());
    }

}

Call that function in your controller:

[HttpPost]
public ActionResult Contact(MailModels e)
{
    if (ModelState.IsValid)
    {

        //prepare email
        var toAddress = "[email protected]";
        var fromAddress = e.Email.ToString();
        var subject = "Test enquiry from "+ e.Name;
        var message = new StringBuilder();
        message.Append("Name: " + e.Name + "\n");
        message.Append("Email: " + e.Email + "\n");
        message.Append("Telephone: " + e.Telephone + "\n\n");
        message.Append(e.Message);

        //start email Thread
        var tEmail = new Thread(() => 
       SendEmail(toAddress, fromAddress, subject, message));
        tEmail.Start();
    }
    return View();
}

If you dont get email, check your spam folder

Solution 2

settings to web.config

<system.net>
  <mailSettings>
    <smtp from="[email protected]">
      <network host="smtp-mail.outlook.com" 
               port="587" 
               userName="[email protected]"
               password="password" 
               enableSsl="true" />
    </smtp>
  </mailSettings>
</system.net>

Port 465 or 587?

Lots of code samples for Gmail feature port 465 but most people cannot get this to work. When they revert to port 587, their email suddenly works. According to Gmail's documentation SSL is required if you specify port 465. Many people think that setting EnableSsl to true achieves this, but in fact, it means that your app must be running under https. When you set EnableSsl to true, you actually switch TLS on, which is required for port 587. Https is not supported by the SmtpClient object. For more details, read the Remarks section of the docs on MSDN.

Solution 3

You need to implement Producer Consumer pattern for this use case. You will have to have one thread running dedicated to emails. This thread will read from the queue & send emails. In the contact method, you will just add to the queue. Its not a good idea to do time consuming operations in controller methods.

C# producer/consumer

Share:
26,937
iggyweb
Author by

iggyweb

Updated on July 09, 2022

Comments

  • iggyweb
    iggyweb almost 2 years

    I wonder if someone can please help with a MVC Contact Form which send an Email on submission? I think I have most elements setup, but for some reason the form appear to be sending (takes ages) then just returns back to the form and no email is received.

    MailModels.cs:

    namespace WebApplication1.Models
    {
        public class MailModels
        {
           public string Name { get; set; }
           public string Email { get; set; }
           public string Telephone { get; set; }
           public string Message { get; set; }
        }
    }
    

    Contact.cshtml:

    @using (Html.BeginForm("Contact", "Home", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @id = "contact-form", role = "form" }))
    {
        @Html.ValidationSummary()
        <fieldset>
            <div class="form-div-1">
                <label class="name">
                    @Html.TextBoxFor(m => m.Name, new { @placeholder = "Name *", @type = "text" })
                </label>
            </div>
            <div class="form-div-2">
                <label class="email">
                    @Html.TextBoxFor(m => m.Email, new { @placeholder = "Email Address *", @type = "email" })
                </label>
            </div>
            <div class="form-div-3">
                <label class="phone notRequired">
                    @Html.TextBoxFor(m => m.Telephone, new { @placeholder = "Telephone Number", @type = "text" })
                </label>
            </div>
            <div>
                <label class="message">
                    @Html.TextAreaFor(m => m.Message, new { @placeholder = "Message *" })
                </label>
            </div>
            <div class="button-wrapper">
                <input type="submit" value="Send" name="submit" class="button"> <input type="reset" value="Reset" name="MFReset" class="button"><span>* Required Fields</span>
            </div>
        </fieldset>
    }
    

    HomeController.cs:

    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Entity;
    using System.Linq;
    using System.Net.Mail;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Mvc.Html;
    using WebApplication1.Models;
    using System.Text;
    
    namespace WebApplication1.Controllers
    {
        public class HomeController : Controller
        {
        public ActionResult Contact()
        {
            ViewBag.Message = "Test Form";
    
            return View();
        }
    
        [HttpPost]
        public ActionResult Contact(MailModels e)
        {
            if (ModelState.IsValid)
            {
    
                StringBuilder message = new StringBuilder();
                MailAddress from = new MailAddress(e.Email.ToString());
                message.Append("Name: " + e.Name + "\n");
                message.Append("Email: " + e.Email + "\n");
                message.Append("Telephone: " + e.Telephone + "\n\n");
                message.Append(e.Message);
    
                MailMessage mail = new MailMessage();
    
                SmtpClient smtp = new SmtpClient();
    
                smtp.Host = "smtp.mail.yahoo.com";
                smtp.Port = 465;
    
                System.Net.NetworkCredential credentials = new System.Net.NetworkCredential("yahooaccount", "yahoopassword");
    
                smtp.Credentials = credentials;
                smtp.EnableSsl = true;
    
                mail.From = from;
                mail.To.Add("yahooemailaddress");
                mail.Subject = "Test enquiry from "+e.Name;
                mail.Body = message.ToString();
    
                smtp.Send(mail);
            }
            return View();
        }
    

    Banging my head against a brickwall with this one, any help would be much appreciated :-)

  • iggyweb
    iggyweb almost 10 years
    Many thanks, I've just amended the code as you have provided, the words Thread, fromAddress, subject and message are underlined in red in Visual Studio 2013, have I missed something?
  • meda
    meda almost 10 years
    @iggyweb sorry I add typo, they are just parameter of the function. As of thread you might need to import the namespace
  • iggyweb
    iggyweb almost 10 years
    I'm very new to this, can you please elabourate on import namespace? I've already declared namespace WebApplication1.Controllers but still words Thread, fromAddress, subject and message are underlined I cant see a typo.
  • iggyweb
    iggyweb almost 10 years
    Thank you, Thread is ok now but the rest of the line (() => SendEmail(toAddress, string fromAddress, subject, message)); is now underlined, sorry to be a pain and thank you for helping.
  • meda
    meda almost 10 years
    @iggyweb yes that was a typo, remove string SendEmail(toAddress, fromAddress, subject, message)
  • iggyweb
    iggyweb almost 10 years
  • jp2code
    jp2code over 8 years
    Awesome bit of work. Too bad you gave all that help to @iggyweb, and he never gave you an upvote or accepted this as the answer. I gave you a +1.
  • iggyweb
    iggyweb over 8 years
    Thanks for the prompt jp2code, apologies Meda and thanks again.
  • JustJohn
    JustJohn over 8 years
    Hey, I stumbled on this and got it all installed with no red or blue underlines once I read the chat discussion. Thank you iggweb and especially meda. Learned a whole lot about building a class.
  • dave317
    dave317 almost 8 years
    I like this post, if I am redirectig to a "Success" view on completion, how do I handle the response portions in the exceptions?
  • Petros Apotsos
    Petros Apotsos over 6 years
    Even better, use a ViewModel or DTO to pass the data to the function. This way, you can better Unit Test and persist messages to a db if need arises. Also, I would put "prepare email" code to a different function. Finally, a question: Doesn;t garbage collector automatically dispose the smtp client?