System.ObjectDisposedException: запрос завершился, а HTTPContext расположен при выполнении запросов в задаче.C#

Место общения программистов C#
Ответить
Anonymous
 System.ObjectDisposedException: запрос завершился, а HTTPContext расположен при выполнении запросов в задаче.

Сообщение Anonymous »

Я пытаюсь создать тест, где я могу моделировать несколько пользователей одновременно передавать запросы через мое пользовательское промежуточное программное обеспечение. Я надеялся, что смогу создать несколько одновременных запросов, используя TestServer , если я обернул свои запросы в отдельные задачи и вызовов. В основном это работает, за исключением того, что в моем промежуточном программном обеспечении я делаю вызовы в контекст.

Код: Выделить всё

    fail: [namespace].[mycustommiddleware][0]
Request has finished and HttpContext disposed.
Object name: 'HttpContext'.
System.ObjectDisposedException: Request has finished and HttpContext disposed.
Object name: 'HttpContext'.
at Microsoft.AspNetCore.Http.DefaultHttpContext.ThrowContextDisposed()
at Microsoft.AspNetCore.Http.DefaultHttpContext.get_Features()
at Microsoft.AspNetCore.Authentication.RequestPathBaseCookieBuilder.Build(HttpContext context, DateTimeOffset expiresFrom)
at Microsoft.AspNetCore.Http.CookieBuilder.Build(HttpContext context)
at Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler.BuildCookieOptions()
at Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler.HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
at Microsoft.AspNetCore.Authentication.AuthenticationService.SignInAsync(HttpContext context, String scheme, ClaimsPrincipal principal, AuthenticationProperties properties)
< /code>
Далее приведен мой код, настраивая задачи и создание запросов.[Test]
public async Task KeepMvcSessionAliveMiddleware_ShouldNotMixTokens()
{

// Arrange
var baseUrl = $"http://localhost:{5050}";

var companies = new[] { "ACME", "Globex", "Initech", "Umbrella", "WayneTech" };
var usersPerCompany = 3;

var userPool = companies
.SelectMany(company => Enumerable.Range(1, usersPerCompany).Select(i =>
{
var tokenDescriptor = GetTokenDescriptor(company);
var refreshTokenDescriptor = GetRefreshTokenDescriptor();

return new
{
UserName = $"{company}_User{i}",
Company = company,
Token = _tokenHandler.CreateToken(tokenDescriptor),
RefreshToken = _tokenHandler.CreateToken(refreshTokenDescriptor)
};
}))
.ToArray();

using (var warmupHandler = new HttpClientHandler { CookieContainer = new CookieContainer() })
using (var warmupClient = new HttpClient(warmupHandler) { BaseAddress = new Uri(baseUrl) })
{
var warmupCookie = CreateCookie("WarmUp", "Test", _dataProtectionProvider);
warmupHandler.CookieContainer = warmupCookie;
await warmupClient.GetAsync("/warmup");
}

// Act
var tasks = Enumerable.Range(1, 50).Select(async i =>
{
var random = new Random();
var user = userPool[random.Next(userPool.Length)];

using (var handler = new HttpClientHandler
{
CookieContainer = CreateCookie(user.Company, user.UserName, _dataProtectionProvider)
}) using (var client = new HttpClient(handler) { BaseAddress = new Uri(baseUrl) })
{
handler.CookieContainer.Add(new Cookie("RefreshToken", user.RefreshToken, "/", "localhost"));
handler.CookieContainer.Add(new Cookie("Token", user.Token, "/", "localhost"));
var task = client.GetAsync("/");
_activeRequests.Add(task);
var response = await task;
response.EnsureSuccessStatusCode();

Assert.That(response, Is.Not.Null);
}

});

await Task.WhenAll(tasks.ToArray());

foreach (var log in _loggerProvider.Logs)
{
TestContext.WriteLine(log);
}

}
Для меня кажется, что httpcontext используется между каждым созданным клиентом, и последующие запросы используют httpcontext , который был расположен в предыдущем запросе. Есть ли что -нибудь, что я могу сделать, чтобы смягчить httpcontext из утилизации между моими запросами? Я чувствую, что есть что-то фундаментальное, что я не понимаю с тем, как я пытаюсь использовать TestServer .
Изменить:
Вот метод Invokeasync из промежуточного программного обеспечения:
public async Task InvokeAsync(HttpContext context)
{
try
{
var jwtExp = _configuration.GetValue("JWT:Expiration", 2);

if (context.Response.HasStarted) return;
// Check if the user is authenticated
if (!RequestingStaticFile(context) && context.User.Identity!.IsAuthenticated)
{

var model = GetTokenCookies(context);

if (string.IsNullOrEmpty(model.RefreshToken) ||
(await IsRefreshTokenExpired(model.RefreshToken)))
{
_logger.Log(LogLevel.Information, $"Refresh token expired or missing. Logging out user: {context.User.Identity.Name}");
// Refresh token has expired. End the session
await context.SignOutAsync();
// Call the next middleware in the pipeline (which will be YARP)
await _next(context);

return;
}

// Check the cache for the session timeout value
var companyName = context.User.Claims.FirstOrDefault(c => c.Type == "Company")?.Value.Replace(" ", string.Empty) ?? "Company";
var cacheKey = $"SessionTimeout_{companyName}_{context.User.Identity.Name}";

// Attempt to retrieve the cached sessionTimeout value
int sessionTimeout = await _memoryCache.GetOrCreateAsync(cacheKey, async entry =>
{

entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(3);
entry.SlidingExpiration = TimeSpan.FromMinutes(2);

var token = context.Request.Cookies["Token"];
var companySettingsResult = await _companyService.GetCompanySettingsAsync(token ?? string.Empty);
return companySettingsResult?.Data?.SessionTimeout ?? 60;

});

var authResult = await context.AuthenticateAsync("LegacyAuth");

if (authResult.Succeeded && authResult.Properties != null)
{
authResult.Properties.AllowRefresh = true;
authResult.Properties.ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromMinutes(sessionTimeout));

// Sign in to refresh the authentication cookie
await context.SignInAsync("LegacyAuth", authResult.Principal, authResult.Properties);

await TryRefreshTokensIfNeededAsync(context, model, companyName, jwtExp);

}
}

// Continue to the next middleware in the pipeline
await _next(context);
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, ex.Message);
}

}
< /code>
Я использую NUNIT в качестве структуры тестирования. Я отлаживал и с тех пор ушел от TestServer и получаю те же результаты с помощью сервера Kestrel.

