Oussama SAIDI

0 %
Oussama SAIDI
Consultant .Net
Sharepoint Office 365

  • Résidence:
    France
  • Ville:
    Lille
Arabe
Français
Anglais
.Net
Html/Css/Js
Web Api
Base de données
  • C#,.Net Core, .Net MVC
  • Rest Api
  • Docker
  • GIT,TFS

.NET 6 – Gestion des exceptions globales de l’API Web

février 14, 2023

Vous pouvez trouver le code source complet sur github

Conditions préalables

  • Code Visual Studio / Visual Studio
  • SDK .NET Core 6
  • Compréhension de la programmation C#
  • Compréhension des API .NET Core

Introduction

Lorsque nous construisons notre application, bien que nous espérons que notre application fonctionnera sans aucune erreur jusqu’à la fin des temps. Ce n’est pas vraiment le cas, des exceptions se produisent dans les applications et nous devons les gérer.

La gestion des exceptions est une base que nous devons prendre en compte lors de la conception et de la construction de notre application pour avoir une application stable et éviter les pannes d’application.

Il existe de nombreuses façons d’implémenter la gestion des exceptions lors de la construction de nos applications à partir d’une approche très granulaire vers une approche plus générique.

Dans cet article, nous explorerons la gestion globale des exceptions via le middleware pour détecter efficacement les erreurs d’exécution conformément à nos exigences.

Code

La première chose que nous devons faire est de créer une nouvelle application WebApi

dotnet new webapi -n ErrorManagement

Maintenant que notre application a été créée, nous devons installer certains packages

dotnet add package Microsoft.EntityFrameworkCore 
dotnet add package Microsoft.EntityFrameworkCore.Design 
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
dotnet add package Microsoft.EntityFrameworkCore.Tools

Pour nous assurer que tout fonctionne comme il se doit, nous devons créer notre application

dotnet build

Il est maintenant temps d’ajouter les modèles, pour cet exemple d’application, nous allons créer une application pour répertorier tous les pilotes F1. Pour cela nous allons créer un dossier Models dans le répertoire racine de notre application qui contiendra nos modèles Dans le dossier Models, nous allons créer une nouvelle classe appelée Employee

namespace Net.WebApi.Globalexcpetions.Models;

public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; } = "";
    public string LastName { get; set; } = "";
}

Une fois le modèle créé, l’étape suivante consiste à créer notre contexte de base de données dans le répertoire racine de notre application. Nous allons créer un nouveau dossier appelé Data et à l’intérieur du dossier Data, nous ajouterons la classe ApiDbContext.

using Microsoft.EntityFrameworkCore;
using Net.WebApi.Globalexcpetions.Models;

namespace Net.WebApi.Globalexcpetions.Data;

public class ApiDbContext :DbContext
{
    public ApiDbContext(DbContextOptions<ApiDbContext> options) : base(options) { }
    public DbSet<Employee> Employees { get; set; }
}

Maintenant, nous devons ajouter la chaîne de connexion dans le appsettings.json

"ConnectionStrings": {
    "SampleDbConnection": "User ID =mohamad;Password=12345678;Server=localhost;Port=5432;Database=sampledb; Integrated Security=true;Pooling=true;"
  }

Ensuite, nous devons mettre à jour notre program.cs

builder.Services.AddEntityFrameworkNpgsql().AddDbContext<ApiDbContext>(opt =>
        opt.UseNpgsql(builder.Configuration.GetConnectionString("SampleDbConnection")));

Une fois que nous les ajoutons, nous pouvons faire notre migration

dotnet ef migrations add "initial_migration"
dotnet ef database update

Créons maintenant les EmployeeServices dans le répertoire racine de notre application, créons un nouveau dossier appelé Services et dans ce dossier, nous allons créer une nouvelle interface appelée IEmployeeService

using Net.WebApi.Globalexcpetions.Models;

namespace Net.WebApi.Globalexcpetions.Services;

