繁体   English   中英

在 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);
        }
    }

基于Panagiotis Kanavos 的回应,这里是一个工作代码:

    [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;
        }
    }

不要使用回调。 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 类用于新开发。 有关更多信息,请参阅GitHub 上不应使用 SmtpClient 。

该链接解释说:

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 方法要么成功,要么抛出。 一种选择是隐藏异常并返回真/假成功标志:

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 
                   }); 
    };
}

使用基于任务的 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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM