.NET Core 3 preview: Synchronous operations are disallowed - angularjs

I have an Angular.js app that I am porting to .NET Core.
It was working fine in the previous version of .NET Core 3 preview; 3.2.
However, after upgrading to latest 3.3 some of the get requests are returning this error:
InvalidOperationException: Synchronous operations are disallowed. Call
WriteAsync or set AllowSynchronousIO to true instead.
I can't see why this is happening with only some requests and not others.
I believe that by default Angular.js does async: xhr.open(method, url, true);
Can anyone shed some light on this?

This problem is described here: https://github.com/aspnet/AspNetCore/issues/8302
The workaround for now is to manually set AllowSynchronous to true in startup.cs;
// Startup.ConfigureServices
services.Configure<IISServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});

It's worth noting that if you host on kestrel directly then your Program.cs should have appropriate ConfigureKestrel call
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureKestrel((context, options) =>
{
options.AllowSynchronousIO = true;
})

you can disable it for a special method
var syncIOFeature = HttpContext.Features.Get<IHttpBodyControlFeature>();
if (syncIOFeature != null)
{
syncIOFeature.AllowSynchronousIO = true;
}
or disable in all application scope
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureKestrel((context, options) =>
{
options.AllowSynchronousIO = true;
})
or in service configure startup
services.Configure<IISServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});

