How to make custom error handling in ASP.NET Core 2.0

in #utopian-io7 years ago (edited)

Hello

Today I want to show You how to make custom Error Handling in ASP.NET Core 2.0.

Why we create custom error handling?

Dealing with exceptions from the API’s point of view is not an easy task. In the end, we want to return meaningful error codes to the end customers of our service once something goes wrong. It doesn’t matter whether it’s an internal server error or some client validation issue – returning simply an HTTP Status Code that something went wrong doesn’t really help.

First of all we have to create new project:

I'm using Visual Studio 2017 Community.

image.png

image.png

I will create very simple implementation of custom error handling, but thanks that you will understand how it work and will be able to create your own error handling in your own application. Doesn't matter if this will be very simple application or huge application consist couple of project.

At the begining let's cretae new folder and named his Exceptions. In this folder we will create 2 classes:

  • ApplicationException
  • ErrorCodes

image.png

ApplicationException class will be inherit from base Exception class. Thans them we can use normal exception handling and change it in our own way.

In ApplicationException class we have to write this code:

using System;

namespace CustomErrorHandling.Exceptions
{
    public abstract class ApplicationException : Exception
    {
        public string Code { get; }

        protected ApplicationException()
        {
        }

        public ApplicationException(string code)
        {
            Code = code;
        }

        public ApplicationException(string message, params object[] args)
            : this(string.Empty, message, args)
        {
        }

        public ApplicationException(string code, string message, params object[] args)
            : this(null, code, message, args)
        {
        }

        public ApplicationException(Exception innerException, string message, params object[] args)
            : this(innerException, string.Empty, message, args)
        {
        }

        public ApplicationException(Exception innerException, string code, string message, params object[] args)
            : base(string.Format(message, args), innerException)
        {
            Code = code;
        }
    }
} 

We have there just contructors, Each of these constructors is different and take different arguments. The simplest one is protected because we don't want to use it in other part of our application.
Thanks them we have to invoke override constructor with some parameters.

In ErrorCode class we can define our own exception codes. For Example when we want to throw exception connected with wrong Email we can write there:
public static string InvalidEmail => "invalid_email";

I wrote seme default exceptions codes. Basic implementation of this class looks like this:

public static class ErrorCodes
    {
        public static string InvalidUsername => "invalid_username";
        public static string InvalidEmail => "invalid_email";
        public static string InvalidRole => "invalid_role";
        public static string InvalidPassword => "invalid_password";
    }

Ok, so we have our exceptions, Now we have create another new folder and name it Framework. In this folder we have to create 2 classes:

  • ExceptionHandlerMiddleware
  • Extensions

First class is responsible for whole flow our exception handling. It looks like:

public class ExceptionHandlerMiddleware
{
private readonly RequestDelegate _next;

    public ExceptionHandlerMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception exception)
        {
            await HandleExceptionAsync(context, exception);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        var errorCode = "error";
        var statusCode = HttpStatusCode.BadRequest;
        var exceptionType = exception.GetType();
        switch (exception)
        {
            case Exception e when exceptionType == typeof(UnauthorizedAccessException):
                statusCode = HttpStatusCode.Unauthorized;
                break;

            case ApplicationException e when exceptionType == typeof(ApplicationException):
                statusCode = HttpStatusCode.BadRequest;
                errorCode = e.Message;
                break;

            default:
                statusCode = HttpStatusCode.InternalServerError;
                errorCode = "Internal Server Error";
                break;
        }

        var response = new { code = statusCode, message = errorCode };
        var payload = JsonConvert.SerializeObject(response);
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)statusCode;

        return context.Response.WriteAsync(payload);

    }
}

In this class we define that if our exception is type ApplicationException the error code is our error message from previous class ErrorCodes and the status code is HttpStatusCode.BadRequest.

In Extensions we have to write some Function:

public static class Extensions
    {
        public static IApplicationBuilder UseCustomExceptionHandler(this IApplicationBuilder builder)
            => builder.UseMiddleware(typeof(ExceptionHandlerMiddleware));
    }

At the end we have to go to our startup class and define that we want to use our own exception handling:

image.png

How to throw our custom exception?

It is very simple, we c=just have to throw exception type Application Exception, and add some message like this:

// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
            throw new ApplicationException(ErrorCodes.CustomException, "This is our custom exception");
}

Now our exceptions will look more professional and final customer will know more about exception, what his occurs.



Posted on Utopian.io - Rewarding Open Source Contributors

Sort:  

Thank you for the contribution. It has been approved.

You can contact us on Discord.
[utopian-moderator]

Hey @babelek I am @utopian-io. I have just upvoted you!

Achievements

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • This is your first accepted contribution here in Utopian. Welcome!

Suggestions

  • Contribute more often to get higher and higher rewards. I wish to see you often!
  • Work on your followers to increase the votes/rewards. I follow what humans do and my vote is mainly based on that. Good luck!

Get Noticed!

  • Did you know project owners can manually vote with their own voting power or by voting power delegated to their projects? Ask the project owner to review your contributions!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x