public interface IEmployeeService
{
    public Task<IEnumerable<Employee>> GetEmployees();
    public Task<Employee?> GetEmployeeById(int id);
    public Task<Employee> AddEmployee(Employee Employee);
    public Task<Employee> UpdateEmployee(Employee Employee);
    public Task<bool> DeleteEmployee(int Id);
}

Maintenant, dans le même dossier, nous allons créer une nouvelle classe appelée EmployeeService

using Microsoft.EntityFrameworkCore;
using Net.WebApi.Globalexcpetions.Data;
using Net.WebApi.Globalexcpetions.Models;

namespace Net.WebApi.Globalexcpetions.Services;

public class EmployeeService : IEmployeeService
{
    private readonly ApiDbContext _dbContext;

    public EmployeeService(ApiDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<IEnumerable<Employee>> GetEmployees()
    {
        return await _dbContext.Employees.ToListAsync();
    }

    public async Task<Employee?> GetEmployeeById(int id)
    {
        return await _dbContext.Employees.FirstOrDefaultAsync(x => x.Id == id);
    }

    public async Task<Employee> AddEmployee(Employee Employee)
    {
        var result = _dbContext.Employees.Add(Employee);
        await _dbContext.SaveChangesAsync();
        return result.Entity;
    }

    public async Task<Employee> UpdateEmployee(Employee Employee)
    {
        var result = _dbContext.Employees.Update(Employee);
        await _dbContext.SaveChangesAsync();
        return result.Entity;
    }

    public async Task<bool> DeleteEmployee(int Id)
    {
        var filteredData = _dbContext.Employees.FirstOrDefault(x => x.Id == Id);
        var result = _dbContext.Remove(filteredData);
        await _dbContext.SaveChangesAsync();
        return result != null ? true : false;
    }
}

Mettons maintenant à jour notre Program.cs afin que nos EmployeeServices soient injectés dans notre conteneur Dependency Inject

builder.Services.AddScoped<IEmployeeService, EmployeeService>();

Maintenant, créons notre EmployessController, à l’intérieur du dossier du contrôleur, nous allons créer une nouvelle classe appelée EmployessController et ajouterons ce qui suit

using Microsoft.AspNetCore.Mvc;
using Net.WebApi.Globalexcpetions.Models;
using Net.WebApi.Globalexcpetions.Services;

namespace Net.WebApi.Globalexcpetions.Controllers;

[ApiController]
[Route("[controller]")]
public class EmployeesController : ControllerBase
{
    private readonly ILogger<EmployeesController> _logger;
    private readonly IEmployeeService _employeeServices;

    public EmployeesController(
        ILogger<EmployeesController> logger,
        IEmployeeService employeeServices)
    {
        _logger = logger;
        _employeeServices = employeeServices;
    }
    [HttpGet("employeelist")]
    public async Task<IEnumerable<Employee>> employeeList()
    {
        var employeeList = await _employeeServices.GetEmployees();
        return employeeList;
    }

    [HttpGet("getemployeebyid")]
    public async Task<IActionResult> GetemployeeById(int Id)
    {
        _logger.LogInformation($"Fetch employee with ID: {Id} from the database");
        var employee = await _employeeServices.GetEmployeeById(Id);
        if (employee == null)
        {
            //throw new Notfound($"employee ID {Id} not found.");
            return NotFound();
        }
        _logger.LogInformation($"Returning employee with ID: {employee.Id}.");
        return Ok(employee);
    }

    [HttpPost("addemployee")]
    public async Task<IActionResult> Addemployee(Employee employee)
    {
        var result = await _employeeServices.AddEmployee(employee);
        return Ok(result);
    }

    [HttpPut("updateemployee")]
    public async Task<IActionResult> Updateemployee(Employee employee)
    {
        var result = await _employeeServices.UpdateEmployee(employee);
        return Ok(result);
    }