If you are using a CustomWebApplicationFactory like me, you can set the flag in its constructor, It makes my test direct from VS2019 works.
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup: class
{
public CustomWebApplicationFactory()
{
Server.AllowSynchronousIO = true;
}

Related

using AbpApplicationFactory.Create overwrites IConfiguration

When using Abp to run a HostedService the IConfiguration created in the main method gets overwritten when using AbpApplicationFactory.Create
The main methods uses the default Microsoft implementation to connect to the AzureKeyvault:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging((context, logging) => logging.ClearProviders())
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<DbMigratorHostedService>();
})
.ConfigureAppConfiguration((context, config) =>
{
if (context.HostingEnvironment.IsProduction())
{
var builtConfig = config.Build();
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(
azureServiceTokenProvider.KeyVaultTokenCallback));
config.AddAzureKeyVault(
$"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
keyVaultClient,
new DefaultKeyVaultSecretManager());
}
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
For now I am using the default DbMigrator that is added when creating an abp module:
public class DbMigratorHostedService : IHostedService
{
private readonly IHostApplicationLifetime _hostApplicationLifetime;
public DbMigratorHostedService(IHostApplicationLifetime hostApplicationLifetime)
{
_hostApplicationLifetime = hostApplicationLifetime;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
using var application = AbpApplicationFactory.Create<MyDbMigratorModule>(options =>
{
options.UseAutofac();
options.Services.AddLogging(c => c.AddSerilog());
});
application.Initialize();
await application
.ServiceProvider
.GetRequiredService<MyDbMigrationService>()
.MigrateAsync();
application.Shutdown();
_hostApplicationLifetime.StopApplication();
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
I can see that the AbpApplicationFactory.Create method also has options. However this doesn't have anything to configure the configuration. Why is the application scope not using the same IConfiguration from outside the scope ? Outside the scope i got 6 configuration providers including the key vault configuration and inside the scope there are only 2 missing the Keyvault one.
Am I missing something here ?
I also tried to get rid of the application scope, then i get the right configuration. However then I can't call the initialize method on the application and I can't use a startup class since it isn't a web application.
You are right. The startup template is not designed to handle such cases. I created an issue to fix it: https://github.com/abpframework/abp/issues/5006
For now, you can replace the configuration with your custom built one:
I din't try it, but this will also probably work (and it is exactly what you want):
inject IConfiguration to the hosted service.
Pass it to the ReplaceConfiguration.

Nancy request tracing does not show anything

After many trials, I can't seem to get Nancy's Diagnostic Tracing to show anything beyond a set of empty white boxes. It appears one box per browser session. Yet the boxes never include any information.
Per the diagnostics page I've added a diagnostics password and set
StaticConfiguration.EnableRequestTracing = true;
I expected there to be at some sort of default tracing out of the box, but just in case I made a call to
this.Context.Trace.TraceLog.WriteLog(x => x.AppendLine("What's ip?"));
in one of my modules, and confirmed said module route was getting called. Still no indication there was tracing going on.
We're running NancyFx version 1.45 in a framework 4.7.1 app. Any suggestions on why this could be happening?
Thanks
Enable tracing and diagnostics by using a custom bootstrapper.
For NancyFx v1:
NancyOptions options = new NancyOptions
{
Bootstrapper = new CustomBootstrapper()
};
app.UseNancy(options);
public class CustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
base.ApplicationStartup(container, pipelines);
StaticConfiguration.DisableErrorTraces = false;
StaticConfiguration.EnableRequestTracing = true;
if (!StaticConfiguration.EnableRequestTracing)
{
DiagnosticsHook.Disable(pipelines);
}
}
protected override DiagnosticsConfiguration DiagnosticsConfiguration => new DiagnosticsConfiguration() { Enabled = true, Password = #"A2\6mVtH/XRT\p,B" };
}
For NancyFx v2:
NancyOptions options = new NancyOptions
{
Bootstrapper = new CustomBootstrapper()
};
app.UseNancy(options);
public class CustomBootstrapper : DefaultNancyBootstrapper
{
public override void Configure(INancyEnvironment environment)
{
environment.Diagnostics(
enabled: true,
password: #"A2\6mVtH/XRT\p,B");
environment.Tracing(
enabled: true,
displayErrorTraces: true);
}
}

Cannot get an IdentityServer4 custom cookie handler working

I'm unable to get a custom cookie authentication handler working with IdentityServer4. I'm using ASP.NET Core Identity and have followed the official guide: https://identityserver4.readthedocs.io/en/release/topics/signin.html
I need to override the CookieAuthenticationEvents.ValidatePrincipal and CookieAuthenticationEvents.SignedIn event handlers.
I've written a class that inherits CookieAuthenticationEvents and overrides the two event handlers.
I'm assigning it to a custom cookie handler via:
var auth = services.AddAuthentication("MyCookies");
auth.AddCookie("MyCookies", options =>
{
options.Events = new RealtimeStatusCookieAuthEvents(Configuration);
});
Here's my code:
https://gist.github.com/Amethi/f3411038a9447d274c0b721698fc5e63
The event handlers don't fire, i.e. I'm expecting them to fire for each request (due to ValidatePrincipal) and when I come back to the site after closing the browser and sign-in using cookie authentication (SignedIn).
Anyone know what I'm doing wrong?
Update:
Even simplifying it as follows doesn't help. The event handlers don't fire.
var auth = services.AddAuthentication("CustomCookies").AddCookie("CustomCookies", options =>
{
options.Events = new CookieAuthenticationEvents
{
OnSignedIn = context =>
{
Console.WriteLine("{0} - {1}: {2}", DateTime.Now,
"OnSignedIn", context.Principal.Identity.Name);
return Task.CompletedTask;
},
OnValidatePrincipal = context =>
{
Console.WriteLine("{0} - {1}: {2}", DateTime.Now,
"OnValidatePrincipal", context.Principal.Identity.Name);
return Task.CompletedTask;
},
};
});
I managed to make my custom cookie authentication handler work by using the ConfigureApplicationCookie extension.
builder.Services.ConfigureApplicationCookie(config =>
{
config.Cookie.Name = "IdentityServer.Cookie";
config.EventsType = typeof(CustomCookieAuthenticationHandler);
config.LoginPath = "/Account/Login";
});
And register the CustomCookieAuthenticationHandler handler
builder.Services.AddScoped<CustomCookieAuthenticationHandler>();
This is the handler implementation:
public class CustomCookieAuthenticationHandler: CookieAuthenticationEvents
{
private readonly IUserRepository _userRepository;
public CustomCookieAuthenticationEvents(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public override Task ValidatePrincipal(CookieValidatePrincipalContext context)
{
// Your cookie authentication logic.
}
}
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-6.0

Infinite authentication loop when using identityserver4 in asp.net core 2.0

I have an Identity Server using identityserver4 framework, its url is http://localhost:9000
My web application is asp.net core 2.0, its url is http://localhost:60002. This application will use the login page of Identity Server.
I want after logging in, the Identity Server will redirect to the application page (http://localhost:60002)
Here is the Startup.cs of client application
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
private string AuthorityUri => Configuration.GetValue<string>("UserManagement-Authority");
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = AuthorityUri; // "http://localhost:9000"
options.RequireHttpsMetadata = false;
options.ClientId = "customer.api";
options.ClientSecret = "testsecret";
options.ResponseType = "code id_token";
options.Scope.Add("customerprivatelinesvn.api");
options.Scope.Add("offline_access");
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
});
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
}
}
Here is the loggin page on Identity Server
But there is an infinite loop that calls to http://localhost:9000/connect/authorize endpoint, and then it returns to http://localhost:60002/signin-oidc with "Bad Request - Request Too Long" as below.
When I look at the cookies, there ar lots of items ".AspNetCore.Correlation.OpenIdConnect.xxx"
Here is the log on Identiy Server. It said that Identiy.Application was successfully authenticated.
Does anyone know what this problem is? And how to resolve this? Thank you very much.
Best regards,
Kevin
I also had a login loop after copying the startup code from an existing .NET Core 2.2 project and reused it in a new .NET Core 3.1 project.
The problem here was, that the app.UseAuthentication() must be called before the new app.UseAuthorization();
https://learn.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#migrate-startupconfigure
Only in case someone is running into this issue too...
Adding default Identity in the client app would cause an infinite redirect loop.
In the client app, if you need to use UserManager, RoleManager.
Then use the below code.
services.AddIdentityCore<IdentityUser>()
.AddRoles<IdentityRole>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddSignInManager<SignInManager<IdentityUser>>()
.AddEntityFrameworkStores<ApplicationDbContext>();
In your client app, in Startup check if you have something like
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
Remove that part and try again.
In my case, I was missing RedirectUri when initiating the Signin from the client. Problem solved by adding the RedirectUri as below.
public IActionResult SignIn()
{
return Challenge(new AuthenticationProperties() { RedirectUri = "/" }, "oidc" );
}
Well, you do have a very long request shown there in your Identity Server log - and the error says "Bad Request - request too long". I'd guess that the problem is that your request is too big :)
maximum length of HTTP GET request?
Have you tried posting rather than using a GET?
This issue was solved after I updated the latest nuget package of IdentityServer4 and .NET Core.

Exception thrown when using TaskSourceCompletion in Silverlight 5

I'm trying to wrap the Event Async Programming model used in RIA Services in a Task.
I have followed the standard way of using a TaskCompletionSource and implemented the following extension method:
public static Task<IEnumerable<T>> LoadAsync<T>(this DomainContext source, EntityQuery<T> query) where T : Entity
{
TaskCompletionSource<IEnumerable<T>> taskCompletionSource = new TaskCompletionSource<IEnumerable<T>>();
source.Load(
query,
loadOperation =>
{
if (loadOperation.HasError && !loadOperation.IsErrorHandled)
{
taskCompletionSource.TrySetException(loadOperation.Error);
loadOperation.MarkErrorAsHandled();
}
else if (loadOperation.IsCanceled)
{
taskCompletionSource.TrySetCanceled();
}
else
{
taskCompletionSource.TrySetResult(loadOperation.Entities);
}
},
null);
return taskCompletionSource.Task;
}
I then use this in the following way:
var task = _context.LoadAsync(_context.GetPlayersQuery());
task.Start();
task.Result;
The problem though is that I get an InvalidOperationException stating that "Start may not be called on a promise-style task". I have tried not starting the task, but then the loadOperation callback never fires.
Can anyone see what I am doing wrong here?
Thanks in advance
Problem is solved. Under the hood the DomainContext.Load() method is already operating in an asynchronous manner. There must have been some conflict with trying to wrap an already asynchronous method in a task.
However, even if I still follow the EAP correctly with the code below, I still get the InvalidOperationException of 'start cannot be called on a promise-style task'
public static Task<IEnumerable<T>> LoadAsync<T>(this DomainContext source, EntityQuery<T> query) where T : Entity
{
TaskCompletionSource<IEnumerable<T>> taskCompletionSource = new TaskCompletionSource<IEnumerable<T>>();
var loadOperation = source.Load(query);
loadOperation.Completed += (obj, args) =>
{
if (loadOperation.HasError && !loadOperation.IsErrorHandled)
{
taskCompletionSource.TrySetException(loadOperation.Error);
loadOperation.MarkErrorAsHandled();
}
else if (loadOperation.IsCanceled)
{
taskCompletionSource.TrySetCanceled();
}
else
{
taskCompletionSource.TrySetResult(loadOperation.Entities);
}
};
return taskCompletionSource.Task;
}
Try this instead
var result = await _context.LoadAsync(_context.GetPlayersQuery());
Try using
task.ContinuewWith(Action<Task<T>> continuation)
That worked for me, as I too got that exception when using task.Start

Resources