18 de septiembre de 2017

Enviar correo MVC C# SMTP

Ejemplos envian correos vía SMTP (Simple Mail Transfer Protocol) utilizando las clases MailMessage, SmtpClient y el namespace System.Net.Mail.

Las instancias de la clase MailMessage se utilizan para construir mensajes de correo electrónico que se transmiten a un servidor SMTP  utilizando la clase SmtpClient, más especificamente en la clase MailMessage se definirán datos correspondientes al cuerpo y direcciones origen - destino(s) de correo electronico y en la clase SmtpClient se definiran datos del servidor SMTP, siendo su propiedad más importatnte SmtpClient.send(MailMessage).

Como una primera aproximación se muestra un muy sencillo ejemplo donde el desarrollador define explicitamente los valores de los parametros de las instancias de MailMessage y SmtpClient en el método EnviarMensaje()
using System.Net.Mail;
using System.Net.Mime;

protected void BtnEnviar_Click(object sender, EventArgs e)
 {
  EnviarCorreo();
 }

private void EnviarCorreo() {

MailMessage email = new MailMessage();
SmtpClient smtp = new SmtpClient();

 email.To.Add(new MailAddress("correo@destino.com"));
 email.From = new MailAddress("correo@origen.com");
 email.Subject = "Notificación ( " + DateTime.Now.ToString("dd / MMM / yyy hh:mm:ss") + " ) ";
 email.SubjectEncoding = System.Text.Encoding.UTF8;
 email.Body = "Tu mensaje | tu firma";
 email.IsBodyHtml = true;
 email.Priority = MailPriority.Normal;
 FileStream fs = new FileStream("E:\\TestFolder\\test.pdf", FileMode.Open, FileAccess.Read);
 Attachment a = new Attachment(fs, "test.pdf", MediaTypeNames.Application.Octet);
 email.Attachments.Add(a);

 smtp.Host = "192.XXX.X.XXX";  // IP empresa/institucional
 //smtp.Host = "smtp.hotmail.com";
 //smtp.Host = "smtp.gmail.com";
 smtp.Port = 25;
 smtp.Timeout = 50;
 smtp.EnableSsl = false;
 smtp.UseDefaultCredentials = false;
 smtp.Credentials = new NetworkCredential("correo@origen.com", "password");

 string lista= "ejemplo1@correo.com; ejemplo2@correo2.com;";
 string output = string.empty;

 var mails = lista.Text.Split(';');
 foreach (string dir in mails)
          email.To.Add(dir);
       
 try
    {
     smtp.Send(email);
     email.Dispose();
     output = "Correo electrónico fue enviado satisfactoriamente.";
    }
 catch (SmtpException exm)
         {
          throw exm.Message.ToString();
         }
 catch (Exception ex)
    {
     output = "Error enviando correo electrónico: " + ex.Message;
    }

 MessageBox.Show(output);
 }

Ahora algo mas funcional, se permitirá al usuario especificar la información básica del correo que desea enviar y adjuntar un archivo a este, para esto se necesitan los siguientes namespaces y se creará una clase (modelo), tener en cuenta que puede necesitar referenciarse la dll System.Web.Abstractions a tu proyecto en caso utilices una version de .NET < 4.0, en versiones más recientes esta dll esta deprecated y basta con agregar system.web
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.IO;

public class CorreoModel
{
  [Required, Display(Name = "Correo Destinatario"), EmailAddress]    
  public string Para { get; set; }
  [Required]
  public string Asunto { get; set; }
  public string Cuerpo { get; set; }
  public HttpPostedFileBase Adjunto { get; set; }
  [Required]
  [Required, Display(Name = "Correo Remitente"), EmailAddress]
  public string Correo { get; set; }
  [Required]
  [DataType(DataType.Password)]
  public string Pass { get; set; }
}
Para enviar un archivo al servidor vía POST en el formulario se debe cambiar el valor implicito de encoding type = application/x-www-form-urlencoded, por el valor encoding type= multipart/form-data, además el formulario debe contar con un control tipo file input para anexar el archivo, este es un ejemplo para la vista MVC.
@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
 

EJEMPLO ENVIO CORREO MVC


