MVC Contact Form with Email
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.
iggyweb
Updated on July 09, 2022Comments
-
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 almost 10 yearsMany 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 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 almost 10 yearsI'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 almost 10 yearsThank 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 almost 10 years@iggyweb yes that was a typo, remove string
SendEmail(toAddress, fromAddress, subject, message)
-
iggyweb almost 10 years
-
jp2code over 8 yearsAwesome 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 over 8 yearsThanks for the prompt jp2code, apologies Meda and thanks again.
-
JustJohn over 8 yearsHey, 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 almost 8 yearsI 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 over 6 yearsEven 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?