简体   繁体   English

在 ASP.NET MVC 中回调后的 RedirectToAction

[英]RedirectToAction after callback in ASP.NET MVC

I want to redirect to action after doing some tasks (sending an email), but I can't figure-out how to do that correctly.我想在完成一些任务(发送电子邮件)后重定向到操作,但我不知道如何正确地做到这一点。

Here's my code, but the RedirectToAction aren't doing anything here!这是我的代码,但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);
        }
    }

Based on Panagiotis Kanavos's response , here is a working code:基于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;
        }
    }

Don't use a callback.不要使用回调。 RedirectToAction creates an ActionResult that should be returned by the action, it doesn't force a redirect. RedirectToAction 创建一个应该由操作返回的 ActionResult,它不会强制重定向。

The proper way to do something asynchronously is to use async/await.异步执行某事的正确方法是使用 async/await。 Even if your email library doesn't have task-based asynchronous methods you can adapt it to the task-based model using a TaskCompletionSource.即使您的电子邮件库没有基于任务的异步方法,您也可以使用 TaskCompletionSource 使其适应基于任务的模型。 That would be rather unusual though as most libraries have moved from older async models like callbacks, events and APM to tasks.尽管大多数库已经从诸如回调、事件和 APM 等较旧的异步模型转移到了任务,但这将是相当不寻常的。

MailMessage suggests you're using SmtpClient . MailMessage建议您使用SmtpClient The SendMailAsync method is task-based, which means you can write SendMailAsync方法是基于任务的,这意味着您可以编写

await client.SendMailAsync(email);

For example :例如 :

[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 is an obsolete class though. SmptClient 是一个过时的类。 Its documentation page warns that :它的文档页面警告说:

We don't recommend that you use the SmtpClient class for new development.我们不建议您将 SmtpClient 类用于新开发。 For more information, see SmtpClient shouldn't be used on GitHub .有关更多信息,请参阅GitHub 上不应使用 SmtpClient 。

That link explains that :该链接解释说:

SmtpClient doesn't support many modern protocols. SmtpClient 不支持许多现代协议。 It is compat-only.它是仅兼容的。 It's great for one off emails from tools, but doesn't scale to modern requirements of the protocol.它非常适合来自工具的一次性电子邮件,但不能满足协议的现代要求。

The recommendation is to use newer libraries like MailKit建议使用较新的库,例如MailKit

MailKit allows explicit casting of a MailMessage to a MimeMessage which makes it easy to convert the existing code to 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 }); 
    };
}

Error Handling错误处理

Both MailKit's and the old SmptClient's Send methods either succeed or throw. MailKit 和旧的 SmptClient 的 Send 方法要么成功,要么抛出。 One option would be to just hide the exception and return a true/false success flag :一种选择是隐藏异常并返回真/假成功标志:

try
{
    await client.SendAsync(msg);
    return RedirectToAction("ContactResult", "Contact", new { success = true});
}
catch
{
    return RedirectToAction("ContactResult", "Contact", new { success = false});
}

That's not very helpful though, either for the user or the administrator trying to diagnose possible problems.但是,这对于试图诊断可能出现的问题的用户或管理员来说并不是很有帮助。 The methods' documentation explains the types of exceptions that may occur, eg: ArgumentNullException for a null message, an InvalidOperationException, SmtpFailedRecipientException and more.这些方法的文档解释了可能发生的异常类型,例如:空消息的ArgumentNullException 、InvalidOperationException、SmtpFailedRecipientException 等。

At the very least, the code can log the exception before returning failure :至少,代码可以在返回失败之前记录异常:

catch(Exception ex)
{
    _log.Error(ex);
    return RedirectToAction("ContactResult", "Contact", new { success = false});
}

A better idea would be to handle specific exceptions and possibly warn the user :一个更好的主意是处理特定的异常并可能警告用户:

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

Pattern matching in C# 7 makes this easier : 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});
}

Separate method分离法

Refactoring the sending code to a separate method is easy.将发送代码重构为单独的方法很容易。 The try/catch block and the client declaration can be extracted to a separate method : 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";
        }
    }
}

Instead of returning a RedirectToActionResult , the method returns a result string.该方法不返回RedirectToActionResult ,而是返回结果字符串。 If that is empty, the operation succeeded.如果为空,则操作成功。 The controller action can be rewritten like this:控制器动作可以像这样重写:

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

Use Task base ActionResult intead of callback使用基于任务的 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