8

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
  • 2,373
  • 12
  • 47
  • 77

4 Answers4

15

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 = "username@yahoo.com";
            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 = "someadress@yahoo.co.uk";
        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

meda
  • 45,103
  • 14
  • 92
  • 122
  • 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? – iggyweb May 06 '14 at 19:26
  • @iggyweb sorry I add typo, they are just parameter of the function. As of thread you might need to import the namespace – meda May 06 '14 at 19:34
  • 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 May 06 '14 at 19:40
  • 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. – iggyweb May 06 '14 at 19:44
  • @iggyweb yes that was a typo, remove string `SendEmail(toAddress, fromAddress, subject, message)` – meda May 06 '14 at 19:45
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/52134/discussion-between-iggyweb-and-meda) – iggyweb May 06 '14 at 19:45
  • 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. –  Dec 05 '15 at 03:20
  • Thanks for the prompt jp2code, apologies Meda and thanks again. – iggyweb Dec 14 '15 at 09:09
  • 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. – JustJohn Dec 15 '15 at 07:32
  • I like this post, if I am redirectig to a "Success" view on completion, how do I handle the response portions in the exceptions? – dave317 Jul 19 '16 at 17:40
  • 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? – Petros Apotsos Nov 27 '17 at 12:49
1

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

Community
  • 1
  • 1
MD Luffy
  • 536
  • 6
  • 18
1

settings to web.config

<system.net>
  <mailSettings>
    <smtp from="you@outlook.com">
      <network host="smtp-mail.outlook.com" 
               port="587" 
               userName="you@outlook.com"
               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.

Majid joghataey
  • 1,488
  • 2
  • 17
  • 28
0
public void SendEmail(string toAddress, string fromAddress,string subject, string message)
{
    try
    {
        using (var mail = new MailMessage())
        {
            const string email = "username@yahoo.com";
            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 chor gai )
    {
        foreach (SmtpFailedRecipientException t in ex.InnerExceptions)
        {
            var status = t.StatusCode;
            if (status == SmtpStatusCode.MailboxBusye ||
                status == SmtpStatusCode.MailboxUnavailableee)
            {
                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.Toread());
    }
}
DerStarkeBaer
  • 669
  • 8
  • 28
yasir
  • 1