在Toastr中显示错误和异常,或使用MVC4和实体框架的类似方法 [英] Displaying Errors and Exceptions in Toastr, or similar method using MVC4, and Entity Framework

查看:274
本文介绍了在Toastr中显示错误和异常,或使用MVC4和实体框架的类似方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Toastr向用户显示错误,因为我的应用程序中出现了异常或错误。我遇到的问题似乎表明,使用Toastr在当前视图中显示的Controller或Data Access层中发生异常是不可能的。



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



我想要完成的是,任何时候都有一个未处理的异常,或有人手动处理异常,我们可以在不中断工作流的情况下向用户显示错误。 Toastr被建议给我,但完全是javascript我不知道在我的MVC4应用程序中实现它的最佳方法。



我正在探索的一个选项是设置我的默认索引控制器来处理传入的错误字符串,以便我可以从Global.asax中的Application_Error方法重定向到.cs为了给一个友好的重定向,然后如果这个传入的字符串不为null,那么我可以在索引视图上使用toastr。然而,这不是理想的,因为它需要重定向并破坏工作流程。此外,我不会允许我显示错误,而不必抛出异常或者在javascript中执行我所有的错误处理。



其他重要信息是我们正在使用Telerik Kendo UI和Razor Syntax,如果这将以任何方式帮助我。

解决方案

对于那些有同样问题的人我在这里是解决方案:



我在这里找到了我的解决方案的第一步: https://github.com/martijnboland/MvcNotification



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



注意:任何地方你看到一个类在Res结尾是一个资源文件。这是为了使我们的字符串在我们的应用程序更有条理。这样没有人会混淆。



这是我如何实现我的解决方案。 注意:这与MVC5一起使用



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

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

///< summary>
///
///< / summary>
///< param name =type>< / param>
///< param name =message>< / param>
///< param name =dtype>< / param>
public Toast(MessageType类型,字符串消息,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;
};

返回位置;
}

///< 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显示创建的两个特殊枚举位置和消息窗口类型,以便它们可以是动态的。

  public enum MessageType 
{
success,
info,
warning,
错误,
};

  public enum DisplayType 
{
TopRight,
TopLeft,
TopFull,
BottomRight,
BottomLeft,
BottomFull,
};

创建Toastr类后,必须覆盖Controller的OnException方法。还有另外一种方法,如果你正在使用一个ApiController我也会显示。



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

  public static class ToastrProperties // TODO:在每个属性的描述中添加
{
///< 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>
}

控制器示例:



我建议为控制器创建一个特殊的基类,以便它们都从它继承,并且它可以帮助你的应用程序的其他事情。这是我的基础控制器类。

  ///< summary> 
/// P3应用程序的基本控制器。不是
/// API控制器的所有控制器应该从这个
///< / summary>
public abstract class BaseController:Controller
{

// TODO:最好是通过构造函数注入新的
protected Services.P3KendoDataAccess Data = PortalServices.DataAccess;

///< summary>
///处理在标准MVC控制器内发生的任何和所有未处理的异常
///。这将使用NLog记录错误
///,然后使用Toastr
///显示错误,这将显示控制器内有问题
/// < /总结>
///< param name =filterContext>< / param>
protected override void OnException(ExceptionContext filterContext)
{
try
{
//记录原始错误,并将其标记为固定,以使消息不会显示给用户
// TODO:为错误分配GUID,并将其显示给用户,以便可以将其引用回异常
P3Log.Error(filterContext.Exception,System.Web .HttpContext.Current);
filterContext.ExceptionHandled = true;

((BaseController)filterContext.Controller).ShowMessage(new Toast(MessageType.error,filterContext.Exception.Message,DisplayType.TopRight),false);
}
catch(Exception excep)
{
P3Log.Error(新的异常(ToastrRes.BaseControllerException,excep));
}

return;
}

}

添加到您的项目只是将您的控制器设置为从该类派生而不是Controller,并且将设置此方法。



WebAPI控制器示例: / p>

这是一个更多的参与,因为你不能继承ApiController类,就像上面的例子。您必须创建一个应用于每个ApiController的异常过滤器属性。我会告诉你如何做到这一点,而不需要手动应用它,因为你会希望在每个控制器上最有可能。



