Javascript 使用MVC4和实体框架在Toastr或类似方法中显示错误和异常

Javascript 使用MVC4和实体框架在Toastr或类似方法中显示错误和异常,javascript,asp.net-mvc-4,logging,error-handling,toastr,Javascript,Asp.net Mvc 4,Logging,Error Handling,Toastr,我正试图找到一种方法,当我的应用程序中出现异常或错误时,使用Toastr向用户显示错误。我遇到的问题似乎表明,不可能在控制器中发生异常,或者在当前视图中使用Toastr显示数据访问层 我想知道你们中是否有人遇到过这种情况,你们的解决方案是什么 我试图做到的是,任何时候出现未处理的异常,或者有人手动处理异常,我们都能够在不中断工作流的情况下向用户显示错误。Toastr是向我建议的,但由于完全是javascript,我不确定在MVC4应用程序中实现它的最佳方式 <script type="te

我正试图找到一种方法,当我的应用程序中出现异常或错误时,使用Toastr向用户显示错误。我遇到的问题似乎表明,不可能在控制器中发生异常,或者在当前视图中使用Toastr显示数据访问层

我想知道你们中是否有人遇到过这种情况,你们的解决方案是什么

我试图做到的是,任何时候出现未处理的异常,或者有人手动处理异常,我们都能够在不中断工作流的情况下向用户显示错误。Toastr是向我建议的,但由于完全是javascript,我不确定在MVC4应用程序中实现它的最佳方式

<script type="text/javascript">

        // Setup message triggers and display all messages for this page
        $(document).ready(function () {
            var tempMessages = '@Html.Raw(TempData[ToastrProperties.MessagesKey])';

            if (!tempMessages) {
                tempMessages = '[]';
            }

            var viewMessages = '@Html.Raw(Response.Headers[ToastrProperties.MessagesKey])';

            if (!viewMessages) {
                viewMessages = '[]';
            }

            var allMessages = $.parseJSON(tempMessages).concat($.parseJSON(viewMessages));

            handleAjaxMessages();

            displayMessages(allMessages);
        });

        // Display all messages that are listed within the Header of the call.
        // These messages are all stored in a serialized XML string that is then Decoded by the RenderMessages method
            function displayMessages(messages) {
                    $.each(messages, function (idx, msg) {
                            toastr[msg.type](msg.message, msg.title, {
                                    fadeIn: msg.fadeIn,
                                    fadeOut: msg.fadeOut,
                                    timeOut: msg.timeOut,
                                    positionClass: msg.positionClass,
                                    onclick: function() {
                                            var wnd = $("#AppMessageWindow").data("kendoWindow");
                                            wnd.content(msg.message).center().open();
                }
                            });
                    });
            }

        // Add methods for events that are both ajaxSuccess, and ajaxError
        function handleAjaxMessages() {
            $(document).ajaxSuccess(function (event, request) {
                checkAndHandleMessageFromHeader(request);
            }).ajaxError(function (event, request) {
                checkAndHandleMessageFromHeader(request);
            });
        }

        // Get messages from the Response header of the request, and display them as
        // a message using Toastr
        function checkAndHandleMessageFromHeader(request) {
            // pull the messages from the Response Header
            var msgs = request.getResponseHeader('@ToastrProperties.MessagesKey');

            if (!msgs) {
                msgs = '[]'
            }

            var allMessages = $.parseJSON(msgs)

            displayMessages(allMessages);
        }

    </script>
我正在探索的一个选项是设置我的默认索引控制器来处理传入的错误字符串,这样我可以从Global.asax.cs中的Application_error方法重定向到它,以便提供友好的重定向,然后如果传入的字符串不为null,那么我可以在索引视图上使用toastr。但是,这并不理想,因为它需要重定向,并且会中断工作流。此外,它不允许我在不抛出异常或在javascript中执行所有错误处理的情况下显示错误


其他重要信息是,我们正在使用Telerik Kendo用户界面和Razor语法,如果这对我有任何帮助的话。

