How to get current User outside of Controller in another layer

mostafa ahmed 41 Reputation points
2024-09-18T22:15:23.81+00:00

In ASP.NET Core 6, I need to get the current user in service layer

This is code:

public class ServiceModel
{
    private readonly IHttpContextAccessor accessor;

    public ServiceModel(IHttpContextAccessor accessor)
    {
        this.accessor = accessor;
    }
}

But User is always Null:

var user = accessor.HttpContext?.User

Code in Program file:

builder.Services.AddIdentity<AppUser, AppRole>(options =>
{
    options.User.AllowedUserNameCharacters =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+/ ";
    options.User.RequireUniqueEmail = false;
})
.AddEntityFrameworkStores<testContext>()
.AddUserManager<UserManager<AppUser>>()
.AddClaimsPrincipalFactory<ApplicationUserClaimsPrincipalFactory>()
.AddDefaultTokenProviders();

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
    options.Cookie.Name = "MyAppCookie";
    options.LoginPath = "/Account/Login";
});

builder.Services.AddHttpContextAccessor();

This the packages installed in Service Layer:

Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.EntityFrameworkCore
Microsoft.AspNetCore.Mvc.ViewFeatures
Entity Framework Core
Entity Framework Core
A lightweight, extensible, open-source, and cross-platform version of the Entity Framework data access technology.
742 questions
.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,863 questions
ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,560 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,939 questions
{count} votes

2 answers

Sort by: Most helpful
  1. SurferOnWww 2,896 Reputation points
    2024-09-19T06:30:24.7533333+00:00

    Although I do not know how do create the object of ServiceModel class and use it, I provide a sample code assuming you want to log the username.

    Below is a sample of ASP.NET Core 6.0 MVC based on Microsoft document Access HttpContext from custom components:

    UserRepository class

    Assume this is your ServiceModel class.

    namespace MvcCore6App3.Libraries
    {
        public class UserRepository : IUserRepository
        {
            // inject IHttpContextAccessor object by DI
            private readonly IHttpContextAccessor _httpContextAccessor;
            private readonly ILogger<UserRepository> _logger;
    
            public UserRepository(IHttpContextAccessor httpContextAccessor,
                                  ILogger<UserRepository> logger)
            {
                _httpContextAccessor = httpContextAccessor;
                _logger = logger;
            }
    
            public void LogCurrentUser()
            {
                // Get login user name
                var username = _httpContextAccessor.HttpContext?
                               .User.Identity?.Name;
                if (username != null)
                {
                    _logger.LogInformation($"{username} access on {DateTime.Now}");
                }
                else
                {
                    _logger.LogInformation($"anonymous access on {DateTime.Now}");
                }
            }
        }
    
        public interface IUserRepository
        {
            public void LogCurrentUser();
        }
    }
    

    Program.cs

    Note that ASP.NET Core Identity is enabled and username is obtained form it.

    using Microsoft.AspNetCore.Identity;
    using Microsoft.EntityFrameworkCore;
    using MvcCore6App3.Data;
    using MvcCore6App3.Libraries;
    
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
    
    builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(connectionString));
    
    builder.Services.AddDatabaseDeveloperPageExceptionFilter();
    
    builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    
    builder.Services.AddControllersWithViews();
    
    // 2024/9/19 Add IHttpContextAccessor and UserReposirty to DI container
    builder.Services.AddHttpContextAccessor();
    builder.Services.AddTransient<IUserRepository, UserRepository>();
    
    // Code omitted below
    

    Controller

    using Microsoft.AspNetCore.Mvc;
    using MvcCore6App3.Models;
    using System.Diagnostics;
    
    namespace MvcCore6App3.Controllers
    {
        public class HomeController : Controller
        {
            private readonly Libraries.IUserRepository _userRepository;
    
            public HomeController(Libraries.IUserRepository userRepository)
            {
                _userRepository = userRepository;
            }
    
            public IActionResult Index()
            {
                // get current user name and log it outside of Controller
                _userRepository.LogCurrentUser();
                return View();
            }
            
            // code omitted
        }
    }
    

    Result

    enter image description here


  2. Bruce (SqlWork.com) 65,396 Reputation points
    2024-09-19T17:41:23.7666667+00:00

    for your ServiceModel to access HttpContextAccessor, it needs to be registered as a service:

    builder.Services.AddTransient<ServiceModel, ServiceModel>();

    then injected into _Layout.cshtml:

    @inject ServiceModel service

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.