首先你必须创建过滤器属性:

  public class P3ApiExceptionFilterAttribute:ExceptionFilterAttribute // TODO:将信息添加到摘要
{
///< summary>
///
///< / summary>
///< param name =上下文>< / param>
public override void OnException(HttpActionExecutedContext Context)
{
try
{
列表&Toast> Toast = new List< Toast>();

//创建一个响应,并使用ajaxError事件
添加要显示的消息头。Context.Response = Context.Request.CreateResponse();

//记录在这里发生的错误
P3Log.Error(Context.Exception);

//浏览与我们的邮件键匹配的所有标题。应该只有
//一个,但是由于Web API的东西处理不同,我想覆盖我们的基础
foreach(Context.Request.Headers.Where(x => x .Key.Equals(ToastrProperties.MessagesKey)))
{
//检查标题以查看它是否为空,如果不是,并且
//的标题有值,将它们添加到Toast列表中,以便将它们重新添加到错误
//响应头,实际上由客户端收到
if(header.Value!= null)
{
foreach(string str in header.Value)
{
if(!string.IsNullOrEmpty(str))
{
try
{
Toasts.AddRan GE(Toast.DeserializeAll(STR));
}
catch {} // do nothing here
}
}
}

}

/ /添加异常Toast
Toasts.Add(new Toast(MessageType.error,GlobalRes.ApplicationError,DisplayType.TopRight));

//添加响应的标题,以便消息将被显示
//一旦响应返回到客户端
if(Toast!= null&&& amp ; Toasts.Any())
{
string Messages = Toast.SerializeAll(Toasts);

if(!string.IsNullOrEmpty(Messages))
{
//添加单个响应头
Context.Response.Headers.Add(ToastrProperties.MessagesKey,消息);
}
}
}
catch(Exception excep)
{
P3Log.Error(ToastrRes.ApiToastrException,excep);
}

base.OnException(Context);
}
}

接下来,您需要将过滤器属性添加到您的Api控制器。最简单的方法是进入你的WebApiConfig.cs文件,并在Register方法的内部放置:

  //添加API控制器的异常处理程序
config.Filters.Add(new P3ApiExceptionFilterAttribute());

这将设置您的WebApi控制器。



NEXT步骤



添加任一/做一些其他的事情。首先,在我们介绍之前,尽管重要的是让您知道我们在这两种方法中所做的工作实际上是处理错误,并将其记录在系统中。然后我们使用Toast对象的静态方法来将JSON序列化和反序列化到请求的响应/临时头文件中,然后将其作为JSON传回客户端,并可以由浏览器在异步或后回页面请求。但是我们会在一秒钟内到达。



因为我不希望这仅用于将异常消息传递给客户端,我还为BaseController和ApiController方法设置扩展,以便他们可以调用一个ShowMessage方法,并将Toastr方法发送给客户端。



这是扩展程序的Base Controller版本:

  public静态类ControllerExtensions 
{
///< summary>
///
///< / summary>
///< param name =controller>< / param>
///< param name =toast>< / param>
///< param name =showAfterRedirect>< / param>
public static void ShowMessage(此控制器控件,Toast toast,bool showAfterRedirect = false)
{
try
{
if(toast!= null)
{
列表< Toast> allToast = new List< Toast>();

//根据重定向选项从Temp或Response
//中拉现有的消息,并将其分配给字符串变量
string messagesJson = showAfterRedirect?
controller.TempData [ToastrProperties.MessagesKey] .ToString()
:controller.Response.Headers [ToastrProperties.MessagesKey];

//将JSON序列化为吐司列表
if(!string.IsNullOrEmpty(messagesJson))
{
try
{
allToast = Toast.DeserializeAll(messagesJson as string);
}
catch {} // do nothing here
}

//添加一个新的Toast到列表
allToast.Add(toast);

//序列化列表
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 ));
}
}
}