@Html.AntiForgeryToken() @Html.ValidationSummary(true)
@Html.LabelFor(model => model.Para, new { @class = "col-md-2 control-label" })
@Html.TextBoxFor(model => model.Para, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Para)
@Html.LabelFor(model => model.Asunto, new { @class = "col-md-2 control-label" })
@Html.TextBoxFor(model => model.Asunto, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Asunto)
@Html.LabelFor(model => model.Cuerpo, new { @class = "col-md-2 control-label", rows = "3", cols = "20" })
@Html.TextBoxFor(model => model.Cuerpo, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Cuerpo)
@Html.LabelFor(model => model.Adjunto, new { type = "file", @class = "col-md-2 control-label" })
@Html.TextBoxFor(model => model.Adjunto, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Adjunto)
@Html.LabelFor(model => model.Correo, new { @class = "col-md-2 control-label" })
@Html.TextBoxFor(model => model.Correo, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Correo)
@Html.LabelFor(model => model.Pass, new { @class = "col-md-2 control-label", type = "password" })
@Html.TextBoxFor(model => model.Pass, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Pass)
}
el controlador que recibe la información del usuario y enviará el correo es
using System.Web.Mvc
using System.IO;
using System.Net;
using System.Net.Mail;

public class HomeController : Controller
{
 [HttpGet] 
 public ActionResult Index()
 {
  return View();
 }
 
 [HttpPost]
 [ValidateAntiForgeryToken]
 public ActionResult Index(CorreoModel model)
 {
  if (ModelState.IsValid)
  { 
   using (MailMessage mm = new MailMessage(model.Correo, model.Para))
    {
      mm.Subject = model.Asunto;
      mm.Body = model.Cuerpo;
        if (model.Adjunto.ContentLength > 0 && model.Adjunto!= null)
           {
            string fileName = Path.GetFileName(model.Adjunto.FileName);
            mm.Attachments.Add(new Attachment(model.Adjunto.InputStream, fileName));
           }

      mm.IsBodyHtml = false;
       using (SmtpClient smtp = new SmtpClient())
          {
           smtp.Host = "smtp.gmail.com";
           smtp.EnableSsl = true;
           NetworkCredential NetworkCred = new NetworkCredential(model.Correo, model.Pass);
           smtp.UseDefaultCredentials = true;
           smtp.Credentials = NetworkCred;
           smtp.Port = 587;
           smtp.Send(mm);

           mm.Dispose();
           smtp.Dispose();
           return RedirectToAction("Sent");
          }
    }
  }
  return View();
 }
}
para agregar al codigo anterior, hay ocasiones en que el adjuntar un archivo es muy poco común, asi que los desarrolladores optan por no especificarlo en el modelo sino utilizar la clase HttpPostedFileBase, asi pues, lel controlador recibe de la vista una instancia de la clase HttpPostedFileBase para que luego el controlador valide si la instancia tiene algún valor o está vacío.
[HttpPost]  
public ActionResult Index( MyMailModel objModelMail, HttpPostedFileBase fileUploader)  
 {  
  if (ModelState.IsValid)  
   {  
    string from = "xyz@gmail.com"; //any valid GMail ID  
    using (MailMessage mail = new MailMessage(from, objModelMail.To))  
    {  
     mail.Subject = objModelMail.Subject;  
     mail.Body = objModelMail.Body;  
      if (fileUploader != null)  
       {  
        string fileName = Path.GetFileName(fileUploader.FileName);  
        mail.Attachments.Add(new Attachment(fileUploader.InputStream, fileName));  
       }  
     mail.IsBodyHtml = false;  
      SmtpClient smtp = new SmtpClient();  
      smtp.Host = "smtp.gmail.com";  
      smtp.EnableSsl = true;  
      NetworkCredential networkCredential = new NetworkCredential(from, "Gmail Id Password");  
      smtp.UseDefaultCredentials = true;  
      smtp.Credentials = networkCredential;  
      smtp.Port = 587;  
      smtp.Host = "localhost";  
      smtp.Send(mail);  
      ViewBag.Message = "Sent";  
      return View("Index", objModelMail);  
       }  
      }  
     else  
    {  
     return View();  
    }  
 }
y en la vista se debe especificar un control tipo file, teniendo en consideración que el nombre de este debe coincidir con el recibido en el controlador
   
y el modelo a utilizar simplemente se le elimina la propiedad HttpPostedFileBase Adjunto
using System.ComponentModel.DataAnnotations;
using System.IO;

public class CorreoModel
{
  [Required]    
  public string Para { get; set; }
  [Required]
  public string Asunto { get; set; }
  public string Cuerpo { get; set; }
  [Required]
  public string Correo { get; set; }
  [Required]
  [DataType(DataType.Password)]
  public string Pass { get; set; }
}