Не используйте обратный вызов.RedirectToAction создает ActionResult, который должен быть возвращен действием, он не вызывает перенаправление.
Правильный способ сделать что-то асинхронно - использовать async / await.Даже если ваша электронная библиотека не имеет асинхронных методов на основе задач, вы можете адаптировать ее к модели на основе задач с помощью TaskCompletionSource.Это было бы довольно необычно, поскольку большинство библиотек перешли от более старых асинхронных моделей, таких как обратные вызовы, события и APM, к задачам.
MailMessage
предлагает использовать SmtpClient .Метод SendMailAsync основан на задачах, что означает, что вы можете написать
await client.SendMailAsync(email);
Например:
[HttpPost]
public async Task<ActionResult> SendEmail(EmailContentViewModel emailDetails)
{
SmptClient client = ... //Configure the client here
using (MailMessage email = new MailMessage(emailDetails.from, emailDetails.to))
{
email.Subject = emailDetails.subject;
email.Body = emailDetails.body;
email.Priority = emailDetails.MailPriority;
await client.SendMailAsync(email);
return RedirectToAction("ContactResult", "Contact", new { success = true });
};
}
SmptClient является устаревшим классом.Его страница документации предупреждает, что:
Мы не рекомендуем использовать класс SmtpClient для новых разработок.Для получения дополнительной информации см. SmtpClient не должен использоваться на GitHub .
Эта ссылка объясняет, что:
SmtpClient не поддерживает многие современные протоколы.Это только для общения.Он отлично подходит для электронных писем от инструментов, но не соответствует современным требованиям протокола.
Рекомендуется использовать более новые библиотеки, такие как MailKit
MailKit позволяет явное приведение MailMessage
к MimeMessage
, что упрощает преобразование существующего кода в MailKit:
[HttpPost]
public async Task<ActionResult> SendEmail(EmailContentViewModel emailDetails)
{
var client = new MailKit.Net.Smtp.SmptClient();
/Configure the client here ...
using (MailMessage email = new MailMessage(emailDetails.from, emailDetails.to))
{
email.Subject = emailDetails.subject;
email.Body = emailDetails.body;
email.Priority = emailDetails.MailPriority;
var msg=(MailKit)email;
await client.SendAsync(msg);
return RedirectToAction("ContactResult", "Contact", new { success = true });
};
}
Обработка ошибок
И методы MailKit, и старые методы SmptClient Send либо успешно выполняются, либо генерируются.Один из вариантов - просто скрыть исключение и вернуть флаг успеха true / false:
try
{
await client.SendAsync(msg);
return RedirectToAction("ContactResult", "Contact", new { success = true});
}
catch
{
return RedirectToAction("ContactResult", "Contact", new { success = false});
}
Это не очень полезно, как для пользователя, так и для администратора, пытающегося диагностировать возможные проблемы.Документация методов объясняет типы исключений, которые могут возникнуть, например: ArgumentNullException
для пустого сообщения, InvalidOperationException, SmtpFailedRecipientException и многое другое.
Как минимум, код может регистрировать исключение перед возвратом ошибки:
catch(Exception ex)
{
_log.Error(ex);
return RedirectToAction("ContactResult", "Contact", new { success = false});
}
Лучшей идеей будет обработать определенные исключения и, возможно, предупредить пользователя:
catch(SmtpFailedRecipientException ex)
{
_log.Error(ex);
return RedirectToAction("ContactResult", "Contact", new { success = false,message=$"Couldn't send the message to {ex.FailedRecipient}"});
}
catch(SmtpException ex)
{
_log.Error(ex);
return RedirectToAction("ContactResult", "Contact", new { success = false,message="Failed to send the message"});
}
catch(Exception ex)
{
_log.Error(ex);
return RedirectToAction("ContactResult", "Contact", new { success = false,message="An unexpected error occured"});
}
Сопоставление с образцом в C # 7 делает это проще:
catch(Exception ex)
{
_log.Error(ex);
string message="";
switch (ex)
{
case SmtpFailedRecipientException f:
message=$"Failed to send to {f.FailedRecipient}";
break;
case SmptException s :
message="Protocol error";
break;
default:
message="Unexpected error";
break;
}
return RedirectToAction("ContactResult", "Contact", new { success = false,message=message});
}
Отдельный метод
Рефакторинг кода отправки в отдельный метод очень прост.Блок try / catch и объявление клиента могут быть извлечены в отдельный метод:
async Task<string> MySendMethod(MailMessage email)
{
var client = new MailKit.Net.Smtp.SmptClient();
//Configure the client here ...
try
{
var msg=(MailKit)email;
await client.SendAsync(msg);
return "";
}
catch(Exception ex)
{
_log.Error(ex);
switch (ex)
{
case SmtpFailedRecipientException f:
return $"Failed to send to {f.FailedRecipient}";
case SmptException s :
return "Protocol error";
default:
return "Unexpected error";
}
}
}
Вместо возврата RedirectToActionResult
, метод возвращает строку результата.Если он пуст, операция прошла успешно.Действие контроллера можно переписать так:
[HttpPost]
public async Task<ActionResult> SendEmail(EmailContentViewModel emailDetails)
{
using (MailMessage email = new MailMessage(emailDetails.from, emailDetails.to))
{
email.Subject = emailDetails.subject;
email.Body = emailDetails.body;
email.Priority = emailDetails.MailPriority;
var message=await MySendMethod(email);
return RedirectToAction("ContactResult", "Contact",
new { success = String.IsNullOrWhitespace(result),
message=message
});
};
}