3

Scenario

I have a .NET Core 2.2 web API with an exception handling middleware. Whenever an exception occurs in the application (inside the MVC layer) it gets caught by the exception middleware and returned as an internal server error back to the frontend and logged to kibana.

The problem

This is all fine and well when things go wrong, but sometimes I want to notify the calling application of specifically what went wrong. I.e., "Could not find record in database!" or "Failed to convert this to that!"

My Solution

I've used application Exceptions (not great - I know) to piggy back off the error middleware to return this to the frontend. This has been working fine, but has created a lot of noise around the code by having to throw a whole bunch of exceptions. I'm not satisfied with this approach and convinced that there must be a better solution.

My application architecture: I'm following a traditional n-tier application layout being services (business logic) and repositories (DAL) all speaking to each other. I would preferably like to elegantly bubble up any issues back to the user in any of these layers.

I've been thinking about this for a while now and am not sure what the best way to go about it is. Any advice would be appreciated.

2
  • Can you add an event listener to the calling application to listen to the middle ware's exception handling event? Commented Mar 20, 2019 at 13:54
  • @L0uis This is possible , but unsure how this would allow me to achieve what Im looking for. Commented Mar 20, 2019 at 13:56

2 Answers 2

4

I use a kind of the operation result pattern (non-official pattern).

The principle is to return a new Type containing:

  • Whether the operation was a success.
  • The result of the operation if was successful.
  • Details about the Exception that caused the failure.

Consider the following class:

public class OperationResult
{
    protected OperationResult()
    {
        this.Success = true;
    }

    protected OperationResult(string message)
    {
        this.Success = false;
        this.FailureMessage = message;
    }

    protected OperationResult(Exception ex)
    {
        this.Success = false;
        this.Exception = ex;
    }

    public bool Success { get; protected set; }
    public string FailureMessage { get; protected set; }
    public Exception Exception { get; protected set; }

    public static OperationResult SuccessResult()
    {
        return new OperationResult();
    }

    public static OperationResult FailureResult(string message)
    {
        return new OperationResult(message);
    }

    public static OperationResult ExceptionResult(Exception ex)
    {
        return new OperationResult(ex);
    }

    public bool IsException()
    {
        return this.Exception != null;
    }
}

Then you could easily adapt OperationResult or create a class that inherits from OperationResult, but uses a generic type parameter.

Some examples:

  1. The Operation Result Pattern — A Simple Guide
  2. Error Handling in SOLID C# .NET – The Operation Result Approach
Sign up to request clarification or add additional context in comments.

2 Comments

I like this , the only problem is having to wrap everything inside the OperationResult generic. Im gonna have this all over the codebase inside all of my concrete implementations.
Ive decided to go with this!
1

As per the Microsoft's standards, it is ideal to use ProblemDetails object in case of 4xx/5xx exceptions -

Following is the customised RequestDelegate method which you can use in ApiExceptionHandler to handle exceptions.

public async Task RequestDelegate(HttpContext context)
{
    var exception = context.Features.Get<IExceptionHandlerFeature>().Error;
    var problemDetails = new ProblemDetails
    {
        Title = "An unexpected error occurred!",
        Status = GetStatusCode(exception),
        Detail = _env.IsDevelopment() ? exception.Message : "An unexpected error occurred!",
        Instance = $"{Environment.MachineName}:{context.TraceIdentifier}:{Guid.NewGuid()}"
    };

    _logger.LogError($"Exception thrown. StatusCode: {problemDetails.Status}. Instance: {problemDetails.Instance}", exception);

    context.Response.StatusCode = problemDetails.Status.Value;
    context.Response.WriteJson(problemDetails, "application/problem + json");

    await Task.CompletedTask;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.