Problems with ApiController in Blazor App - c#

So I'm wracking my brain here. I have a Blazor Server App that I've created, and to it I've added a few ApiControllers. I am getting what seems to me to be strange errors, only with the production environment. For some reason the Request.Body is being disposed before I ever get to read it. I've tried reading with a StreamReader, copying into a new MemoryStream, what's weird is, it will work for maybe a dozen requests, then stops working and starts throwing either "Reading not allowed after stream is completed" or "IFeatureCollection has been disposed" errors. I can get it to work flawlessly in debug mode with IIS Express in VS. But it will not work consistently on IIS server. Oddly enough, it WILL work consistently when calls are sent with Postman. Just really odd. So basically what I have as an example is, an ApiController, ProfileController, and it just has 1 POST method
[HttpPost]
public async void Post()
{
String bodyContent = String.Empty;
using(StreamReader rdr = new StreamReader(Request.Body))
{
bodyContent = await rdr.ReadToEndAsync();
}
// use body content xml, generate response xml
String responseXML = "ResponseText";
Response.ContentType = "application/xml";
Response.WriteAsync(responseXML);
}
So I get errors some times when reading the request stream above, sometimes that works then I get an error that the Response has been disposed when I try to set ContentType
Is there something about Blazor that is consuming the Request/Response streams ahead of the API routing? This just really baffles me.

// public async void Post()
public async Task Post()
async void methods are not awaitable, the aspnet framework has no way of knowing that your action is still running and returns the response immediately.
what seems to me to be strange errors
Yes, it can accidentally succeed. Threading and asynchrony errors can be very undeterministic.

Related

NullReferenceException When Trying To Reach REST Endpoint

I have a database management class which has multiple methods to reach different endpoints on a REST API. Everything appears to be working except for this one issue. When the app is started, I am trying to reach an endpoint the exact same way I reach the other endpoints. For some reason, this one completely breaks the app and throws a NullReferenceException without any information.
I have confirmed that the client is not null, and the URL and endpoint works fine in Postman. I honestly have no idea what would be causing this exception, and the fact that it doesn't show any information about the exception just makes it worse.
private async void InitDatabase()
{
HttpResponseMessage response = await client.GetAsync("System?Guid=1");
string responseString = await response.Content.ReadAsStringAsync();
SysParams sysParams = JsonConvert.DeserializeObject<SysParams>(responseString);
}
The error occurs when the first line is executed where client.GetAsync is called.
For some reason, it started working after I had made the call without awaiting it.

Asynchronous file upload response never makes it back to client (or takes longer than it should)

We are having an issue with our Application where we are never getting a response back to the client (Chrome in this case) from a pretty simple asynchronous file upload call. It also bogs down our server for up to 2 minutes. Below is our Controller method:
public async Task<HttpResponseMessage> Post(string id, string fileName)
{
string[] allowedAttachmentFileTypes = ConfigurationManager.AppSettings["AttachmentsSetting"].Split(',');
string extension = Path.GetExtension(fileName);
bool extensionAllowed = allowedAttachmentFileTypes.Any(allowedAttachmentFileType => allowedAttachmentFileType.ToLower().Trim() == extension.ToLower().Trim());
if (extensionAllowed)
{
var fileResult = await Request.Content.ReadAsStreamAsync();
//...do async database stuff with fileResult
return Request.CreateResponse(HttpStatusCode.OK, result);
}
else
{
//this never makes it back to client
var response = new HttpResponseMessage(HttpStatusCode.UnsupportedMediaType)
{
Content = new StringContent(ConfigurationManager.AppSettings["AttachmentsSetting"])
};
return response;
}
}
What I am mainly concerned with at the moment is that we are testing with what would in our system be an invalid file extension so that it would go to the else clause and just return the bad response. When we put a breakpoint here, it hits our controller and eventually hits our return in the else clause, and works as expected, but in Chrome, it still shows "Pending".
Another thing is it seems to be dependent on the size of the file we send to the Controller, even though we don't really do anything with the file unless the extension is valid. An invalid file that is 26,939KB never gives us a server response. While one that is 17,432KB gave us one, although it still took a minute.
One other thing I should add: This is more inconsistent, but at times if we do have a valid file extension on a bigger file, like say the 26,939KB one, we would get "There is no longer an HttpContext available." when trying to copy the file to the file system
This ended up being a firewall issue. Nothing code related.

Web API call works but gives exception The view 'xxx' or its master was not found or no view engine supports

