ASP.NET Core : Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead
asp.net Mvcasp.net Mvc Problem Overview
ASP.NET core server, AllowSynchronousIO
is set to false
new WebHostBuilder()
.UseKestrel(options =>
{
options.AllowSynchronousIO = false;
})
In the action, it outputs a JsonResult
public async Task<IActionResult> SanityCheck()
{
Dictionary<string, string> dic = await GetDic();
return this.Json(dic);
}
And it ends with an exception
> System.InvalidOperationException: Synchronous operations are > disallowed. Call WriteAsync or set AllowSynchronousIO to true instead.
Can't I return a JsonResult with AllowSynchronousIO=false
?
asp.net Mvc Solutions
Solution 1 - asp.net Mvc
You might have the following problem: https://github.com/aspnet/AspNetCore/issues/8302
And you can find more info here: https://github.com/aspnet/AspNetCore/issues/7644
A workaround until the issue is being solved is to allow Synchronous IO. Put this in Startup.cs for either Kestrel or IIS:
public void ConfigureServices(IServiceCollection services)
{
// If using Kestrel:
services.Configure<KestrelServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
// If using IIS:
services.Configure<IISServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
}
Solution 2 - asp.net Mvc
When the exception is thrown in code you cannot control, and you have no other choice than to enable AllowSynchronousIO
, it is best to enable it for specific requests, instead of globally.
In the GitHub issue announcing this feature, the following workaround is suggested:
var syncIOFeature = HttpContext.Features.Get<IHttpBodyControlFeature>();
if (syncIOFeature != null)
{
syncIOFeature.AllowSynchronousIO = true;
}
You can create a simple middleware function to apply this to specific requests:
app.Use(async (context, next) =>
{
if (context.Request.Path.StartsWithSegments("/some-endpoint-that-needs-sync-io"))
{
var syncIoFeature = context.Features.Get<IHttpBodyControlFeature>();
if (syncIoFeature != null)
{
syncIoFeature.AllowSynchronousIO = true;
}
}
await next();
})
Solution 3 - asp.net Mvc
I had this issue with my unit tests. I had to update my TestServer to AlloSynchronousIO
Server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
Server.AllowSynchronousIO = true;
Solution 4 - asp.net Mvc
I managed to find my own unique version of this problem with the following middleware (.NET 6.0):
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
context.Response.StatusCode = 200;
using (var writer = new StreamWriter(context.Response.Body))
{
await writer.WriteLineAsync("Done!");
return;
}
}
I spent a long time staring at this until I realised what the stack trace was telling me:
> System.InvalidOperationException: Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead. > > at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseStream.Write(Byte[] buffer, Int32 offset, Int32 count) > > at System.IO.Stream.Write(ReadOnlySpan`1 buffer) > > at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder) > > at System.IO.StreamWriter.Dispose(Boolean disposing) > > at System.IO.TextWriter.Dispose()
The important line here was the System.IO.TextWriter.Dispose()
: this is causing the resulting flush and lower-level write to be called synchronously.
Fortunately, StreamWriter
implements IAsyncDisposable
so it's easy to solve it by adding await
before using
:
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
context.Response.StatusCode = 200;
await using (var writer = new StreamWriter(context.Response.Body))
{
await writer.WriteLineAsync("Done!");
return;
}
}
Hopefully this helps someone not waste as much time as I did.
Solution 5 - asp.net Mvc
Based on Mark Lagendijk's answer, i applied that logic on a declarative way with an attribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AllowSynchronousIOAttribute : ActionFilterAttribute
{
public AllowSynchronousIOAttribute()
{
}
public override void OnResultExecuting(ResultExecutingContext context)
{
var syncIOFeature = context.HttpContext.Features.Get<IHttpBodyControlFeature>();
if (syncIOFeature != null)
{
syncIOFeature.AllowSynchronousIO = true;
}
}
}
Just use it on an action method or an entire controller to enable it:
[AllowSynchronousIO]
public IActionResult DownloadSynchronous()
{
return Something();
}
Solution 6 - asp.net Mvc
I'm not sure what your requirements are or what GetDic()
does, but code like the following should absolutely work given GetDic()
doesn't do any synchronous IO:
public async Task<IActionResult> SanityCheck()
{
Dictionary<string, string> dic = await GetDic();
return this.Ok(dic);
}
And if you still want to serialise dic
to JSON, the following code should do:
public async Task<IActionResult> SanityCheck()
{
Dictionary<string, string> dic = await GetDic();
string json = JsonConvert.SerializeObject(dic);
return this.Ok(json);
}
Note that this last piece of code returns the result as text/plain
instead of application/json
.
Also, I tested this under ASP.NET Core 2.2.
Solution 7 - asp.net Mvc
this code work for me, make async read:
public override async void OnActionExecuting(ActionExecutingContext filterContext)
{
string content = "";
var request = filterContext.HttpContext.Request;
try
{
request.EnableBuffering();
request.Body.Position = 0;
using (StreamReader reader = new StreamReader(request.Body, Encoding.UTF8,true,1024,true))
{
content = await reader.ReadToEndAsync();
}
}
finally
{
request.Body.Position = 0;
}
}
Solution 8 - asp.net Mvc
In the configure service add below code. It worked for me
services.Configure<KestrelServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
// If using IIS:
services.Configure<IISServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
Solution 9 - asp.net Mvc
Everybody else is showing how to allow synchronous IO.
In my case, the problem was that my custom stream classes were not implementing DisposeAsync
and FlushAsync
correctly.
Fixing that made the error go away.