    [HttpDelete("deleteemployee")]
    public async Task<bool> Deleteemployee(int Id)
    {
        return await _employeeServices.DeleteEmployee(Id);
    }
}

Ajoutons maintenant un nouveau dossier appelé Exceptions qui sera utilisé pour gérer toutes nos exceptions

Nous ajouterons les exceptions suivantes

namespace Net.WebApi.Globalexcpetions.Exceptions;

public class BadRequestException : Exception
{
    public BadRequestException(string message) : base(message)
    { }
}
namespace Net.WebApi.Globalexcpetions.Exceptions;

public class KeyNotFoundException : Exception
{
    public KeyNotFoundException(string message) : base(message)
    { }
}
namespace Net.WebApi.Globalexcpetions.Exceptions;

public class NotFoundException : Exception
{
    public NotFoundException(string message) : base(message)
    { }
}
namespace Net.WebApi.Globalexcpetions.Exceptions;

public class NotImplementedException : Exception
{
    public NotImplementedException(string message) : base(message)
    { }
}
namespace Net.WebApi.Globalexcpetions.Exceptions;

public class UnauthorizedAccessException : Exception
{
    public UnauthorizedAccessException(string message) : base(message)
    { }
}

Maintenant que nos exceptions ont été ajoutées, nous devons ajouter un dossier au répertoire racine de notre application appelé configurations où nous pouvons construire notre GlobalErrorHandlingMiddleware

using System.Net;
using System.Text.Json;
using Net.WebApi.Globalexcpetions.Exceptions;

using KeyNotFoundException = Net.WebApi.Globalexcpetions.Exceptions.KeyNotFoundException;
using NotImplementedException = Net.WebApi.Globalexcpetions.Exceptions.NotImplementedException;
using UnauthorizedAccessException = Net.WebApi.Globalexcpetions.Exceptions.UnauthorizedAccessException;

namespace Net.WebApi.Globalexcpetions.Exceptions;
public class GlobalErrorHandlingMiddleware
{
    private readonly RequestDelegate _next;

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

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

    private static Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        HttpStatusCode status;
        var stackTrace = string.Empty;
        string message;

        var exceptionType = exception.GetType();

        if (exceptionType == typeof(BadRequestException))
        {
            message = exception.Message;
            status = HttpStatusCode.BadRequest;
            stackTrace = exception.StackTrace;
        }
        else if (exceptionType == typeof(NotFoundException))
        {
            message = exception.Message;
            status = HttpStatusCode.NotFound;
            stackTrace = exception.StackTrace;
        }
        else if (exceptionType == typeof(NotImplementedException))
        {
            status = HttpStatusCode.NotImplemented;
            message = exception.Message;
            stackTrace = exception.StackTrace;
        }
        else if (exceptionType == typeof(UnauthorizedAccessException))
        {
            status = HttpStatusCode.Unauthorized;
            message = exception.Message;
            stackTrace = exception.StackTrace;
        }
        else if (exceptionType == typeof(KeyNotFoundException))
        {
            status = HttpStatusCode.Unauthorized;
            message = exception.Message;
            stackTrace = exception.StackTrace;
        }
        else
        {
            status = HttpStatusCode.InternalServerError;
            message = exception.Message;
            stackTrace = exception.StackTrace;
        }

        var exceptionResult = JsonSerializer.Serialize(new { error = message, stackTrace });
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)status;

        return context.Response.WriteAsync(exceptionResult);
    }
}

Le GlobalErrorHandlingMiddleware est utilisé pour fournir plus de contrôle sur les exceptions que l’application va générer

S’il y a des erreurs dans une demande entrante, le GlobalErrorHandlingMiddleware traitera l’erreur

Créons maintenant ApplicationBuilderExtension afin que nous puissions injecter notre middleware dans le dossier Services

using Net.WebApi.Globalexcpetions.Exceptions;

namespace Net.WebApi.Globalexcpetions.Configurations;

public static class ApplicationBuilderExtensions
{
    public static IApplicationBuilder AddGlobalErrorHandler(this IApplicationBuilder applicationBuilder)
        => applicationBuilder.UseMiddleware<GlobalErrorHandlingMiddleware>();
}

Injectons maintenant ceci dans le Program.cs

app.AddGlobalErrorHandler();

Veuillez fournir vos commentaires et poser toute question.

Derniers Articles

Publié dans .Net Core, C Sharp, c#, swagger ui, web api
© 2024 Tous les droits sont réservés.
oussamasaidi.com