Exception not handled by Middleware in .NET 8 Web API

93 views Asked by At

Hi I have following code:

Program.cs

using AutoMapper;
using RestaurantAPI;
using RestaurantAPI.Entities;
using RestaurantAPI.Services;
using NLog;
using NLog.Web;
using Microsoft.Extensions.Logging;
using RestaurantAPI.Middleware;

// Early init of NLog to allow startup and exception logging, before host is built
var logger = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
//logger.Debug("init main");
try
{
    var builder = WebApplication.CreateBuilder(args);

    // Add services to the container.

    builder.Services.AddControllers();
    builder.Services.AddDbContext<RestaurantDbContext>();
    builder.Services.AddScoped<RestaurantSeeder>();
    builder.Services.AddScoped<IRestaurantService, RestaurantService>();
    builder.Services.AddAutoMapper(typeof(RestaurantMappingProfile));
    builder.Services.AddScoped<ErrorHandlingMiddleware>();

    // NLog: Setup NLog for Dependency injection
    builder.Logging.ClearProviders();
    builder.Host.UseNLog();


    var app = builder.Build();
    app.UseMiddleware<ErrorHandlingMiddleware>();


    // Retrieve the Seeder from the service provider and call the Seed method.
    using (var scope = app.Services.CreateScope())
    {
        
        var services = scope.ServiceProvider;
        var seeder = services.GetRequiredService<RestaurantSeeder>();
        seeder.Seed();
    }

    

    // Configure the HTTP request pipeline.

    app.UseHttpsRedirection();

    app.MapControllers();

    app.Run();
}

catch (Exception exception)
{
    // NLog: catch setup errors
    logger.Error(exception, "Stopped program because of exception");
    throw;
}
finally
{
    // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
    NLog.LogManager.Shutdown();
}

ErrorHandlingMiddleware.cs


namespace RestaurantAPI.Middleware
{
    public class ErrorHandlingMiddleware : IMiddleware
    {
        private readonly ILogger<ErrorHandlingMiddleware> _logger;

        public ErrorHandlingMiddleware(ILogger<ErrorHandlingMiddleware> logger)
        {
            _logger = logger;
        }
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            try
            {
                await next.Invoke(context);
            }
            catch (Exception e)
            {
                _logger.LogError(e, e.Message);

                context.Response.StatusCode = 500;
                await context.Response.WriteAsync("Something went wrong");
            }
        }
    }
}


RestaurantController.cs


using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using RestaurantAPI.Entities;
using RestaurantAPI.Models;
using RestaurantAPI.Services;

namespace RestaurantAPI.Controllers
{
    [Route("api/restaurant")]
    public class RestaurantController : ControllerBase
    {
        private readonly IRestaurantService _restaurantService;

        public RestaurantController(IRestaurantService restaurantService)
        {
            _restaurantService = restaurantService;
        }

        [HttpPost]
        public ActionResult CreateRestaurant([FromBody] CreateRestaurantDto dto)
        {
            if (!ModelState.IsValid)
                return BadRequest();

            var id = _restaurantService.Create(dto);

            return Created($"/api/restaurant/{id}", null);
        }

        [HttpGet]
        public ActionResult<IEnumerable<RestaurantDto>> GetAll()
        {


            var restaurantsDtos = _restaurantService.GetAll();

            return Ok(restaurantsDtos);
        }

        [HttpGet("{id}")]
        public ActionResult<RestaurantDto> Get([FromRoute] int id)
        {
            var restaurantDto = _restaurantService.GetById(id);

            if (restaurantDto is null)
            {
                return NotFound();
            }


            return Ok(restaurantDto);
        }

        [HttpDelete("{id}")]
        public ActionResult Delete([FromRoute] int id)
        {
            var isDeleted = _restaurantService.Delete(id);
            
            if (!isDeleted)
                return NotFound();

            return NoContent();
        }

        [HttpPut("{id}")]
        public ActionResult UpdateRestaurant([FromRoute] int id, [FromBody] UpdateRestaurantDto dto)
        {
            if(!ModelState.IsValid)
                return BadRequest(ModelState);

            var updated = _restaurantService.Update(id, dto);

            if (!updated)
                return NotFound();

            return Ok();
        }


    }
}

Now I am trying to force throwing the exception in action of the controller because of impossible access to database (exception is thrown inside the service). The thing is that my Middleware does not handle the exception and according to my knowledge it should handle exceptions globally. What am I doing wrong?

I am actually new to middleware and I don't know how to troubleshoot. Even the concept of one Program.cs instead of full-code Program.cs+Startup.cs is kind of mistery for me as of now.

0

There are 0 answers