简体   繁体   English

带Angularjs的MVC,ASP.NET身份,EmailService和异步调用

[英]MVC, ASP.NET Identity, EmailService and Async calls w/Angularjs

After reviewing and trying many of the suggestions surrounding the error message: 查看并尝试解决有关错误消息的许多建议后:

"An asynchronous module or handler completed while an asynchronous operation was still pending." “异步模块或处理程序在异步操作仍处于挂起状态时完成。”

I found myself in the situation where even though the call to the MVC accountController actually EXECUTED the desired code (an email was sent to the right place with the right content) and a Try/Catch in the controller method would not 'catch' the error, the AngularJS factory that was initiating the call would receive a server error "page". 我发现自己处于这样一种情况,即使对MVC accountController的调用实际上已执行了所需的代码(一封电子邮件已发送至具有正确内容的正确位置),并且在controller方法中使用Try / Catch不会“捕获”错误,发起调用的AngularJS工厂将收到服务器错误“页面”。

Factory:(AngularJS) 工厂:(AngularJS)

InitiateResetRequest: function (email) {
                    var deferredObject = $q.defer();

                    $http.post(
                        '/Account/InitiateResetPassword', { email: email }
                    )
                    .success(function (data) {
                            deferredObject.resolve(data);
                    })
                    .error(function (data) {
                        //This is a stop-gap solution that needs to be fixed..!
                        if (data.indexOf("An asynchronous module or handler completed while an asynchronous operation was still pending.") > 0) {
                            deferredObject.resolve(true);
                        } else {
                            deferredObject.resolve(false);
                        }
                    });
                    return deferredObject.promise;
                }

MVC Controller (C#): MVC控制器(C#):

        [HttpPost]
        [AllowAnonymous]
        public async Task<int> InitiateResetPassword(string email)
        {
            try
            {
                _identityRepository = new IdentityRepository(UserManager);
                string callbackUrl = Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, "/account/reset?id=");
                await _identityRepository.InitiatePasswordReset(email, callbackUrl);
                return 0;
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return 1;
            }
        }

Identity Repository/InitiatePasswordReset: 身份存储库/ InitiatePasswordReset:

 public async Task InitiatePasswordReset(string email, string callbackUrl)
        {
            try
            {
                var u = await _applicationUserManager.FindByEmailAsync(email);

                string passwordResetToken = await GetResetToken(u);
                callbackUrl = callbackUrl + HttpUtility.UrlEncode(passwordResetToken);
                await _applicationUserManager.SendEmailAsync(u.Id, RESET_SUBJECT, string.Format(RESET_BODY, u.FirstName, u.LastName, callbackUrl));
            }
            catch(Exception ex)
            {      //another vain attempt to catch the exception...
                Console.WriteLine(ex.ToString());
                throw ex;
            }
        }

The EmailService injected into the ASP.NET Identity "ApplicationUserManager" EmailService已注入ASP.NET标识“ ApplicationUserManager”中

   public class EmailService : IIdentityMessageService
    {
        XYZMailer xyzMailer;
        public EmailService()
        {
            xyzMailer = XYZMailer.getCMRMailer();
        }
        public async Task SendAsync(IdentityMessage message)
        {
            //original code as posted:
            //await Task.FromResult(xyzMailer.SendMailAsync(message));
            //solution from @sirrocco-
            await xyzMailer.SendMailAsync(message);
        }
    }

and finally...the XYZMailer class 最后是XYZMailer类

class XYZMailer
    {
        #region"Constants"
        private const string SMTP_SERVER = "XYZEXCHANGE.XYZ.local";
        private const string NO_REPLY = "noReply@XYZCorp.com";
        private const string USER_NAME = "noreply";
        private const string PASSWORD = "theMagicP@55word"; //NO, that is not really the password :) 
        private const int SMTP_PORT = 587;
        private const SmtpDeliveryMethod SMTP_DELIVERY_METHOD = SmtpDeliveryMethod.Network;
        #endregion//Constants

        internal XYZMailer()
        {
            //default c'tor
        }

        private static XYZMailer _XYZMailer = null;
        public static XYZMailer getXYZMailer()
        {
            if (_XYZMailer == null)
            {
                _XYZMailer = new XYZMailer();
            }
            return _XYZMailer;
        }

        public async Task<int> SendMailAsync(IdentityMessage message)
        {
#if DEBUG
                message.Body += "<br/><br/>DEBUG Send To: " + message.Destination;
                message.Destination = "me@XYZCorp.com";
#endif
            // Create the message:
            var mail =
                new MailMessage(NO_REPLY, message.Destination)
                {
                    Subject = message.Subject,
                    Body = message.Body,
                    IsBodyHtml = true
                };

            // Configure the client:
            using (SmtpClient client = new SmtpClient(SMTP_SERVER, SMTP_PORT)
            {
                DeliveryMethod = SMTP_DELIVERY_METHOD,
                UseDefaultCredentials = false,
                Credentials = new System.Net.NetworkCredential(USER_NAME, PASSWORD),
                EnableSsl = true
            })
            {
                // Send:
                await client.SendMailAsync(mail);

            }
            return 0;
        }

    }

(note: originally the controller method was simply "public async Task InitiateResetPassword, I added the return type as an attempt to trap the error on the server. At runtime, return 0; does hit (breakpoint) the catch does not get hit and at the client") (注意:最初,控制器方法只是“公共异步任务InitiateResetPassword,我添加了返回类型,以尝试在服务器上捕获错误。在运行时,返回0;不会命中(断点),catch不会命中,并且在客户端”)

At the moment I am simply filtering for the expected error message and telling javascript to treat it as a success. 目前,我只是在过滤预期的错误消息,然后告诉javascript将其视为成功。 This solution has the benefit of 'actually working'... but it is not 'ideal'. 该解决方案具有“实际工作”的优势……但不是“理想的”。

How do I prevent the error on the server? 如何防止服务器上的错误? or alternately, How do I catch the error on the server? 或者, 如何在服务器上捕获错误?

You need to remove await Task.FromResult from EmailService because that makes it so the code executes synchronously instead of async. 您需要从EmailService删除await Task.FromResult ,因为这样做会导致代码同步执行,而不是异步执行。

As to why the the exception was still raised and bubbled up outside the try/catch - I suspect the Task.FromResult was the culprit here too - if you now raise an exception in SendAsync (just to test it) you should catch in the controller. 至于为什么仍然在try / catch之外引发异常并冒出气泡-我怀疑Task.FromResult也是罪魁祸首-如果现在在SendAsync引发异常(只是为了对其进行测试),则应在控制器中捕获。

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

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