对于那些与我在这里遇到的问题相同的人,解决方案是:

我在这里找到了解决方案的第一步:

他实施了自己的通知形式。但我希望能够使用Toastr,或者任何其他类型的通知选项

注意:只要看到以“Res”结尾的类,它就是一个资源文件。这是为了使应用程序中的字符串更有条理。这样就没有人会被卷入其中。

下面是我如何实现我的解决方案注意:这也适用于MVC5

首先要做的是在源代码中创建Toastr对象。这将用于最终在UI中将消息弹出给用户

public class Toast
    {
        public string type { get; set; }
        public string message { get; set; }
        public string title { get; set; }
        public string positionClass { get; set; }
        public int fadeIn { get; set; }
        public int fadeOut { get; set; }
        public int timeOut { get; set; }
        public int extendedTimeOut { get; set; }
        public bool debug { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <param name="message"></param>
        /// <param name="dtype"></param>
        public Toast(MessageType type, string message, DisplayType dtype = DisplayType.TopRight)
        {
            this.type = type.ToString();
            this.message = message;
            this.DType = dtype;
            this.fadeIn = 300;
            this.fadeOut = 1000;
            this.timeOut = 5000;
            this.extendedTimeOut = 1000;
            this.debug = false;
        }

        /// <summary>
        /// 
        /// </summary>
        public DisplayType DType
        { 
            set 
            { 
                this.positionClass = GetPositionClass(value); 
            } 
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dtype"></param>
        /// <returns></returns>
        private string GetPositionClass(DisplayType dtype)
        {
            string position = string.Empty;

            switch (dtype)
            {
                case DisplayType.TopLeft:
                    position = ToastrProperties.TopLeft;
                    break;
                case DisplayType.TopFull:
                    position = ToastrProperties.TopFull;
                    break;
                case DisplayType.BottomRight:
                    position = ToastrProperties.BottomRight;
                    break;
                case DisplayType.BottomLeft:
                    position = ToastrProperties.BottomLeft;
                    break;
                case DisplayType.BottomFull:
                    position = ToastrProperties.BottomFull;
                    break;
                case DisplayType.TopRight:
                default:
                    position = ToastrProperties.TopRight;
                    break;
            };

            return position;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="json"></param>
        /// <returns></returns>
        public static List<Toast> DeserializeAll(string json)
        {
            return Newtonsoft.Json.JsonConvert.DeserializeObject<List<Toast>>(json);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="allToast"></param>
        /// <returns></returns>
        public static string SerializeAll(List<Toast> allToast)
        {
            return Newtonsoft.Json.JsonConvert.SerializeObject(allToast);
        }
    }

创建Toastr类后,必须重写控制器的OnException方法。如果您使用的是ApiController,那么还有另一种方法,我也将展示

此外,您还需要创建一个ToastrProperties类,如下所示

public static class ToastrProperties // TODO: Add in the descriptions for each of these properties
{
    /// <summary>
    /// 
    /// </summary>
    public const string MessagesKey = "messages";

    /// <summary>
    /// 
    /// </summary>
    public const string BottomFull = "toast-bottom-full-width";

    /// <summary>
    /// 
    /// </summary>
    public const string BottomLeft = "toast-bottom-left";

    /// <summary>
    /// 
    /// </summary>
    public const string BottomRight = "toast-bottom-right";

    /// <summary>
    /// 
    /// </summary>
    public const string TopFull = "toast-top-full-width";

    /// <summary>
    /// 
    /// </summary>
    public const string TopLeft = "toast-top-left";

    /// <summary>
    /// 
    /// </summary>
    public const string TopRight = "toast-top-right";

    /// <summary>
    /// 
    /// </summary>
}
这将设置WebApi控制器

下一步

在添加了其中一种/两种方法之后,您需要做一些其他事情

在我们开始之前,让您知道我们在这两种方法中所做的实际上是处理错误,并在系统中记录错误,这一点很重要。然后,我们使用Toast对象静态方法将JSON序列化并反序列化到请求的响应/temp头中,这样它就可以作为JSON传递回客户机,并且可以在异步或回发页面请求时由浏览器处理。但我们马上就要开始了

因为我不希望它只用于向客户端传递异常消息,所以我还为BaseController和ApiController方法设置了扩展,以便它们可以调用“ShowMessage”方法并将Toastr方法发送到客户端

以下是扩展的基本控制器版本:

public static class ControllerExtensions
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="toast"></param>
        /// <param name="showAfterRedirect"></param>
        public static void ShowMessage(this Controller controller, Toast toast, bool showAfterRedirect = false)
        {
            try
            {
                if (toast != null)
                {
                    List<Toast> allToast = new List<Toast>();

                    // Pull the existing messages from the Temp, or Response 
                    // based on the redirect option, and assign it to a string variable
                    string messagesJson = showAfterRedirect ?
                        controller.TempData[ToastrProperties.MessagesKey].ToString()
                        : controller.Response.Headers[ToastrProperties.MessagesKey];

                    // Deserialize the JSON into the toast list
                    if (!string.IsNullOrEmpty(messagesJson))
                    {
                        try
                        {
                            allToast = Toast.DeserializeAll(messagesJson as string);
                        }
                        catch { } // Do nothing here
                    }

                    // Add a new Toast to the list
                    allToast.Add(toast);

                    // Serialize the List
                    string SerializedString = Toast.SerializeAll(allToast);

                    if (!string.IsNullOrEmpty(SerializedString))
                    {
                        if (showAfterRedirect)
                        {
                            controller.TempData[ToastrProperties.MessagesKey] = SerializedString;
                        }
                        else
                        {
                            controller.Response.Headers[ToastrProperties.MessagesKey] = SerializedString;
                        }
                    }
                }
            }
            catch (Exception excep)
            {
                P3Log.Error(new Exception(ToastrRes.ShowMessageException, excep));
            }
        }
    }
公共静态类控制器扩展
{
/// 
/// 
/// 
/// 
/// 
/// 
公共静态void ShowMessage(此控制器控制器,Toast Toast,bool showAfterRedirect=false)
{
尝试
{
if(toast!=null)
{
List allToast=new List();
//从临时或响应中提取现有消息
//基于重定向选项,并将其分配给字符串变量
字符串messagesJson=showAfterRedirect?
controller.TempData[ToastrProperties.MessagesKey].ToString()
:controller.Response.Headers[ToastrProperties.MessagesKey];
//将JSON反序列化到toast列表中
如果(!string.IsNullOrEmpty(messagesJson))
{
尝试
{
allToast=Toast.deserializeral(messagesJson作为字符串);
}
catch{}//在这里什么也不做
}
//在列表中添加新的祝酒词
所有吐司。添加(吐司);
//序列化列表
string SerializedString=Toast.SerializeAll(allToast);
如果(!string.IsNullOrEmpty(SerializedString))
{
如果(showAfterRedirect)
{
controller.TempData[ToastrProperties.MessagesKey]=SerializedString;
}
其他的
{
controller.Response.Headers[ToastrProperties.MessagesKey]=SerializedString;
}
}
}
}
捕获(例外例外)
{
P3Log.Error(新异常(ToastrRes.ShowMessageException,excep));
}
}
}
以下是同一扩展的Web Api版本:

public static class ApiControllerExtensions
    {
        /// <summary>
        /// Show a message to the user Using Toastr
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="messageType"></param>
        /// <param name="message"></param>
        public static void ShowMessage(this ApiController controller, Toast ToastMessage)
        {
            try
            {
                string message = string.Empty;

                List<Toast> Messages = new List<Toast>();

                var header = controller.Request.Headers.FirstOrDefault(x => x.Key.Equals(ToastrProperties.MessagesKey));

                if (header.Value != null && header.Value.Any())
                {
                    string hString = header.Value.FirstOrDefault();

                    if (!string.IsNullOrEmpty(hString))
                    {
                        try
                        {
                            Messages = Toast.DeserializeAll(hString);
                        }
                        catch {} // Do nothing here
                    }
                }

                // Add the message to the existing messages in the
                // header
                Messages.Add(ToastMessage);

                message = Toast.SerializeAll(Messages);

                if (!string.IsNullOrEmpty(message))
                {
                    // Remove the old header, and put the new one in
                    controller.Request.Headers.Remove(ToastrProperties.MessagesKey);

                    controller.Request.Headers.Add(ToastrProperties.MessagesKey, message);
                }
            }
            catch (Exception excep)
            {
                // Log here with NLog
                P3Log.Error(new Exception(ToastrRes.ShowMessageException, excep));
            }
        }
    }
public静态类ApiControllerExtensions
{
/// 
///使用Toastr向用户显示消息
/// 
/// 
/// 
/// 
公共静态void ShowMessage(此ApiController控制器,Toast ToastMessage)
{
尝试
{
斯特林
    public class P3ApiExceptionFilterAttribute : ExceptionFilterAttribute // TODO: Add information to the summaries
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="Context"></param>
        public override void OnException(HttpActionExecutedContext Context)
        {
            try
            {
                List<Toast> Toasts = new List<Toast>();

                // Create a response and add a header for the Message to be displayed using the ajaxError event
                Context.Response = Context.Request.CreateResponse();

                // Log the error that occurred here
                P3Log.Error(Context.Exception);

                // Go through all of the Headers that match our messages key. There should only ever be
                // one, but since the Web API stuff handles this differently I want to cover our bases
                foreach (var header in Context.Request.Headers.Where(x => x.Key.Equals(ToastrProperties.MessagesKey)))
                {
                    // Check the header to see if it's null, and if it's not, and there are values for
                    // the header, add them to the Toasts list so that they will be re-added to the error
                    // response header, and actually be received by the client
                    if (header.Value != null)
                    {
                        foreach (string str in header.Value)
                        {
                            if (!string.IsNullOrEmpty(str))
                            {
                                try
                                {
                                    Toasts.AddRange(Toast.DeserializeAll(str));
                                }
                                catch { } // Do nothing here
                            }
                        }
                    }

                }

                // Add the Exception Toast
                Toasts.Add(new Toast(MessageType.error, GlobalRes.ApplicationError, DisplayType.TopRight));

                // Add the header for the response so that the messages will be displayed
                // once the response gets back to the client
                if (Toasts != null && Toasts.Any())
                {
                    string Messages = Toast.SerializeAll(Toasts);

                    if (!string.IsNullOrEmpty(Messages))
                    {
                        // Adding a single Response Header
                        Context.Response.Headers.Add(ToastrProperties.MessagesKey, Messages);
                    }
                }
            }
            catch (Exception excep)
            {
                P3Log.Error(ToastrRes.ApiToastrException, excep);
            }

            base.OnException(Context);
        }
    }
            // Add the exception handler for the API controllers
            config.Filters.Add(new P3ApiExceptionFilterAttribute());
public static class ControllerExtensions
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="toast"></param>
        /// <param name="showAfterRedirect"></param>
        public static void ShowMessage(this Controller controller, Toast toast, bool showAfterRedirect = false)
        {
            try
            {
                if (toast != null)
                {
                    List<Toast> allToast = new List<Toast>();

                    // Pull the existing messages from the Temp, or Response 
                    // based on the redirect option, and assign it to a string variable
                    string messagesJson = showAfterRedirect ?
                        controller.TempData[ToastrProperties.MessagesKey].ToString()
                        : controller.Response.Headers[ToastrProperties.MessagesKey];

                    // Deserialize the JSON into the toast list
                    if (!string.IsNullOrEmpty(messagesJson))
                    {
                        try
                        {
                            allToast = Toast.DeserializeAll(messagesJson as string);
                        }
                        catch { } // Do nothing here
                    }

                    // Add a new Toast to the list
                    allToast.Add(toast);

                    // Serialize the List
                    string SerializedString = Toast.SerializeAll(allToast);

                    if (!string.IsNullOrEmpty(SerializedString))
                    {
                        if (showAfterRedirect)
                        {
                            controller.TempData[ToastrProperties.MessagesKey] = SerializedString;
                        }
                        else
                        {
                            controller.Response.Headers[ToastrProperties.MessagesKey] = SerializedString;
                        }
                    }
                }
            }
            catch (Exception excep)
            {
                P3Log.Error(new Exception(ToastrRes.ShowMessageException, excep));
            }
        }
    }
public static class ApiControllerExtensions
    {
        /// <summary>
        /// Show a message to the user Using Toastr
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="messageType"></param>
        /// <param name="message"></param>
        public static void ShowMessage(this ApiController controller, Toast ToastMessage)
        {
            try
            {
                string message = string.Empty;

                List<Toast> Messages = new List<Toast>();

                var header = controller.Request.Headers.FirstOrDefault(x => x.Key.Equals(ToastrProperties.MessagesKey));

                if (header.Value != null && header.Value.Any())
                {
                    string hString = header.Value.FirstOrDefault();

                    if (!string.IsNullOrEmpty(hString))
                    {
                        try
                        {
                            Messages = Toast.DeserializeAll(hString);
                        }
                        catch {} // Do nothing here
                    }
                }

                // Add the message to the existing messages in the
                // header
                Messages.Add(ToastMessage);

                message = Toast.SerializeAll(Messages);

                if (!string.IsNullOrEmpty(message))
                {
                    // Remove the old header, and put the new one in
                    controller.Request.Headers.Remove(ToastrProperties.MessagesKey);

                    controller.Request.Headers.Add(ToastrProperties.MessagesKey, message);
                }
            }
            catch (Exception excep)
            {
                // Log here with NLog
                P3Log.Error(new Exception(ToastrRes.ShowMessageException, excep));
            }
        }
    }
<script type="text/javascript">

        // Setup message triggers and display all messages for this page
        $(document).ready(function () {
            var tempMessages = '@Html.Raw(TempData[ToastrProperties.MessagesKey])';

            if (!tempMessages) {
                tempMessages = '[]';
            }

            var viewMessages = '@Html.Raw(Response.Headers[ToastrProperties.MessagesKey])';

            if (!viewMessages) {
                viewMessages = '[]';
            }

            var allMessages = $.parseJSON(tempMessages).concat($.parseJSON(viewMessages));

            handleAjaxMessages();

            displayMessages(allMessages);
        });

        // Display all messages that are listed within the Header of the call.
        // These messages are all stored in a serialized XML string that is then Decoded by the RenderMessages method
            function displayMessages(messages) {
                    $.each(messages, function (idx, msg) {
                            toastr[msg.type](msg.message, msg.title, {
                                    fadeIn: msg.fadeIn,
                                    fadeOut: msg.fadeOut,
                                    timeOut: msg.timeOut,
                                    positionClass: msg.positionClass,
                                    onclick: function() {
                                            var wnd = $("#AppMessageWindow").data("kendoWindow");
                                            wnd.content(msg.message).center().open();
                }
                            });
                    });
            }

        // Add methods for events that are both ajaxSuccess, and ajaxError
        function handleAjaxMessages() {
            $(document).ajaxSuccess(function (event, request) {
                checkAndHandleMessageFromHeader(request);
            }).ajaxError(function (event, request) {
                checkAndHandleMessageFromHeader(request);
            });
        }

        // Get messages from the Response header of the request, and display them as
        // a message using Toastr
        function checkAndHandleMessageFromHeader(request) {
            // pull the messages from the Response Header
            var msgs = request.getResponseHeader('@ToastrProperties.MessagesKey');

            if (!msgs) {
                msgs = '[]'
            }

            var allMessages = $.parseJSON(msgs)

            displayMessages(allMessages);
        }

    </script>