Код: Выделить всё

return Host.CreateDefaultBuilder()
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddProvider(loggerProvider);
logging.SetMinimumLevel(LogLevel.Debug);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseKestrel()
.UseUrls($"http://localhost:{port}")
.ConfigureServices(services =>
{
services.AddAuthentication("LegacyAuth")
.AddCookie("LegacyAuth", options =>
{
options.Cookie.Name = "Test";
options.Events.OnValidatePrincipal = async context =>
{
// Simulate successful validation (optional)
await Task.CompletedTask;
};
});

services.AddAuthorization();

configureServices?.Invoke(services);
})
.Configure(app =>
{
app.UseAuthentication();
app.UseAuthorization();

configureApp(app);
});
})
.Build();
< /code>
[OneTimeSetUp]
public async Task OneTimeSetUp()
{
_tokenServiceMock = new Mock();
_loggerProvider = new TestLoggerProvider();

_testHost = TestHostBuilder.BuildHost(5050, app =>
{
app.UseMiddleware();

app.Run(async ctx =>
{
await ctx.Response.WriteAsync("OK");
});

}, services =>
{
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();

services.AddSingleton();
services.AddSingleton();
services.AddSingleton(_tokenServiceMock.Object);
services.AddHttpClient();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton(provider => new JwtValidatorService(new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "Test",
ValidAudience = "Test",
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes("88888888-4444-4444-4444-222222222222-88888888-4444-4444-4444-222222222222"))
}));
}, _loggerProvider);

await _testHost.StartAsync();

_dataProtectionProvider = _testHost.Services.GetRequiredService();
}
Благодаря отладке я обнаружил, что контекст выбрасывает ObjectDisposedException из строки в промежуточном программном обеспечении: await context.signisync ("Legacyauth", autresult.principal, autresult.properties); . Это было бы потому, что промежуточное программное обеспечение пытается обновить заголовки в запросе, который утилизировал свой контекст.


Подробнее здесь: https://stackoverflow.com/questions/797 ... isposed-wh
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»