I'm getting this error from a behavior I don't understand:
I've looked online to try to find a solution but nothing worked or applied to my case.
It says that Stream.Length threw an exception but as I found online, people said to check the .CanSeek
method and for each stream open I checked and they all returned true
.
Here's the proof that it reads the length of the stream:
Debug output ( Sorry for the quality, screenshots were removing the debug output window ).
And this is right before returning the item, in this case a user
, and everything works before it.
Here's my code for this method:
public async Task<RegistrationResponse> RegisterAsync(RegistrationRequest body)
{
// Create a new instance of the IHttpClientFactory
var httpClient = _httpClientFactory.CreateClient("default");
// Create a MemoryStream to be able to use compression
using (var memoryContentStream = new MemoryStream())
{
await JsonSerializer.SerializeAsync(memoryContentStream, body);
// CAN SEEK returns true
var canSeek = memoryContentStream.CanSeek;
memoryContentStream.Seek(0, SeekOrigin.Begin);
// Use HttpRequestMessage instead of the .PostAsync shortcut but either could be used
using (var httpRequest = new HttpRequestMessage(
HttpMethod.Post,
"/api/Accounts/register"))
{
// Add headers to the call
httpRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpRequest.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
using (var compressedMemoryContentStream = new MemoryStream())
{
// Compress the content
using (var gzipStream = new GZipStream(
compressedMemoryContentStream, CompressionMode.Compress))
{
memoryContentStream.CopyTo(gzipStream);
gzipStream.Flush();
compressedMemoryContentStream.Position = 0;
// CAN SEEK returns true
var canSeek2 = compressedMemoryContentStream.CanSeek;
// Create a stream
using (var streamContent = new StreamContent(compressedMemoryContentStream))
{
// Add headers to the call
streamContent.Headers.ContentType =
new MediaTypeHeaderValue("application/json");
streamContent.Headers.ContentEncoding.Add("gzip");
httpRequest.Content = streamContent;
var response = await httpClient.SendAsync(
httpRequest,
HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
// THIS IS WHERE IT DOESN'T WORK ANYMORE, even thought .Content shows the length of the stream
var stream = await response.Content.ReadAsStreamAsync();
var user = await JsonSerializer.DeserializeAsync<RegistrationResponse>(stream);
return user;
}
}
}
}
}
}
Here's another picture for the stack errors:
UPDATE
Here's the code that calls the stream method:
async Task ValidSubmit()
{
try
{
var catId = _configuration["OtherCategory"];
_processing = true;
var newRequest = new RegistrationRequest()
{
FirstName = RegistrationRequest.FirstName,
LastName = RegistrationRequest.LastName,
CategoryId = Guid.Parse(catId),
PhoneNumber = RegistrationRequest.PhoneNumber,
Password = RegistrationRequest.Password
};
// RegistrationRequest.CategoryId = Guid.Parse(this.CategoryId);
IsSuccess = true;
await AuthenticationService.RegisterAsync(newRequest);
// SuccessNotification();
_navigationManager.NavigateTo("code-verification");
}
catch (Exception e)
{
throw;
}
}
And what Exception e
throws here is:
"Cannot access a closed stream"
at >System.ThrowHelper.ThrowObjectDisposedException_StreamClosed(String objectName) at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count) at System.IO.Compression.DeflateStream.PurgeBuffers(Boolean disposing) at System.IO.Compression.DeflateStream.Dispose(Boolean disposing) at System.IO.Stream.Close() at System.IO.Compression.GZipStream.Dispose(Boolean disposing) at System.IO.Stream.Close() at Website.UI.Services.AuthenticationService.d__2.MoveNext() in C:\Users\something\source\repos\Website.Website\Website.UI\Services\AuthenticationService.cs:line 75 at Website.UI.Components.Pages.PageComponents.SignUpComponent.d__8.MoveNext() in C:\Users\something\source\repos\Website.Website\Website.UI\Components\Pages\PageComponents\SignUpComponent.razor:line 114
It seems that at some point the compressedMemoryContent
is being closed too early. Exactly why that is the case is not clear, but it's entirely unrelated to your debugger screenshots of .Length
and CanSeek
failing, which is quite normal for many types of streams. Do not attempt to use the debugger view on a Stream
, it quite often messes up the stream and you need to restart. If you need to debug then use local variables or WriteLine
.
You can simplify the code significantly, as well as add in missing using
s, and hopefully that will resolve your issue.
public async Task<RegistrationResponse> RegisterAsync(RegistrationRequest body)
{
var httpClient = _httpClientFactory.CreateClient("default");
using var httpRequest = new HttpRequestMessage(
HttpMethod.Post,
"/api/Accounts/register"));
httpRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpRequest.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
using var compressedMemoryContentStream = new MemoryStream();
// NOTE the use of leaveOpen, so we don't need to flush it
using (var gzipStream = new GZipStream(
compressedMemoryContentStream, CompressionMode.Compress, leaveOpen: true))
{
// NOTE serialize directly to gzip, no need for second memstream
await JsonSerializer.SerializeAsync(gzipStream, body);
}
compressedMemoryContentStream.Position = 0; // rewind
using var streamContent = new StreamContent(compressedMemoryContentStream);
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
streamContent.Headers.ContentEncoding.Add("gzip");
httpRequest.Content = streamContent;
using var response = await httpClient.SendAsync(
httpRequest,
HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
using var stream = await response.Content.ReadAsStreamAsync();
var user = await JsonSerializer.DeserializeAsync<RegistrationResponse>(stream);
return user;
}
Even after making these changes you are reporting that you get all nulls. This seems to be because you have a case mismatch between the property names and your JSON, for example name
and Name
.
To fix this, you can either set the deserializer to case-insensitive:
static JsonSerializerOptions _options = new JsonSerializerOptions(JsonSerializerOptions.Default)
{
PropertyNameCaseInsensitive = true,
};
var user = await JsonSerializer.DeserializeAsync<RegistrationResponse>(stream, _options);
Or set each property with a name explicitly
public class MyData
{
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
}