这是Web Api版本相同的扩展名:

  public static class ApiControllerExtensions 
{
///< summary>
///向用户显示消息使用Toastr
///< / summary>
///< param name =controller>< / param>
///< param name =messageType>< / param>
///< param name =message>< / param>
public static void ShowMessage(此ApiController控件,Toast ToastMessage)
{
try
{
string message = string.Empty;

列表< Toast>消息=新列表< 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
}
}

//将消息添加到
中的现有消息// header
Messages.Add(ToastMessage);

message = Toast.SerializeAll(Messages);

if(!string.IsNullOrEmpty(message))
{
//删除旧标题,并将新的标题放在
controller.Request.Headers中。除去(ToastrProperties.MessagesKey);

controller.Request.Headers.Add(ToastrProperties.MessagesKey,message);
}
}
catch(Exception excep)
{
//使用NLog
登录这个P3Log.Error(新的异常(ToastrRes.ShowMessageException,excep ));
}
}
}

像您需要的任何标准扩展确保包含命名空间,否则将无法正常工作。



最终步骤



安装Toastr NUGET软件包或在线获取,并确保将其添加到软件包中,或者将您添加到视图中的方法。



现在,您需要将JavaScript添加到应用程序中的_Layout.cshtml。

 < script type =text / javascript> 

//设置消息触发并显示此页面的所有消息
$(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);
});

//显示呼叫头部中列出的所有消息。
//这些消息都存储在序列化的XML字符串中,然后由RenderMessages方法解码
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();
}
});
});
}

//为ajaxSuccess和ajaxError
的事件添加方法handleAjaxMessages(){
$(document).ajaxSuccess(function(event,请求){
checkAndHandleMessageFromHeader(request);
})。ajaxError(function(event,request){
checkAndHandleMessageFromHeader(request);
});
}

//从请求的响应头获取消息,并使用Toastr
函数将它们显示为
//一个消息checkAndHandleMessageFromHeader(request){
//从响应头获取消息
var msgs = request.getResponseHeader('@ ToastrProperties.MessagesKey');

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

var allMessages = $ .parseJSON(msgs)

displayMessages(allMessages);
}

< / script>

这需要一些解释。脚本中的第一个函数加载初始响应/ temp标头,因为在初始页面加载时,没有在页面中触发的标准请求。或至少我找不到一个允许访问标题的人。所以这些都放在使用剃须刀。



其余的应该很直接。它使用JSON来弹出一个toastr消息,并将事件添加到Ajax请求中,以便任何回到它的Toastr消息被正确处理。



我很确定我有一切都在这里。如果你有任何问题,或者当你尝试实现它时丢失的东西,请在这里或者PM我发表,我会更新我的帖子。我希望这可以帮助那些试图做同样事情的人。 :)



享受!


I'm trying to find a way to use Toastr to display errors to users as Exceptions or Errors occur within my application. The problems I'm running into seem to suggest that it would not be possible to have an exception that occurs in the Controller, or Data Access layer displayed in the current view using Toastr.

I'm wondering if any of you have run into this scenario and what your solution to it was?

What I'm trying to accomplish is that any time there is an unhandled exception, or someone handles an exception manually that we have the ability to display the error to the user without disrupting workflow. Toastr was suggested to me, but being completely javascript I'm not sure the best way to implement it within my MVC4 application.

One option I'm exploring is setting up my default index controller to handle an incoming error string so that I can redirect to it from the Application_Error method in the Global.asax.cs in order to give a friendly redirect, and then if that incoming string is not null then I can use toastr on the Index view. However this is not ideal because it requires a redirect, and disrupts workflow. Also it will not allow for me to display an error without having to thrown an exception or do all my error handling within the javascript.

Other important information is that we are using Telerik Kendo UI, and Razor Syntax if that would help me in any way.

解决方案

For those of you who have this same question that I had here is the solution:

I found the first step of my solution here: https://github.com/martijnboland/MvcNotification

He had implemented his own form of Notification. But I wanted to be able to use Toastr, or any other kind of Notification options that were out there.

NOTE: Anywhere you see a class that ends in "Res" it's a resource file. This is to keep our strings in our application more organized. That way nobody gets mixed up with that.

Here is how I implemented my solution. NOTE: This works with MVC5 as well

First thing to do is the create a Toastr object in your source code. This will be used to pop the message to the user in the UI eventually.

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

This uses two special enumerations I created for the Toastr display locations, and message window types so that they could be dynamic.

