Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HttpContext NULL issue?? System.NullReferenceException on HttpContextAccessor? Core 2.1 #3668

Closed
codeinflash opened this issue Oct 18, 2018 · 10 comments

Comments

@codeinflash
Copy link

Describe the bug

I am using asp.net core 2.1

Because HttpContext becomes null, I get System.NullReferenceException.
I have interface called 'IUserRepository' and it is implemented in Startup class.
Startup.cs:

public void ConfigureServices(IServiceCollection services) {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

			services.AddSession(options => {
                options.IdleTimeout = TimeSpan.FromHours(12);
            });
            services.AddHttpContextAccessor(); //core 2.1
            services.AddTransient<IUserRepository, UserRepository>();
        }

Then I get user's info and tells whether the user is validated user or not.
However, I get System.NullReferenceException: Object reference not set to an instance of an object.
at Services.UserRepository.GetUserNetworkId() in ...\Services\UserRepository.cs:line 122

I marked where is the line#122 in below code.
UserRepository.cs:

public class UserRepository : IUserRepository
    {
        private readonly IHttpContextAccessor _httpContextAccessor;

        public UserRepository(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        public string GetUserNetworkId()
        {
            string userName = null;
            string NetworkId = null;

            //-------------checking where fails
            if (_httpContextAccessor == null)
            {
                throw new Exception("In IsUserValidated() - Error: _httpContextAccessor == null");
            }

            if (_httpContextAccessor.HttpContext == null)
            {
                throw new Exception("In IsUserValidated() - Error: _httpContextAccessor.HttpContext == null");
            }

            if (_httpContextAccessor.HttpContext.User == null)
            {
                throw new Exception("In IsUserValidated() - Error: _httpContextAccessor.HttpContext.User == null");
            }

            if (_httpContextAccessor.HttpContext.User.Identity == null)
            {
                throw new Exception("In IsUserValidated() - Error: _httpContextAccessor.HttpContext.User.Identity == null");
            }
            //-------------checking where fails done

            var identity = _httpContextAccessor.HttpContext.User.Identity;

            if (identity.IsAuthenticated)
            {
                userName = identity.Name;
            }
            else
            {
                **** line#122 **** var basicCredentials = new BasicAuthenticationHeader(_httpContextAccessor.HttpContext);
                userName = basicCredentials.UserName;
            }

            if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development"
                && Environment.GetEnvironmentVariable("USER_NETWORK_ID") != null) // used for testing in development by setting overriding environmental varibles
            {
                NetworkId = Environment.GetEnvironmentVariable("USER_NETWORK_ID");
            }
            else
            { // assume production
                NetworkId = userName.Split("\\").Last();
            }

            return NetworkId;
        }

        public int GetUserPhoneKey()
        {
            var userInfo = UserInfo.GetUserInfo(GetUserNetworkId());
            int PhoneKey = userInfo.userPhoneKeyId;
            return PhoneKey;
        }
    }

The line#122 creates a class BasicAuthenticationHeader
and decodes AuthenticationHeader of the HttpConext.

I think (guess) there is where the HttpContext becomes Null out.
class BasicAuthenticationHeader.cs:
private readonly string _authenticationHeaderValue;

        private string[] _splitDecodedCredentials;

        public bool IsValidBasicAuthenticationHeaderValue { get; private set; }
        public string UserName { get; private set; }

		public BasicAuthenticationHeader(HttpContext context)
        {
			var basicAuthenticationHeader = context.Request.Headers["Authorization"]
				.FirstOrDefault(header => header.StartsWith("Basic", StringComparison.OrdinalIgnoreCase));

			if (!string.IsNullOrWhiteSpace(basicAuthenticationHeader))
            {
                _authenticationHeaderValue = basicAuthenticationHeader;
                if (TryDecodeHeaderValue())
                {
                    ReadAuthenticationHeaderValue();
                }
            }
        }

The more weird is that it happens at random, and I don't know why HttpContext gets lost.

Please help.

Thank you.

To Reproduce

Steps to reproduce the behavior:

  1. Using this version of ASP.NET Core '2.1'
@Tratcher
Copy link
Member

Realize that IHttpContextAccessor is a feature of last resort as it's easy to use incorrectly. Where does GetUserNetworkId get called from in your application? HttpContext is only valid during a request, and it should only be accessed on the main request thread.

@codeinflash
Copy link
Author

I have 6 controllers that implements IUserRepository like below HomeController and calls the function GetUserNetworkId()
ex) HomeController.cs:

public class HomeController : Controller
	{
        private readonly IUserRepository _userRepository;

        public HomeController(IUserRepository userRepository)
        {
            _userRepository = userRepository;
        }
	public IActionResult Index()
	{
            var userValidated = _userRepository.IsUserValidated();
            if (!userValidated)
            { ViewData["Error"] = "Please be sure you are NOT logging in with your email address. You need to use your network id.The id(" + _userRepository.GetUserNetworkId() + ") you provided was not found in the Phone Directory. Please contact the Service Desk";
            }
	    return View("vHome");
	}
}

@Tratcher
Copy link
Member

HttpContext is available in HomeController, pass it directly into GetUserNetworkId rather than relying on IHttpContextAccessor.

@poke
Copy link
Contributor

poke commented Oct 18, 2018

Or if you just want to use the ClaimsPrincipal, then just pass User to the user repository.

@codeinflash
Copy link
Author

Is passing HttpContext to a different controller or interface safe do so??

@Tratcher
Copy link
Member

You can pass it anywhere you like so long as only one thread is using it at a time and they stop using it after the end of the request.

@Eilon
Copy link
Member

Eilon commented Oct 23, 2018

Before the end of the request 😄

@blowdart
Copy link
Contributor

As this appears to be a misunderstanding of how long a context lasts for, and advice has been given, closing

@codeinflash
Copy link
Author

@blowdart Can I ask how long does Http Context last????

@Eilon
Copy link
Member

Eilon commented Nov 1, 2018

It lasts for the duration of the request, and can be used only from the request thread.

@dotnet dotnet locked as resolved and limited conversation to collaborators Dec 3, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants