Introduction
Error handling is a crucial aspect of developing reliable APIs in .NET. Effective error handling not only ensures a smooth user experience but also helps in troubleshooting and debugging issues efficiently. In this blog, we’ll explore the best practices for API error handling in .NET, along with clear examples to demonstrate each concept.
Define Clear and Consistent Error Responses
- Use HTTP status codes to indicate the result of API requests (e.g., 200 for success, 400 for client errors, 500 for server errors).
- Provide meaningful error messages along with error codes for easy identification and troubleshooting.
- Maintain consistency in error responses across different endpoints and API versions.
Example
{
"error": {
"code": 400,
"message": "Invalid request parameters.",
"details": "The 'id' parameter is missing."
}
}
Exception Handling
- Implement try-catch blocks to handle exceptions gracefully within the API code.
- Log detailed error information, including stack traces, to facilitate debugging.
- Avoid exposing sensitive information in error messages to prevent security risks.
Example
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace API_Error_Handling.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class EmployeesController : ControllerBase
{
private readonly ILogger<EmployeesController> _logger;
public EmployeesController(ILogger<EmployeesController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult GetEmployees()
{
try
{
throw new NotImplementedException();
}
catch (Exception ex)
{
// Log the exception
_logger.LogError(ex, "An error occurred");
// Return a generic error response
return StatusCode(500, "An unexpected error occurred, Please try again later.");
}
}
}
}
Output
Use Custom Exception Types
- Define custom exception classes to represent specific error scenarios in the API.
- When faced with domain-specific errors, throw custom exceptions to give the caller extra information.
Scenario
Imagine an application for managing employee data. We can create a custom exception to handle situations where a user tries to assign a negative salary to an employee.
Custom Exception Class
public class InvalidSalaryException : Exception
{
public decimal RequestedSalary { get; private set; }
public InvalidSalaryException() { }
public InvalidSalaryException(decimal requestedSalary)
: base($"Salary cannot be negative. You tried to assign: {requestedSalary}")
{
RequestedSalary = requestedSalary;
}
}
Explanation
- The InvalidSalaryException inherits from System. Exception.
- It includes a property RequestedSalary to store the attempted invalid value.
- The constructor with an argument initializes the property and creates a specific error message based on the provided salary.
Throwing the Exception
public void UpdateEmployeeSalary(int employeeId, decimal newSalary)
{
if (newSalary < 0)
{
throw new InvalidSalaryException(newSalary);
}
}
Catching and Handling the Exception
[HttpGet(nameof(GetEmployeeSalary))]
public IActionResult GetEmployeeSalary()
{
try
{
CustomException customException = new();
customException.UpdateEmployeeSalary(1, -1000); // Invalid salary
return Ok();
}
catch (InvalidSalaryException ex)
{
_logger.LogError("Error updating salary: " + ex.Message);
return StatusCode(403, "Please enter a valid positive salary.");
}
}
- The error message includes the attempted value, providing context to the user.
- This allows for better validation and user experience.
Output
Global Error Handling
- Implement a global exception handler to centralize error-handling logic.
- Customize error responses based on the type of exception and status code.
“In my next blog post, I’ll be covering Global Error Handling in more depth.”
Logging and Monitoring
- Implement logging to capture error details for debugging and troubleshooting.
- Consider logging libraries like NLog or Serilog.
- Integrate with monitoring tools like Application Insights for in-depth error analysis.
Source Code
Download code from here
Keep Learning!