public enum MessageType
    {
        success,
        info,
        warning,
        error,
    };

And

public enum DisplayType
    {
        TopRight,
        TopLeft,
        TopFull,
        BottomRight,
        BottomLeft,
        BottomFull,
    };

Once you've created the Toastr class you have to override the OnException method of your Controller. There is another way this has to happen if you are using an ApiController which I will also show.

Also you will need to create a ToastrProperties class, seen below.

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

Controller Example:

I suggest creating a special base class for your controllers so that they all inherit from it, and it can help with other things later in your application. Here is my base controller class.

    /// <summary>
    /// The Base Controller for the P3 Application. All Controllers that are not 
    /// API Controllers should derive from this
    /// </summary>
    public abstract class BaseController : Controller
    {

        // TODO: Preferably, new up through injection through constructor
        protected Services.P3KendoDataAccess Data = PortalServices.DataAccess;

        /// <summary>
        /// Handles any and all unhandled exceptions that occur
        /// within a standard MVC controller. This will Log the Error
        /// using NLog, and then display an error to he user using Toastr
        /// which will show that there was a problem within the controller
        /// </summary>
        /// <param name="filterContext"></param>
        protected override void OnException(ExceptionContext filterContext)
        {
            try
            {
                // Log the original error, and mark it as fixed so that the message isn't displayed to the User
                // TODO: Assign a GUID to the error, and display that to the user so that it can be referenced back to the exception
                P3Log.Error(filterContext.Exception, System.Web.HttpContext.Current);
                filterContext.ExceptionHandled = true;

                ((BaseController)filterContext.Controller).ShowMessage(new Toast(MessageType.error, filterContext.Exception.Message, DisplayType.TopRight), false);
            }
            catch (Exception excep)
            {
                P3Log.Error(new Exception(ToastrRes.BaseControllerException, excep));
            }

            return;
        }

    }

After you've added this to your project just set your controllers to derive from this class instead of Controller, and that will set this method up.

WebAPI Controller Example:

This one is a little more involved because you can't just inherit from the ApiController class like in the above example. You have to create an Exception Filter Attribute that you would apply to each ApiController. I will show you how you can do it without manually applying it since you will want it on every controller anyways most likely.

First you have to create the Filter Attribute:

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

Next you need to add your Filter Attribute to all of your Api Controllers. The easiest way to do this is to go into your "WebApiConfig.cs" file, and inside of the Register method just put:

            // Add the exception handler for the API controllers
            config.Filters.Add(new P3ApiExceptionFilterAttribute());

This will setup your WebApi Controllers.

NEXT Step

After you've added either/both methods you need to do a few other things.

First before we go into that though it's important to let you know that what we are doing here in these two methods are actually handling the errors, and logging them within our system. Then we are using the Toast objects static methods to serialize and deserialize JSON into the response/ temp headers of the request so that it's then passed back to the client as JSON and can be handled by the browser upon both async, or post back page requests. But we will get to that in a second.

Because I didn't want this to only be used for passing exception messages to the client I also setup extensions for both the BaseController, and ApiController methods so that they could call a "ShowMessage" method and send Toastr methods down to the client.

Here is the Base Controller version of the Extension:

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

Here is the Web Api version of the same extension:

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

Like any standard extension you need to make sure to have the namespace included otherwise it won't work.

Final Step:

Install the Toastr NUGET Package, or get it online, and make sure that it's added to your bundles, or the method you are using to add scripts to your Views.

Now you need to add the Javascript to the _Layout.cshtml in your application.

<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>

This requires some explanation. The first function in the script loads the initial response / temp headers because on the initial page load there isn't a standard request that is triggered within the page. Or at least I couldn't find one that would allow access to the headers. So these are placed in using Razor.

The rest should be pretty straight forward. It uses the JSON to pop a toastr message, and adds events to the Ajax requests so that any Toastr messages that come back to it are handled properly.

I'm pretty sure I've got everything in here. If you have any questions, or something is missing when you try to implement it, post on here or PM me and I'll update my post. I hope this helps others who are attempting to do the same thing. :)

Enjoy!

这篇关于在Toastr中显示错误和异常,或使用MVC4和实体框架的类似方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