3

Background:

I'm setting up error handling in an ASP.NET Core 2.2 MVC app. When in development environment, I use the app.UseDeveloperExceptionPage();, and in production - app.UseExceptionHandler("/Error/Index");. I am navigated to the correct error page during non-AJAX (regular form submission) requests based on the environment.

If an exception occurs in the server during an AJAX request, I want the app to display the correct error page depending on the environment.

I have already set up all of what I described above, as you can see in my code examples below.

Problem/Concern:

While this works (though still have to complete the TODO in InitializeGlobalAjaxEventHandlers function), I have some concerns.

With non-AJAX calls in MVC, it feels like there is a "official/correct" way to do it with app.UseDeveloperExceptionPage(); and app.UseExceptionHandler("/Error/Index");, which automatically redirects the program to the error page. With the AJAX end of error handling, however, I don't feel as confident because I pieced it together with parts from different solutions I've researched. I'm worried I'm not aware of what could go wrong.

Question:

Is this the proper way to handle errors during AJAX requests in MVC? Could something possibly go wrong with this set up? Is this in any way improper or too far from common standards?

Code:

Startup.cs > Configure method:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //using build config to use the correct error page: https://stackoverflow.com/a/62177235/12300287
    //Reason why we don't use environmental variables is because we can't guarantee access to clients' 
    //machines to create them.
#if (DEVELOPMENT || STAGING)
    app.UseDeveloperExceptionPage();
#else
    app.UseExceptionHandler("/Error/Index");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
#endif

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    //app.UseCookiePolicy();

    app.UseAuthentication();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=UserAccess}/{action=Index}/{id?}");
    });
}

ErrorController.cs:

public class ErrorController : Controller
{
    [AllowAnonymous]
    public IActionResult Index()
    {
        IExceptionHandlerPathFeature exceptionDetails = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
        Exception exception = exceptionDetails?.Error; // Here will be the exception details.

        Error model = new Error();
        model.ID = exception.HResult;
        model.Message = exception.Message;
        model.Path = exceptionDetails.Path;

        return View(model);
    }
}

Global AJAX error event handler (the if statement is to handle authentication):

function InitializeGlobalAjaxEventHandlers() {
    $(document).ajaxError(function (event, xhr, ajaxSettings, thrownError) {
        //status is set in the [AuthorizeAjax] action filter if user isn't authenticated
        if (xhr.status == 403) {
            var response = $.parseJSON(xhr.responseText);
            window.location = response.redirectUrl;
        }

        //the document HTML is replaces by responseText value, which contains the HTML of error page
        document.write(xhr.responseText);

        //TODO: will need to display an error page if responseText is empty, which
        //can happen if an AJAX request doesn't reach the server (for example: if URL is incorrect).
    });
}
David Liang
  • 20,385
  • 6
  • 44
  • 70
Lukas
  • 1,699
  • 1
  • 16
  • 49
  • Did you take a look at this? https://stackoverflow.com/questions/4707755/asp-net-mvc-ajax-error-handling –  Jul 10 '20 at 14:19
  • Yes, I have. Seems like a fine way to do it, but I do want to be able crash the program with most of the AJAX requests in the app and display the correct error page with information, which is what my set up does. – Lukas Jul 10 '20 at 14:30

2 Answers2

1

As you explain, there are two threads of code to consider. One is at the client and the other at the server.

Whilst the server can return codes and messages for non-expected results, the client must still be robust and stand-alone. For example, if the server cannot be reached, a time-out error can occur at the client which it must handle. As mentioned above, you can catch errors at the local and global Ajax level.

The server may also generate an error response that never reaches the client.

In some projects I have performed the following:- If a non-expected result is generated on the server, it is logged in a database and returns an JSON message as an error.

If a non-expected result is generated on the client, a server service is called to log the error on the database.

In both cases, a message is displayed in the current page. A user can also navigate to an error page where recent errors (stored in the database) are displayed.

Working on many project of varying sizes, I've come to the conclusion that there isn't really a solution that fits everything

0

Is this the proper way to handle errors during AJAX requests in MVC? Could something possibly go wrong with this set up? Is this in any way improper or too far from common standards?

As far as I know, there are no unified/official way of handling Ajax errors in ASP.NET Core MVC project.

Normally we show specific confirm message/content through Ajax error callback function if the Ajax request fails rather than directly displaying detailed exception information to client user, which would help achieve a better customer experience.

If you have specific requirement that requires displaying the Developer Exception Page/custom error page while Ajax request fails, as you did, you can dynamically write the returned responseText to the HTML document using document.write(xhr.responseText); in global error handler.

Fei Han
  • 26,415
  • 1
  • 30
  • 41
  • While this works, if the user decides to click the back button on the browser they will get directed to the page they were on before the page that the error occurred on. This happens because we replace the HTML of the page that caused the error. Do you know of a way around this so going back functions properly? – Lukas Nov 11 '20 at 20:56