How to Create Exceptions
How to Create Exceptions 관련
Properly handling exceptions is crucial for ensuring the stability and reliability of an application. In the context of ASP.NET Core, there are two main types of exceptions:
- System Exceptions: These are exceptions thrown by the .NET runtime or the underlying system.
- Application Exceptions: These are exceptions thrown by the application code to handle specific errors or conditions.
In ASP.NET Core with .NET 8, a new feature called global exception handling was introduced. This feature allows you to handle exceptions globally in your application, making it easier to manage errors and provide a consistent user experience.
In our application, we will create custom exception classes to handle specific errors and conditions. We’ll also leverage the global exception handling feature to manage exceptions globally, ensuring a uniform approach to error handling across the entire application.
We are going to create the following exception classes:
NoBookFoundException
: Thrown when a book with the specified ID is not found.BookDoesNotExistException
: Thrown when a book with the specified ID does not exist.GlobalExceptionHandler
: Handles exceptions globally in the application.
In the Exceptions
folder, create a new file named NoBookFoundException.cs
and add the following code:
namespace bookapi_minimal.Exceptions
{
public class NoBookFoundException : Exception
{
public NoBookFoundException() : base("No books found")
{}
}
}
In this code, we are creating a custom exception class named NoBookFoundException
that inherits from the Exception
class. The NoBookFoundException
class is used to handle the scenario where no books are found in the database. We are also providing a custom error message for the exception.
In the Exceptions
folder, create a new file named BookDoesNotExistException.cs
and add the following code:
namespace bookapi_minimal.Exceptions
{
public class BookDoesNotExistException : Exception
{
private int id { get; set; }
public BookDoesNotExistException(int id) : base($"Book with id {id} does not exist")
{
this.id = id;
}
}
}
In this code, we are creating a custom exception class named BookDoesNotExistException
that inherits from the Exception
class. The BookDoesNotExistException
class is used to handle the scenario where a book with the specified ID does not exist in the database. We are also providing a custom error message for the exception.
In the Exceptions
folder, create a new file named GlobalExceptionHandler.cs
and add the following code:
using System.Net;
using bookapi_minimal.Contracts;
using Microsoft.AspNetCore.Diagnostics;
namespace bookapi_minimal.Exceptions
{
// Global exception handler class implementing IExceptionHandler
public class GlobalExceptionHandler : IExceptionHandler
{
private readonly ILogger<GlobalExceptionHandler> _logger;
// Constructor to initialize the logger
public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
{
_logger = logger;
}
// Method to handle exceptions asynchronously
public async ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{
// Log the exception details
_logger.LogError(exception, "An error occurred while processing your request");
var errorResponse = new ErrorResponse
{
Message = exception.Message,
Title = exception.GetType().Name
};
// Determine the status code based on the type of exception
switch (exception)
{
case BadHttpRequestException:
errorResponse.StatusCode = (int)HttpStatusCode.BadRequest;
break;
case NoBookFoundException:
case BookDoesNotExistException:
errorResponse.StatusCode = (int)HttpStatusCode.NotFound;
break;
default:
errorResponse.StatusCode = (int)HttpStatusCode.InternalServerError;
break;
}
// Set the response status code
httpContext.Response.StatusCode = errorResponse.StatusCode;
// Write the error response as JSON
await httpContext.Response.WriteAsJsonAsync(errorResponse, cancellationToken);
// Return true to indicate that the exception was handled
return true;
}
}
}
Let's break down the code above:
- We define a class named
GlobalExceptionHandler
that implements theIExceptionHandler
interface. TheIExceptionHandler
interface is used to handle exceptions globally in the application. - The
GlobalExceptionHandler
class contains a constructor that initializes theILogger<GlobalExceptionHandler>
dependency. TheILogger
is used for logging information and errors. - The
TryHandleAsync
method is used to handle exceptions asynchronously. This method accepts theHttpContext
,Exception
, andCancellationToken
as parameters. - We log the exception details using the
ILogger
dependency. - We create an
ErrorResponse
object to represent the error response returned by the API. TheErrorResponse
object contains the error message, title, and status code. - We determine the status code based on the type of exception. If the exception is a
BadHttpRequestException
, we set the status code toBadRequest
. If the exception is aNoBookFoundException
orBookDoesNotExistException
, we set the status code toNotFound
. Otherwise, we set the status code toInternalServerError
. - We set the response status code using the
httpContext.Response.StatusCode
property. - We write the error response as JSON using the
httpContext.Response.WriteAsJsonAsync
method. - We return
true
to indicate that the exception was handled successfully.
Now that we have created the exception classes, let's register the GlobalExceptionHandler
in the dependency injection container. Since we created an Extension method for registering services in the dependency injection container, we will add the GlobalExceptionHandler
to the ServiceExtensions
class.
Update the ServiceExtensions
class in the Extensions
folder as follows:
//...
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
builder.Services.AddProblemDetails();
//...
The AddExceptionHandler
method registers the GlobalExceptionHandler
in the dependency injection container. The AddProblemDetails
method registers the ProblemDetails
class in the dependency injection container.
Now that we have registered the GlobalExceptionHandler
in the dependency injection container, we can use it to handle exceptions globally in our application. In the next section, we will create the API endpoints to interact with the book data.