ASP.NET MVC 回调后的 RedirectToAction
RedirectToAction after callback in ASP.NET MVC
我想在完成一些任务(发送电子邮件)后重定向到操作,但我不知道如何正确地做到这一点。
这是我的代码,但是 RedirectToAction
在这里什么也没做!
[HttpPost]
public 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;
processSendingEmail(email, (result) =>
{
RedirectToAction("ContactResult", "Contact", new { success = result }); //It's not redirecting to the ContactResult page!
});
}
return null;
}
private void processSendingEmail(MailMessage email, Action<bool> callback= null)
{
using (SmtpClient smtpClient = new SmtpClient(_smtpHostName, _smtpPort))
{
bool sentSuccessfully = false;
try
{
//.............//
}
catch(Exception e)
{
//.............//
}
callback?.Invoke(sentSuccessfully);
}
}
基于,这是一个工作代码:
[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 sentSuccessfully= await processSendingEmail(email);
return RedirectToAction("ContactResult", "Contact", new { success = sentSuccessfully});
}
}
private async Task<bool> processSendingEmail(MailMessage email)
{
var client = new MailKit.Net.Smtp.SmtpClient();
//Configure the client here ...
try
{
var msg = (MimeKit.MimeMessage)email;
await client.SendAsync(msg);
return true;
}
catch (Exception ex)
{
Debug.Fail(ex.Message);
string errorMessage = "";
switch (ex)
{
case SmtpFailedRecipientException f:
errorMessage = $"Failed to send to {f.FailedRecipient}";
break;
case SmtpException s:
errorMessage = "Protocol error";
break;
default:
errorMessage = "Unexpected error";
break;
}
//Do anything you want with the error message
return false;
}
}
使用基于任务的 ActionResult 而不是回调
[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 result = await processSendingEmail(email);
return RedirectToAction("ContactResult", "Contact", new { success = result });
}
}
async Task<bool> processSendingEmail(System.Net.Mail.MailMessage email) {
await Task.Delay(1000); //email code here...
return true;
}
不要使用回调。 RedirectToAction 创建一个应由操作 return 编辑的 ActionResult,它不会强制重定向。
异步执行某些操作的正确方法是使用 async/await。即使您的电子邮件库没有基于任务的异步方法,您也可以使用 TaskCompletionSource 使其适应基于任务的模型。这将是相当不寻常的,因为大多数库已经从旧的异步模型(如回调、事件和 APM)转移到任务。
MailMessage
建议你使用 SmtpClient. The 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 已过时 class。它的文档页面警告说:
We don't recommend that you use the SmtpClient class for new development. For more information, see SmtpClient shouldn't be used on GitHub.
link 解释说:
SmtpClient doesn't support many modern protocols. It is compat-only. It's great for one off emails from tools, but doesn't scale to modern requirements of the protocol.
建议使用较新的库,例如 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 方法要么成功,要么抛出异常。一种选择是隐藏异常和 return 一个 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 等等。
至少,代码可以在 returning 失败之前记录异常:
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";
}
}
}
而不是 returning RedirectToActionResult
,方法 returns 一个结果字符串。如果为空,则操作成功。控制器动作可以这样重写:
[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
});
};
}
我想在完成一些任务(发送电子邮件)后重定向到操作,但我不知道如何正确地做到这一点。
这是我的代码,但是 RedirectToAction
在这里什么也没做!
[HttpPost]
public 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;
processSendingEmail(email, (result) =>
{
RedirectToAction("ContactResult", "Contact", new { success = result }); //It's not redirecting to the ContactResult page!
});
}
return null;
}
private void processSendingEmail(MailMessage email, Action<bool> callback= null)
{
using (SmtpClient smtpClient = new SmtpClient(_smtpHostName, _smtpPort))
{
bool sentSuccessfully = false;
try
{
//.............//
}
catch(Exception e)
{
//.............//
}
callback?.Invoke(sentSuccessfully);
}
}
基于
[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 sentSuccessfully= await processSendingEmail(email);
return RedirectToAction("ContactResult", "Contact", new { success = sentSuccessfully});
}
}
private async Task<bool> processSendingEmail(MailMessage email)
{
var client = new MailKit.Net.Smtp.SmtpClient();
//Configure the client here ...
try
{
var msg = (MimeKit.MimeMessage)email;
await client.SendAsync(msg);
return true;
}
catch (Exception ex)
{
Debug.Fail(ex.Message);
string errorMessage = "";
switch (ex)
{
case SmtpFailedRecipientException f:
errorMessage = $"Failed to send to {f.FailedRecipient}";
break;
case SmtpException s:
errorMessage = "Protocol error";
break;
default:
errorMessage = "Unexpected error";
break;
}
//Do anything you want with the error message
return false;
}
}
使用基于任务的 ActionResult 而不是回调
[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 result = await processSendingEmail(email);
return RedirectToAction("ContactResult", "Contact", new { success = result });
}
}
async Task<bool> processSendingEmail(System.Net.Mail.MailMessage email) {
await Task.Delay(1000); //email code here...
return true;
}
不要使用回调。 RedirectToAction 创建一个应由操作 return 编辑的 ActionResult,它不会强制重定向。
异步执行某些操作的正确方法是使用 async/await。即使您的电子邮件库没有基于任务的异步方法,您也可以使用 TaskCompletionSource 使其适应基于任务的模型。这将是相当不寻常的,因为大多数库已经从旧的异步模型(如回调、事件和 APM)转移到任务。
MailMessage
建议你使用 SmtpClient. The 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 已过时 class。它的文档页面警告说:
We don't recommend that you use the SmtpClient class for new development. For more information, see SmtpClient shouldn't be used on GitHub.
link 解释说:
SmtpClient doesn't support many modern protocols. It is compat-only. It's great for one off emails from tools, but doesn't scale to modern requirements of the protocol.
建议使用较新的库,例如 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 方法要么成功,要么抛出异常。一种选择是隐藏异常和 return 一个 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 等等。
至少,代码可以在 returning 失败之前记录异常:
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";
}
}
}
而不是 returning RedirectToActionResult
,方法 returns 一个结果字符串。如果为空,则操作成功。控制器动作可以这样重写:
[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
});
};
}