0

I am trying to identify the issue I am facing here with my middleware injection. I have added an 'Audit' trail to my application by forwarding requests via HttpContext to my SQL Server DB which then can be viewed by admins in my application under my Events Controller/View.

My StartUp.cs is as follows:

using AWS_Service_Catalog.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.EntityFrameworkCore.SqlServer;
using Microsoft.EntityFrameworkCore;
using AWS_Service_Catalog.Models;
using AWS_Service_Catalog.Data;
using AWS_Service_Catalog.Middleware;

namespace AWS_Service_Catalog
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDistributedMemoryCache();

            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => false;
                options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
                // Handling SameSite cookie according to https://learn.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1
                options.HandleSameSiteCookieCompatibility();
            });

            services.AddOptions();

            //Adds authentication/authorization via Microsoft Azure AD
            services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAd")
                     .EnableTokenAcquisitionToCallDownstreamApi(Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' '))
                     .AddDownstreamWebApi("AWSServiceCatalogAPI", Configuration.GetSection("DownstreamApi:Scopes"))
                     .AddInMemoryTokenCaches();

            services.AddApiService(Configuration);

            services.AddControllersWithViews(options =>
            {
                var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();
                options.Filters.Add(new AuthorizeFilter(policy));
            }).AddMicrosoftIdentityUI();

            services.AddRazorPages();

            services.AddDbContext<EventLogContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("MicrosoftSQLServer")));

            //Configuring appsettings section AzureAd, into IOptions
            services.AddOptions();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                //Production Exception Handler ex: API connection failed will trigger exception routed to /Home/Error
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            //Handles User Error: 401, 403, 404, etc. Errors caught must land Application side. Errors occured in API with return 500 and be routed via Exception Handler
            app.UseStatusCodePagesWithReExecute("/Home/Error", "?status={0}");

            app.UseHttpException();

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseRouting();

            //Must include Authentication/Authorization under routing
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEventLogCaptureMiddleware();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Server}/{action=Index}/{id?}");
                endpoints.MapRazorPages();
            });
        }
    }
}

My EventLogCaptureMiddleware class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Identity.Web;
using AWS_Service_Catalog.Models;
using AWS_Service_Catalog.Data;

namespace AWS_Service_Catalog.Middleware
{
    // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
    public class EventLogCaptureMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly EventLogContext _context;

        public EventLogCaptureMiddleware(RequestDelegate next, EventLogContext context)
        {
            _next = next;
            _context = context;
        }

        public Task Invoke(HttpContext httpContext)
        {
            string role;
            try
            {
                role = httpContext.User.Claims.ToArray()[5].Value;
            }
            catch
            {
                role = null;
            }
            var eventLogModel = new EventLogViewModel
            {
                Timestamp = DateTime.Now,
                Role = role,
                Method = httpContext.Request.Method,
                Upn = httpContext.User.Identity.Name,
                Resource = $"{httpContext.Request.Scheme}://{httpContext.Request.Host}{httpContext.Request.Path}{httpContext.Request.QueryString}"
            };
            _context.Add(eventLogModel);
            var tasks = new Task[] { _context.SaveChangesAsync() };

            Task.WaitAll(tasks);

            return _next(httpContext);
        }
    }

    // Extension method used to add the middleware to the HTTP request pipeline.
    public static class EventLogCaptureMiddlewareExtensions
    {
        public static IApplicationBuilder UseEventLogCaptureMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<EventLogCaptureMiddleware>();
        }
    }
}

I am failing to understand the exact error or the cause. I have looked at this question posted: Cannot resolve scoped service from root provider .Net Core 2

Some elaboration would be wonderful. I can't identify how to resolve this issue until I know what it is. What is interesting to me, this error only occurs when I run my application in IIS Express. Otherwise there seems to be no issue.

Thoughts?

Noah Eldreth
  • 77
  • 10
  • Can you please post full exception message? – Guru Stron Jun 02 '21 at 11:32
  • You dont added the EventLogCaptureMiddleware to the service container in the ConfigureServices method. See: https://learn.microsoft.com/pt-br/aspnet/core/mvc/controllers/filters?view=aspnetcore-5.0 – MestreDosMagros Jun 02 '21 at 11:50
  • 1
    Inject `EventLogContext` into `Invoke` instead of the constructor. See [Middleware dependencies](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-5.0#middleware-dependencies). It's likely you only see the warning under IIS express because that's running it in `Development`, which performs this "check" for you. – Kirk Larkin Jun 02 '21 at 12:48
  • I did as you said @KirkLarkin and that fixed my issue. Thank you. – Noah Eldreth Jun 02 '21 at 13:42
  • @MestreDosMagros if I do not register my DB context there where can I configure my DB Connection? – Noah Eldreth Jun 02 '21 at 13:44
  • The exception was as follows @GuruStron "Cannot resolve scoped service 'AWS_Service_Catalog.Data.EventLogContext' from root provider." – Noah Eldreth Jun 02 '21 at 13:45

0 Answers0