Strange one here, code calls the method, and the method grabacat is executed on the server (I debug it and step through right to the end). The code returns to the client, but the response it received was 500 Internal Server Error with the above message. So it's saying it couldn't find the web API method that it just called successfully.
using (var response = await client.PostAsXmlAsync("cats/grabacat", mycatprefs))
{
if (response.IsSuccessStatusCode) // 500 cats/grabacat not found
Controller code:
[Route("~/api/cats/grabacat")]
[HttpPost]
public async Task GrabACat()
{
}
After debugging, if I change it to public async Task<SomeObject> GrabACat() then it works OK. This lets me return an object back to the client. However I don't want to return anything back; I want it to be equivelent to calling a void method. It will examine the status code to determine if it was successful.
I have got it working by changing GrabACat to Task and returning new object(); but I am not sure why this is required. It seems crude to return an empty object just to get it to work.
Any ideas?
The WebAPI method has a Route attribute like this:
[Route("~/api/cats/grabacat")]
Which means the URL is wrong in the POST request - you are missing the /api prefix:
using (var response = await client.PostAsXmlAsync("api/cats/grabacat", mycatprefs))
//snip

IIS & Chrome: failed to load resource: net::ERR_INCOMPLETE_CHUNKED_ENCODING

I recently came across a Chrome issue which I think is worth sharing it with you.
I worked on a self written API using an HttpHandler which primary should return json data. But when an error occures I wanted to display an html file. That worked pretty well in IE and FF, but not in Chrome.
Looking to the developer tools revealed this error: net::ERR_INCOMPLETE_CHUNKED_ENCODING
Google said not very much about this issue while it was seen very much. All I got to know was, that it was magically disappearing after some time.
I found out it lays on this lines of code:
result.StoreResult(context);
context.Response.Flush();
context.Response.Close(); //<-- this causes the error
After removing the last line it worked well. I donĀ“t know why only Chrome had/has an issue with that, but it seemed as if I closed the response stream before chrome finished reading it.
I hope it helps those of you coming across the same or a similar issue.
Now my question:
How is the best pratice in closing/flushing the response stream? Are there any rules?
According to ASP.NET sets the transfer encoding as chunked on premature flushing the Response:
ASP.NET transfers the data to the client in chunked encoding (Transfer-Encoding: chunked), if you prematurely flush the Response stream for the Http request and the Content-Length header for the Response is not explicitly set by you.
Solution: You need to explicitly set the Content-Length header for the Response to prevent ASP.NET from chunking the response on flushing.
Here's the C# code that I used for preventing ASP.NET from chunking the response by setting the required header:
protected void writeJsonData (string s) {
HttpContext context=this.Context;
HttpResponse response=context.Response;
context.Response.ContentType = "text/json";
byte[] b = response.ContentEncoding.GetBytes(s);
response.AddHeader("Content-Length", b.Length.ToString());
response.BinaryWrite(b);
try
{
this.Context.Response.Flush();
this.Context.Response.Close();
}
catch (Exception) { }
}
I was running into this error when generating a file and pushing it to the user for download, but only occasionally. When it didn't fail, the file was consistently 2 bytes short. Close() forcibly closes the connection, whether it's finished or not, and in my case it was not. Leaving it out, as suggested in the question, meant the resulting file contained both the generated content as well as the HTML for the entire page.
The solution here was replacing
context.Response.Flush();
context.Response.Close();
with
context.Response.End();
which does the same, but without cutting the transaction short.
In my case, the problem was cache-related and was happening when doing a CORS request.
Forcing the response header Cache-Control to no-cache resolved my issue:
[ using Symfony HttpFoundation component ]
<?php
$response->headers->add(array(
'Cache-Control' => 'no-cache'
));
I was also getting same error. This issue was with web server user permission on cache folder.
On the offchance that someone is landing here as a result of issues with their ASP.net Core project, I was able to resolve by adding the IIS middleware.
This is done by adding UseIISIntegration when instantiating your webhost instance.
Once I had the same problem and the main reason was lying in my controller return type.
If you try to return a C# object just as-is, you will only get net::ERR_INCOMPLETE_CHUNKED_ENCODING so don't forget to serialize your complex objects before sending them out for java script client (or View).
i.e. my controller return type was :
public async Task<List<ComplexModel>> GetComplexModelList(){
return new List<ComplexModel>()
}
Which caused INCOMPLETE_CHUNKED_ENCODING error, so I tried to fix my mistake with something like:
using Newtonsoft.Json;
...
public async Task<string> GetComplexModelList(){
return JsonConvert.SerializeObject(new List<ComplexModel>())
}

Web Api + HttpClient: An asynchronous module or handler completed while an asynchronous operation was still pending

I'm writing an application that proxies some HTTP requests using the ASP.NET Web API and I am struggling to identify the source of an intermittent error.
It seems like a race condition... but I'm not entirely sure.
Before I go into detail here is the general communication flow of the application:
Client makes a HTTP request to Proxy 1.
Proxy 1 relays the contents of the HTTP request to Proxy 2
Proxy 2 relays the contents of the HTTP request to the Target Web Application
Target Web App responds to the HTTP request and the response is streamed (chunked transfer) to Proxy 2
Proxy 2 returns the response to Proxy 1 which in turn responds to the original calling Client.
The Proxy applications are written in ASP.NET Web API RTM using .NET 4.5.
The code to perform the relay looks like so:
//Controller entry point.
public HttpResponseMessage Post()
{
using (var client = new HttpClient())
{
var request = BuildRelayHttpRequest(this.Request);
//HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
//As it begins to filter in.
var relayResult = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
var returnMessage = BuildResponse(relayResult);
return returnMessage;
}
}
private static HttpRequestMessage BuildRelayHttpRequest(HttpRequestMessage incomingRequest)
{
var requestUri = BuildRequestUri();
var relayRequest = new HttpRequestMessage(incomingRequest.Method, requestUri);
if (incomingRequest.Method != HttpMethod.Get && incomingRequest.Content != null)
{
relayRequest.Content = incomingRequest.Content;
}
//Copies all safe HTTP headers (mainly content) to the relay request
CopyHeaders(relayRequest, incomingRequest);
return relayRequest;
}
private static HttpRequestMessage BuildResponse(HttpResponseMessage responseMessage)
{
var returnMessage = Request.CreateResponse(responseMessage.StatusCode);
returnMessage.ReasonPhrase = responseMessage.ReasonPhrase;
returnMessage.Content = CopyContentStream(responseMessage);
//Copies all safe HTTP headers (mainly content) to the response
CopyHeaders(returnMessage, responseMessage);
}
private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
var content = new PushStreamContent(async (stream, context, transport) =>
await sourceContent.Content.ReadAsStreamAsync()
.ContinueWith(t1 => t1.Result.CopyToAsync(stream)
.ContinueWith(t2 => stream.Dispose())));
return content;
}
The error that occurs intermittently is:
An asynchronous module or handler completed while an asynchronous operation was still pending.
This error usually occurs on the first few requests to the proxy applications after which the error is not seen again.
Visual Studio never catches the Exception when thrown.
But the error can be caught in the Global.asax Application_Error event.
Unfortunately the Exception has no Stack Trace.
The proxy applications are hosted in Azure Web Roles.
Any help identifying the culprit would be appreciated.
Your problem is a subtle one: the async lambda you're passing to PushStreamContent is being interpreted as an async void (because the PushStreamContent constructor only takes Actions as parameters). So there's a race condition between your module/handler completing and the completion of that async void lambda.
PostStreamContent detects the stream closing and treats that as the end of its Task (completing the module/handler), so you just need to be sure there's no async void methods that could still run after the stream is closed. async Task methods are OK, so this should fix it:
private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
Func<Stream, Task> copyStreamAsync = async stream =>
{
using (stream)
using (var sourceStream = await sourceContent.Content.ReadAsStreamAsync())
{
await sourceStream.CopyToAsync(stream);
}
};
var content = new PushStreamContent(stream => { var _ = copyStreamAsync(stream); });
return content;
}
If you want your proxies to scale a bit better, I also recommend getting rid of all the Result calls:
//Controller entry point.
public async Task<HttpResponseMessage> PostAsync()
{
using (var client = new HttpClient())
{
var request = BuildRelayHttpRequest(this.Request);
//HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
//As it begins to filter in.
var relayResult = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
var returnMessage = BuildResponse(relayResult);
return returnMessage;
}
}
Your former code would block one thread for each request (until the headers are received); by using async all the way up to your controller level, you won't block a thread during that time.
I would like to add some wisdom for anyone else who landed here with the same error, but all of your code seems fine. Look for any lambda expressions passed into functions across the call-tree from where this occurs.
I was getting this error on a JavaScript JSON call to an MVC 5.x controller action. Everything I was doing up and down the stack was defined async Task and called using await.
However, using Visual Studio's "Set next statement" feature I systematically skipped over lines to determine which one caused it. I kept drilling down into local methods until I got to a call into an external NuGet package. The called method took an Action as a parameter and the lambda expression passed in for this Action was preceded by the async keyword. As Stephen Cleary points out above in his answer, this is treated as an async void, which MVC does not like. Luckily said package had *Async versions of the same methods. Switching to using those, along with some downstream calls to the same package fixed the problem.
I realize this is not a novel solution to the problem, but I passed over this thread a few times in my searches trying to resolve the issue because I thought I didn't have any async void or async <Action> calls, and I wanted to help someone else avoid that.
A slightly simpler model is that you can actually just use the HttpContents directly and pass them around inside the relay. I just uploaded a sample illustrating how you can rely both requests and responses asynchronously and without buffering the content in a relatively simple manner:
http://aspnet.codeplex.com/SourceControl/changeset/view/7ce67a547fd0#Samples/WebApi/RelaySample/ReadMe.txt
It is also beneficial to reuse the same HttpClient instance as this allows you to reuse connections where appropriate.

Categories