RedirectToAction после обратного вызова в ASP.NET MVC - PullRequest
0 голосов
/ 15 февраля 2019

Я хочу перенаправить к действию после выполнения некоторых задач (отправки по электронной почте), но я не могу понять, как это сделать правильно.

Вот мой код, но RedirectToAction aren 'здесь ничего не делать!

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

Ответы [ 3 ]

0 голосов
/ 15 февраля 2019

Не используйте обратный вызов.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 
                   }); 
    };
}
0 голосов
/ 15 февраля 2019

ПОПРОБУЙТЕ ЭТО => вызывайте свою функцию следующим образом

if(processSendingEmail(email, (result)))
{
    return RedirectToAction("ContactResult", "Contact", new { success = result });
}
else
{ 
    return null;
}
0 голосов
/ 15 февраля 2019

Использовать базу задач 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...