I'm trying to get body from request of an authorization class (AuthorizationHandler), but that body is a Stream and after reading your content, the post request that comes on next can not be executed because Stream content has been disposable.
I'm using this code to get Stream content:
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, Autorizacao requirement)
{
var routeValues = context.Resource as AuthorizationFilterContext;
if (routeValues != null)
{
var obj = StreamToObject(routeValues.HttpContext.Request.Body);
context.Succeed(requirement);
}
return Task.FromResult(0);
}
private Object StreamToObject(Stream stream)
{
try
{
string content;
using (var reader = new StreamReader(stream))
content = reader.ReadToEnd();
return Newtonsoft.Json.JsonConvert.DeserializeObject(content);
}
catch (Exception e)
{
throw e;
}
}
How i can do to workaround this problem ?
StreamReader has a special constructor that allow you to pass a boolean as last parameter. It will prevent dispose underlying stream
EDIT: Because ReadToEnd do not restore position in stream you should do it by yourself like this:
var position = stream.Position;
using (var reader = new StreamReader(stream, Encoding.UTF8, false, 8192, true))
content = reader.ReadToEnd();
stream.Seek(position, SeekOrigin.Begin);
EDIT 2: From MSDN I see that Body has a setter. So you can replace original Body with memory stream:
if (routeValues != null)
{
var memoryStream = new MemoryStream();
routeValues.HttpContext.Request.Body.CopyTo(memoryStream);
// reset position after CopyTo
memoryStream.Seek(0, SeekOrigin.Begin);
var obj = StreamToObject(memoryStream);
// reset position after ReadToEnd
memoryStream.Seek(0, SeekOrigin.Begin);
routeValues.HttpContext.Request.Body = memoryStream;
context.Succeed(requirement);
}
Maybe not needed any more, but you can set request.EnableRewind() and then do the request.Body.Seek(0, SeekOrigin.Begin);
Work for me
Related
I would like to be able to return MemoryStream from my function but I think that when returning the stream its also automatically closed.
using (var httpStream = await httpClient.GetStreamAsync(link))
{
using (var stream = new MemoryStream())
{
await httpStream.CopyToAsync(stream);
return stream;
}
}
Is there any way to maybe override this so I can return stream so I can use it elsewhere.
Here's how I'm trying to use it in another method:
using (var fielStream = new FileStream(path, FileMode.Create))
{
Stream stream = await GetStreamAsync(videoid, type, quality);
await stream.CopyToAsync(fileStream);
}
Drop inner using: it is the caller (who calls for creation) should Dispose the stream:
using (var httpStream = await httpClient.GetStreamAsync(link)) {
var stream = new MemoryStream();
try {
await httpStream.CopyToAsync(stream);
return stream;
}
catch {
stream.Dispose();
throw;
}
}
It is the caller who should put using, like this:
// We create MemoryStream with the code above (GetStreamAsync)
/ and then Dispose the stream
using (var stream = await GetStreamAsync(...)) {
...
}
I have the following code that generate two kinds of errors. First with the current code I get an exception 'NotSupportedException: This stream from ZipArchiveEntry does not support reading.'. How am I supposed to read the data ?
Furthermore if i use a MemoryStream (as the commented code ) then I can read the data and deserialize correctly but the memorystream i created still remains in memory even if the dispose method has been called on it , causing some memory leaks . Any idea what is wrong with this code ?
void Main()
{
List<Product> products;
using (var s = GetDb().Result)
{
products = Utf8Json.JsonSerializer.Deserialize<List<Product>>(s).ToList();
}
}
// Define other methods and classes here
public static Task<Stream> GetDb()
{
var filepath = Path.Combine("c:/users/tom/Downloads", "productdb.zip");
using (var archive = ZipFile.OpenRead(filepath))
{
var data = archive.Entries.Single(e => e.FullName == "productdb.json");
return Task.FromResult(data.Open());
//using (var reader = new StreamReader(data.Open()))
//{
// var ms = new MemoryStream();
// data.Open().CopyTo(ms);
// ms.Seek(0, SeekOrigin.Begin);
// return Task.FromResult((Stream)ms);
//}
}
}
With the commented code you open the stream into a reader, don't use the reader, then open the stream again and copy over to the memory stream without closing the second opened stream.
It is the second opened stream that remains in memory, not the MemoryStream.
Refactor
public static async Task<Stream> GetDb() {
var filepath = Path.Combine("c:/users/tom/Downloads", "productdb.zip");
using (var archive = ZipFile.OpenRead(filepath)) {
var entry = archive.Entries.Single(e => e.FullName == "productdb.json");
using (var stream = entry.Open()) {
var ms = new MemoryStream();
await stream.CopyToAsync(ms);
return ms;
}
}
}
I have (several) WebAPI action(s), which load QuickFix logs from database (via EF) and use this private method to return them as CSV:
private HttpResponseMessage BuildCsvResponse<T>(T[] entries, Func<T, string> row, string fileName)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
var i = entries.Length;
foreach (var entry in entries)
{
i--;
writer.WriteLine(row(entry)); // simply call to overridden ToString() method
}
stream.Seek(0, SeekOrigin.Begin);
stream.Flush();
response.Content = new StreamContent(stream);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fileName,
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
return response;
}
The problem is that content is never loaded to the end and cut on random symbol not so far from end. Why could it happen?
May be it is important - all log strings contain delimiter 0x01
You need to Flush your streamwriter's internal buffers before you touch the underlying stream.
Best is to tell your StreamWriter to keep the stream open by using another contructor. You can then safely dispose your streamwriter causing it to flush its buffer while your memorystream instance stays open and doesn't get disposed.
Notice that you need to pick an encoding that matches your HTTP content response. I choose UTF8 here, adapt accordingly.
var stream = new MemoryStream();
// notice the true as last parameter, false is the default.
using(var writer = new StreamWriter(stream, Encoding.UTF8, 8192, true))
{
var i = entries.Length;
foreach (var entry in entries)
{
i--;
writer.WriteLine(row(entry)); // simply call to overridden ToString() method
}
}
// your streamwriter has now flushed its buffer and left the stream open
stream.Seek(0, SeekOrigin.Begin);
// calling Flush on the stream was never needed so I removed that.
response.Content = new StreamContent(stream);
I get the warning CA2202 (Object can be disposed more than once) on the following code:
using (Stream responseStream = response.GetResponseStream())
{
if (responseStream != null)
using (var br = new BinaryReader(responseStream))
{
responseValue = br.ReadBytes(500000);
}
}
It seems like, the responseStreams Dispose is called when the BinaryReaders Dispose is called. Does the BinaryReader always call the Streams Dispose Method?
One solution would be to initialize the ResponseStream directly and let the BinaryReader take care about disposing the stream (which, of course, would only work, if the BinaryReader would dispose the stream in any situation)
Stream responseStream = response.GetResponseStream();
if(responseStream != null)
using (var br = new BinaryReader(responseStream)) //Always disposes the response stream?
[...]
I could use a try/finalize instead of the outer using statement to get something like this:
Stream responseStream = null;
try
{
responseStream = response.GetResponseStream();
if (responseStream != null)
using (var br = new BinaryReader(responseStream))
{
responseValue = br.ReadBytes(500000);
}
}
finally
{
if(stream != null)
stream.Dispose;
}
This isn't nice to look and unnecessary, when the BinaryReader always disposes the stream. Is there a better / preferred solution to solve this kind of problem?
You can actually look how BinaryReader.Dispose is implemented:
protected virtual void Dispose(bool disposing) {
if (disposing) {
Stream copyOfStream = m_stream;
m_stream = null;
if (copyOfStream != null && !m_leaveOpen)
copyOfStream.Close();
}
m_stream = null;
m_buffer = null;
m_decoder = null;
m_charBytes = null;
m_singleChar = null;
m_charBuffer = null;
}
public void Dispose()
{
Dispose(true);
}
Now, question is, what is m_leaveOpen? That's a flag which say if underlying stream should be disposed or not! It's by default set to false, which means underlying stream will be disposed:
public BinaryReader(Stream input) : this(input, new UTF8Encoding(), false) {
}
public BinaryReader(Stream input, Encoding encoding) : this(input, encoding, false) {
}
public BinaryReader(Stream input, Encoding encoding, bool leaveOpen) {
// (...)
m_leaveOpen = leaveOpen;
// (...)
}
So you can skip using statement around you stream, as it will be disposed anyway:
responseStream = response.GetResponseStream();
if (responseStream != null)
{
using (var br = new BinaryReader(responseStream))
{
responseValue = br.ReadBytes(500000);
}
}
or just
using (var br = new BinaryReader(response.GetResponseStream()))
{
responseValue = br.ReadBytes(500000);
}
If you are using VS 2012 with .NET 4.5, you can create a BinaryReader that won't close the stream. E.g.:
using(var reader = new BinaryReader(theStream, new UTF8Encoding(), true)
{
//...
}
new UTF8Encoding is the default if you used the BinaryReader(Stream) constructor, if you don't want UTF8Encoding, you can use something else. The true signifies "yes, leave the stream open".
Constructs like :
public BinaryReader(
Stream input,
Encoding encoding,
bool leaveOpen
)
Hope this leads you to something you are looking for.
Cheers.
Behold the code:
using (var client = new WebClient())
{
using (var stream = client.OpenWrite("http://localhost/", "POST"))
{
stream.Write(post, 0, post.Length);
}
}
Now, how do I read the HTTP output?
It looks like you have a byte[] of data to post; in which case I expect you'll find it easier to use:
byte[] response = client.UploadData(address, post);
And if the response is text, something like:
string s = client.Encoding.GetString(response);
(or your choice of Encoding - perhaps Encoding.UTF8)
If you want to keep streams everywhere and avoid allocating huge arrays of bytes, which is good practise (for example, if you plan to post big files), you still can do it with a derived version of WebClient. Here is a sample code that does it.
using (var client = new WebClientWithResponse())
{
using (var stream = client.OpenWrite(myUrl))
{
// open a huge local file and send it
using (var file = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
file.CopyTo(stream);
}
}
// get response as an array of bytes. You'll need some encoding to convert to string, etc.
var bytes = client.Response;
}
And here is the customized WebClient:
public class WebClientWithResponse : WebClient
{
// we will store the response here. We could store it elsewhere if needed.
// This presumes the response is not a huge array...
public byte[] Response { get; private set; }
protected override WebResponse GetWebResponse(WebRequest request)
{
var response = base.GetWebResponse(request);
var httpResponse = response as HttpWebResponse;
if (httpResponse != null)
{
using (var stream = httpResponse.GetResponseStream())
{
using (var ms = new MemoryStream())
{
stream.CopyTo(ms);
Response = ms.ToArray();
}
}
}
return response;
}
}