0

I want to implement toastr.js in dotvvm. I created an exception filter which catch all the exception errors on the viewmodel:

public class ErrorLoggingActionFilter : ExceptionFilterAttribute
    {

        protected override async Task OnPageExceptionAsync(IDotvvmRequestContext context, Exception exception)
        {

            //context.CustomResponseProperties.Add("Error", exception.Message);

            // Inject JavaScript code to display toastr notification
            // Get the exception message
            string errorMessage = exception.Message;
            // Inject JavaScript code to display toastr notification
            string toastrScript = $"<script>toastr.error('{errorMessage}');</script>";
            context.ResourceManager.AddInlineScript(toastrScript);

            // Mark the exception as handled
            context.IsPageExceptionHandled = true;

            await base.OnPageExceptionAsync(context, exception);
        }


    }

but i'm having hard time adding the toastr.js and implementing it.

edoms06
  • 89
  • 2
  • 11

1 Answers1

1

You should use OnCommandExceptionAsync instead of OnPageExceptionAsync.

  • The "command exception" is an exception that occurs during a command or a static command - the page is already loaded in the browser, the user has done something, and it produced an error. In such situation, it makes sense to display a toastr notification on the page the user is currently seeing.
  • The "page exception" means there was a problem loading or rendering the page. If such exception occurs, the toastr wouldn't help the user anyway because the page cannot be loaded at all.

Additionally, the AddInlineScript requires just the JS code without the <script></script> tags:

public class ErrorLoggingActionFilter : ExceptionFilterAttribute
{
    protected override Task OnCommandExceptionAsync(IDotvvmRequestContext context, ActionInfo actionInfo, Exception ex)
    {
        string errorMessage = KnockoutHelper.MakeStringLiteral(ex.Message);
        string toastrScript = $"toastr.error('{errorMessage}');";
        context.ResourceManager.AddInlineScript(toastrScript);

        context.IsCommandExceptionHandled = true;

        return base.OnCommandExceptionAsync(context, actionInfo, ex);
    }   
}

Also, be careful when building scripts using the string concatenation. If the error message contains apostrophe, it would end the string literal and interpret the rest as a script code. If an attacker finds a way how to inject their input into the error message, you would make a script injection vulnerability in your app.

I've added KnockoutHelper.MakeStringLiteral to escape characters in the error message.

Finally, don't forget to register all script and style dependencies in DotvvmStartup:

config.Resources.RegisterScriptFile("jquery", "wwwroot/jquery/jquery.min.js");
config.Resources.RegisterScriptFile("toastr", "wwwroot/toastr.js/toastr.min.js", dependencies: new [] { "jquery", "toastr-css" });
config.Resources.RegisterStylesheetFile("toastr-css", "wwwroot/toastr.js/toastr.min.css");
Tomáš Herceg
  • 1,595
  • 1
  • 13
  • 18
  • Hi @Tomáš Herceg, I tried this one however it doesn't hit the OnCommandExceptionAsync whenever i'm having exception errors in my code. also added this config.Runtime.GlobalFilters.Add(new ErrorLoggingActionFilter()); at my DotvvmStartup.cs. Do I still need to add ErrorLoggingActionFilter in my ViewModel class as an attribute? – edoms06 Jun 14 '23 at 07:09
  • If it's in GlobalFilters, you shouldn't need it. Does the exception occur when a method from a button is called? This will not catch the errors which occur in Init, Load, or PreRender phases. – Tomáš Herceg Jun 16 '23 at 05:57
  • error occurs in PreRender method. can you advise how can I also catch that? – edoms06 Jun 17 '23 at 06:03
  • Hi Tomáš Herceg, do you have any updates? – edoms06 Jun 25 '23 at 23:32
  • As I mentioned previously, if the exception occurs in `Init`, `Load` or `PreRender` phase, the page initialization is not complete, thus DotVVM won't render any HTML. You can override the `OnPageExceptionAsync` and produce your own HTML response, or redirect the user to some error page: `context.RedirectToRoute("Error", query: new { message = exception.Message });` – Tomáš Herceg Jun 26 '23 at 06:54
  • Hi Tomáš Herceg, I'm having issue with the toastr "Cannot read properties of undefined (reading 'extend')" Issue was related to not loading the jquery. registered it in dotvvmstartup config.Resources.Register("jquery", new ScriptResource { Location = new UrlResourceLocation("~/administrator/js/jquery-1.7.2.min.js") }); config.Resources.RegisterStylesheetFile("toastr-css", "~/Scripts/DotVVM/toastr.min.css"); config.Resources.RegisterScriptFile("toastr", "~/Scripts/DotVVM/toastr.min.js", dependencies: new[] { "jquery", "toastr-css" }); – edoms06 Jul 06 '23 at 00:11
  • I don't understand - is jquery loaded at the page? If not, you may need to explicitly request it using `` or by code `context.ResourceManager.RegisterRequiredResource(...)`. If jquery is included in the page, maybe the script is executed too early, and jquery is not initialized yet. If DotVVM scripts are in the page, you can wrap your script with `dotvvm.events.initCompleted.subscribe(function () { ... });` – Tomáš Herceg Jul 08 '23 at 11:17