Skip to content

Exception Handling with BoilerPlate for WebAPI

An Exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program’s instructions.

Exceptions are generally classified into 2 types Client-Errors (4xx) and Server-Errors(5xx). Client-Errors are usually caused due to incorrect requests from the user like

Status CodeStatus MessageCause
400BadRequestIncorrect Request (Validation Errors, missing data, missing parameters etc)
401UnauthorizedThe requested page needs a username and a password.
403ForbiddenAccess is forbidden to the requested page.
404NotFoundThe server can not find the requested page.

While Server-Errors are caused due to Code issues or Server issues like

Status CodeStatus MessageCause
500Internal Server ErrorThe request was not completed. The server met an unexpected condition.
502Bad GatewayThe request was not completed. The server received an invalid response from the upstream server.
503Service UnavailableThe request was not completed. The server is temporarily overloading or down.
504Gateway Timeout
The gateway has timed out.
505HTTP Version Not SupportedThe server does not support the “HTTP protocol” version.

Error Handling Implementation

  • Filters: These are mainly used for validation purposes where they are placed on top of models instead of fields. We can define custom filters or use an existing one for finding the errors.
  • GlobalExceptionHandler: Exceptions like (errors generated from controller Constructors, message Handlers, at the time of routing and during the response of content serialization) when generated cannot be captured by ExectionFilterAttribute so using of globalExceptionalHandler becomes a necessity
  • MiddleWare: ASP consists of Request Pipelines which includes a chain of middleware components. Which inturn contains series of reqeust delegates that are invoked one after another.

Exception Handling Middleware BoilerPlate Code

ApiException.cs :DataTransferObject / DataType

namespace Exceptionhandling.ExceptionHandling._Errors
{
    public class ApiExceptions
    {
        public ApiExceptions(int statusCode, string message = null, string stackTrace = null)
        {
            this.StatusCode = statusCode;
            this.Message = message;
            this.StackTrace = stackTrace;
        }

        public int StatusCode { get; set; }
        public string Message { get; set; }
        public string StackTrace { get; set; }
    }
}

Exception Handling Logic: In case we are running the app in local we will return stack-trace else simply return “Internal Server Error” message.

using System.Net;
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using Exceptionhandling.ExceptionHandling._Errors;
using System.Text.Json;
using Microsoft.Extensions.Logging;

namespace Exceptionhandling.ExceptionHandling._Middleware
{
    public class ExceptionMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IHostEnvironment _env;
        private readonly ILogger<ExceptionMiddleware> _logger;
        public ExceptionMiddleware(RequestDelegate next, IHostEnvironment env, ILogger<ExceptionMiddleware> logger)
        {
            _logger = logger;
            _env = env;
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception error)
            {
                //Writes the error to our output window
                _logger.LogError(error, error.Message);

                //Setting the status of the response
                context.Response.ContentType = "application/json";
                context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

                //used for displaying stack in dev environment and simple message in Production
                var response = (_env.IsDevelopment())
                ? new ApiExceptions(context.Response.StatusCode, error.Message, error.StackTrace?.ToString())
                : new ApiExceptions(context.Response.StatusCode, "Internal Server Error");

                var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
                var json = JsonSerializer.Serialize(response, options);
                await context.Response.WriteAsync(json);
            }
        }
    }
}

Startup.cs: Add our middleware (app.UseMiddleware<ExceptionMiddleware>();) like below

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //Configure ExceptionMiddleware
            app.UseMiddleware<ExceptionMiddleware>();

            if (env.IsDevelopment())
            {
                //app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ExceptionHandling v1"));
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

ErrorGeneratorController.cs : Generate Errors For Testing

using Microsoft.AspNetCore.Mvc;

namespace Exceptionhandling.ExceptionHandling.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ErrorGeneratorController : ControllerBase
    {
        [HttpGet("NotFound")]
        public ActionResult GetNotFound()
        {
            return NotFound();
        }

        [HttpGet("ServerError")]
        public ActionResult GetServerError()
        {
            dynamic upper = 10;
            dynamic lower = 0;

            return upper / lower;
        }

        [HttpGet("BadRequest")]
        public ActionResult GetBadRequest()
        {
            return BadRequest("This was not a good request");
        }
    }
}
Published inCS FundamentalsUncategorised

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *