0

I have a SingleTon Service that receives an HttpContext. The service receives the HttpContext as a parameters and whenever I try to access the HttpContext I am getting an error: ObjectDisposedException.

When I make use of a Dependency Injection (second approach) in order to get the HttpContext, then the HttpContext is null.

On both occasions the method is accessed from a Controller action method.

Stack Trace HttpConext passed as a parameter:

Exception has occurred: CLR/System.ObjectDisposedException Exception thrown: 'System.ObjectDisposedException' in Microsoft.AspNetCore.Http.Features.dll: 'IFeatureCollection has been disposed.' at Microsoft.AspNetCore.Http.Features.FeatureReferences1.ThrowContextDisposed() at Microsoft.AspNetCore.Http.Features.FeatureReferences1.ContextDisposed() at Microsoft.AspNetCore.Http.Features.FeatureReferences1.Fetch[TFeature,TState](TFeature& cached, TState state, Func2 factory) at Microsoft.AspNetCore.Http.Features.FeatureReferences1.Fetch[TFeature](TFeature& cached, Func2 factory) at Microsoft.AspNetCore.Http.DefaultConnectionInfo.get_RemoteIpAddress() at myapp.Areas.Auth.Service.AuthorizeService.d__5.MoveNext() in

Startup SingleTon added

// snippet
services.AddHttpContextAccessor();
services.AddSingleton<IAuthorizeService, AuthorizeService>();

The Service Implementation

// The Service Implementation
public interface IAuthorizeService
{
       public Task AuthorizeUserAsync(UserModel user, HttpContext context);
}


public async Task AuthorizeUserAsync(UserModel user, HttpContext context) 
{
    await some Database operation, retrieving user roles 
    // do something with the context
    // e.g.                 
    context.Response.Cookies.Append("access_token", token, GetCookieOptions()); 
} 

Updated: Controller using the Service

private readonly IAuthorizeService authorizeService;

public LoginController(IAuthorizeService authorizeService)
{
        this.authorizeService = authorizeService;
}

public async Task<IActionResult> LogIn(LogInModel model, string actionType)
{
    // snip (model and userService not shown) 
    var user = await userService.Authenticate(model.Email, model.Password, model.OTP);

    await authorizeService.AuthorizeUserAsync(user, HttpContext);
    return RedirectToAction("Index", "Home", new { area = "" });
}

Another approach: using Dependency Injection (DI), by injecting IHttpContextAccessor also does not work:

In the default constructor I have this in the AuthorizeService:

    private readonly IHttpContextAccessor contextAccessor;
    
    public AuthorizeService(IHttpContextAccessor contextAccessor, {
        this.contextAccessor = contextAccessor;
    }

In the Startup.cs I add the IHttpContextAccessor

services.AddHttpContextAccessor();

When I inspect the values of contextAccessor:

default constructor = HttpContext is not null

AuthorizeUserAsync method = HttpContext is null

I am expecting to have a non null value for HttpContext.

public async Task AuthorizeUserAsync(UserModel user) 
{
    if (contextAccessor.HttpContext == null) {
        throw new Exception("ContextAccessor.HttpContext");
    }
    // await some Database operation, retrieving user roles
    contextAccessor.HttpContext.Response.Cookies.Append("access_token", token, GetCookieOptions()); 
} 
Wayne
  • 3,359
  • 3
  • 30
  • 50
  • Please check the Startup.ConfigureServices method, the `services.AddHttpContextAccessor();` should be before the `services.AddSingleton();`. Besides, in the LoginController, try to use: `private readonly IAuthorizeService authorizeService; private readonly IHttpContextAccessor contextAccessor; public LoginController(IAuthorizeService authorizeService, IHttpContextAccessor contextaccessor) { authorizeService = authorizeService; contextAccessor = contextaccessor;}`, then, use the service as below: `authorizeService.AuthorizeUserAsync(user, contextAccessor);` – Zhi Lv Feb 01 '21 at 09:06
  • What is `AuthUser` in your first approach? While you await `AuthUser` it's inclear if it awaits your service. If passing `HttpContext` as method parameter and getting disposed exception it may indicate that you are not awaiting the results within the `AuthUser` method (action ends, before the async methods are done) – Tseng Feb 01 '21 at 09:26
  • @Zhi thanks for that; I tried that approach. Also the sequence is correct; I also found that while in the constructor of the LoginController I have HttpContext; But when the Login Action method is called, then the HttpContext is null. This is so strange and I cannot spot the error yet. – Wayne Feb 01 '21 at 09:33
  • @Tseng I have updated the code to be more clear, and also to align with my local refactoring. – Wayne Feb 01 '21 at 09:43
  • @ZhiLv with your suggestion; I can see that when the `[HttpPost] [ValidateAntiForgeryToken] [AllowAnonymous] public async Task LogIn(LogInModel model, string actionType)` is called the contextAccessor.HttpContext is null. my Startup Has this: `services.AddHttpContextAccessor(); services.AddSingleton();` – Wayne Feb 01 '21 at 09:49
  • I have noticed the following that if I `await Task.Delay(5000);` and inspect the HttpContext again, the HttpContext becomes null. – Wayne Feb 01 '21 at 11:47
  • Hi @Wayne, When and where you add the `await Task.Delay(5000);`? If remove it whether the httpcontext is not null? According to your code, I tried to create a custom IAuthorizeService, and use the AddSingleton() method to add the service, but can't reproduce the problem. the http context is not null. – Zhi Lv Feb 02 '21 at 08:05
  • Hi @ZhiLv, I have two awaits in the Controller, first one does some `DB related stuff`, and is too much detail to post, second one is the `Delay`. I do see some similar post here: https://stackoverflow.com/questions/19509672/why-is-httpcontext-current-null but not sure if this is relevant to .NET Core 3.1. – Wayne Feb 02 '21 at 09:44
  • Hi @Wayne, I'm not sure about that. You could try to test the solution in your application or try to remove the async/await. Besides, I also search some resource with the same error, it seems that it might also relate to the HttpContext is not yet initialized in the constructor or not configure in the Configure method. So, if still not working, I suggest you could post the details steps (with related code and the Startup.cs code) to reproduce the problem. – Zhi Lv Feb 03 '21 at 07:00
  • Thanks @ZhiLv I will see if I can isolate the cause. – Wayne Feb 03 '21 at 18:49

3 Answers3

0

The code supplied was not sufficient to indicate what the cause was, but it could still be useful to others.

The request/response was closed by some buggy middleware.

After removing the middleware that was closing the requests the HttpContext was valid in all cases.

Wayne
  • 3,359
  • 3
  • 30
  • 50
0

I think you have a few issues here, all centering around this fact...

HttpContext is only valid in between receiving a request and before the response is complete.

Problem 1: Injecting HttpContext into a Singleton

A singleton is only created once. However, HttpContext is unique per request. Therefore, services which depend on HttpContext should not be Singletons. They should use .AddScoped(...) instead.

Otherwise, HttpContext is only correct for the first request.

Injecting IHttpContextAccessor instead of HttpContext should work, however, because the context instance will always be for the current request.

Problem 2: Using HttpContext too late.

HttpContext cannot be used after the response has been completed. The following situations can lead to this...

  • Attempting to utilize after a previous middleware has already short-circuited the pipeline
  • Attempting to use it asynchronously, such that the response completes before asynchronous work can use it.
0

This happened to me using the IHttpContextAccessor because I forgot I was testing in my local IIS and I forgot to shut it down when I was done. After I stopped the app pool and website, I had no problem debugging my app again in Visual Studio.

Post Impatica
  • 14,999
  • 9
  • 67
  • 78