Reading Request body in .net core 3.1 - .net-core-3.1

I am trying to read request body in ActionFilter but having some strange errors.
What I tried so far:
Copying request body to memory stream
context.HttpContext.Request.Body.CopyTo(memoryStream)
Throws an error that only async operations are supported.
Copying request body to memory stream async
context.HttpContext.Request.Body.CopyToAsync(memoryStream).Wait()
Copies 0 bytes
Using BodyReader:
context.HttpContext.Request.BodyReader.AsStream(true).CopyToAsync(requestBody).Wait();
throws ArgumentOutOfRange exception 'Specified argument was out of the range of valid values. (Parameter 'start')'
I am using .net core 3.1

I had to convert my code to MiddleWare which is able to deal with Body stream:
MIddleware ccaptures request and put it to service.
Action filter access service and decides what to do with it.
Here is a code of middleware
public class RequestReaderMiddleWare
{
private readonly RequestDelegate _next;
public RequestReaderMiddleWare(RequestDelegate next)
{
_next = next;
}
public virtual async Task Invoke(HttpContext context)
{
//replace and revert original request stream
var requestBodyStream = new MemoryStream();
var originalRequestBody = context.Request.Body;
await context.Request.Body.CopyToAsync(requestBodyStream);
requestBodyStream.Seek(0, SeekOrigin.Begin);
var bodyContentService = context.RequestServices.GetService(typeof(BodyContentService))
as BodyContentService;
bodyContentService.Body = requestBodyStream.ToArray();
requestBodyStream.Seek(0, SeekOrigin.Begin);
context.Request.Body = requestBodyStream;
await _next(context);
context.Request.Body = originalRequestBody; //restore request body
}
}
PS: You need to register it in startup:
app.UseMiddleware<RequestReaderMiddleWare>();

Related

is returning stream considered anti pattern in web api?

I am from the old world that think webapi should return a strong typed object and let json serialization return data.
However, recently we got this requirement:
We have a sql table which has more than 500 columns.
The customer always want to return all the columns.
Our c# code does nothing other than reading the SqlDatareader, convert the reader to a c# object and return result.
In this case, wouldn't better to do this (example copied from another stackoverflow post). Basically just return a stream? Does returning a stream still considered to be anti-pattern?
public HttpResponseMessage SomeMethod(List<string> someIds)
{
HttpResponseMessage resp = new HttpResponseMessage();
resp.Content = new PushStreamContent(async (responseStream, content, context) =>
{
await CopyBinaryValueToResponseStream(responseStream, someIds);
});
return resp;
}
private static async Task CopyBinaryValueToResponseStream(Stream responseStream, int imageId)
{
// PushStreamContent requires the responseStream to be closed
// for signaling it that you have finished writing the response.
using (responseStream)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
await connection.OpenAsync();
using (SqlCommand command = new SqlCommand("SELECT 500 columns FROM [StupidWideTable] WHERE ....", connection))
{
.....
using (SqlDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess))
{
if (await reader.ReadAsync())
{
if (!(await reader.IsDBNullAsync(0)))
{
using (Stream data = reader.GetStream(0))
{
// Asynchronously copy the stream from the server to the response stream
await data.CopyToAsync(responseStream);
}
}
}
}
}
}
}// close response stream
}
Does returning a stream still considered to be anti-pattern?
Well, that depends on what you want to do. For example, if you want to return a 500 if the SQL server fails partway through, then you shouldn't return a stream.
Streaming results works fine on ASP.NET, but it's important to note that all headers (including the response status code) are sent before the stream begins. So you'll send an immediate 200 when you start streaming the result, and if there's an error later on there's no way to go back in time and change that to a 500. Or add some kind of Continue header.
In other words, yes it's supported; but you lose all the benefits of model binding, content negotiation, exception handlers, etc., because you're bypassing that whole pipeline.

ASP.Net Core 1.1 MiddleWare response

I am replacing a HttpHandler with a middleware service. I have all the code working except for returning the actual image. All the existing samples are for asp.net Core (or earlier) , but with asp.net core 1.1 the response object has change?
public async Task Invoke(HttpContext context)
{
var mediaType = new MediaTypeHeaderValue("image/jpeg");
mediaType.Encoding = System.Text.Encoding.UTF8;
context.Response.ContentType = mediaType.ToString();
byte[] results = some process that generates a byte array
Stream stream = new MemoryStream(results);
context.Response.Body = stream;
await _next.Invoke(context);
}
So how do we attach the byte array to the response object?
There is couple of methods which you can use on .NET Core 1.1:
httpContext.Response.Body.WriteAsync([BUFFER], [OFFSET], [COUNT]);
httpContext.Response.Body.Write([BUFFER], [OFFSET], [COUNT]);
httpContext.Response.Body.WriteByte([BYTE]);
httpContext.Response.WriteAsync([TEXT])

Web API file transfer unauthorized error

I'm writing a simple Web API application using ASP.NET MVC 5 and Web API v2. The api should receive a binary file from a client that uses HttpClient in a Winforms application. The application and the web site should be running on a closed network with Active Directory.
The controller:
[Route("FileTest"]
public HttpResponseMessage PostTest([FromBody]HttpPostedFileBase file)
{
// does nothing just retuns ok
}
The client:
public void SendFile(string fileName)
{
using(FileStream fileStream = new FileStream(fileName, fileMode.open, FilleAccess.read))
{
using(MultipartFormDataContent data = new MultipartFormDataContent ())
{
using(StreamContent streamcontent = new StreamContent(fileStream))
{
data.Add(streamcontent );
HttpClient client = new HttpClient();
res = client .PostAsync("address", data).Result;
res.EnsureSucessStatusCode(); // exception unauthorized
}
}
}
Why is this not working?
HttpPostedFileBase is a MVC class, it is not a Web API class, so it is unlikely they will work together.
Instead of trying to pass the file as a parameter, just read the request content as a stream.
[Route("FileTest"]
public async Task<HttpResponseMessage> PostTest(HttpRequestMessage request)
{
var stream = await request.Content.ReadAsStreamAsync();
// ...
}

OWIN OnSendingHeaders Callback - Reading Response Body

This question is related to the excellent answer by Youssef. I love OnSendingHeaders callback. I can now add the response headers without worrying about switching streams. Anyways, here is my question. Is it possible to read the response body inside the callback, like so.
public override async Task Invoke(OwinRequest request, OwinResponse response)
{
request.OnSendingHeaders(state =>
{
var resp = (OwinResponse)state;
// Here, I want to convert resp, which is OwinResponse
// to HttpResponseMessage so that when Content.ReadAsStringAsync
// is called off this HttpResponseMessage object, I want the
// response body as string.
var responseMessage = new HttpResponseMessage();
responseMessage.Content = new StreamContent(resp.Body);
// Here I would like to call
// responseMessage.Content.ReadAsStringAsync()
}, response);
await Next.Invoke(request, response);
}
The methods I want to call from the callback are part of classes that depend on HttpResponseMessage and do not want to change them.
If I set the response body to memory stream before the pipeline processing starts (as was initially suggested by Youssef in the linked answer), I'm able to get this working. Is there a better way to do this here in the callback instead of that?
EDIT:
Is this okay?
public override async Task Invoke(OwinRequest request, OwinResponse response)
{
// Do something with request
Stream originalStream = response.Body;
var buffer = new MemoryStream();
response.Body = buffer;
await Next.Invoke(request, response);
var responseMessage = new HttpResponseMessage();
response.Body.Seek(0, SeekOrigin.Begin);
responseMessage.Content = new StreamContent(response.Body);
// Pass responseMessage to other classes for the
// response body to be read like this
// responseMessage.Content.ReadAsStringAsyn()
// Add more response headers
if (buffer != null && buffer.Length > 0)
{
buffer.Seek(0, SeekOrigin.Begin);
await buffer.CopyToAsync(originalStream);
}
}
What do you want to do with the response body?
This callback is invoked on first write, so it's too late to replace the stream. You also can't read from the response stream as there is nothing stored in it normally. This is normally a write-only stream that goes out to the network.
Replacing the response stream earlier is the correct approach here.

Windows Phone sends more than one web requests in order in a call

Reccently, I am working on a project in Windows Phone. and In this project, to validate a user, I need to check at 3 web API, the logic is like below:
Step 1: access web api 1 to get the token
Step 2: access web api 2 to get the username/password by the token retrieved in Step 1
Step 3: access web API 3 to validate the user name/password in step 2
you can see we need to access those 3 API in order. as well know, window phone now access the network asynchronously, which causes a big challenge on make those API access in order, and which make the soure code hard to maintainace.
I also consider the synchronous source code like below, but I found there are some problems to access the network,many exeption will be thrown. For example, when an exception is thrown, I try to use asynchronous web request to access the same URL, it is OK. I am strugglig in it now. And I have to introduce thread to call it to avoid to block the UI thread.
internal static class HttpWebRequestExtensions
{
public const int DefaultRequestTimeout = 60000;
public static bool IsHttpExceptionFound = false;
public static WebResponse GetResponse(this WebRequest request, int nTimeOut = DefaultRequestTimeout)
{
var dataReady = new AutoResetEvent(false);
HttpWebResponse response = null;
var callback = new AsyncCallback(delegate(IAsyncResult asynchronousResult)
{
try
{
response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
dataReady.Set();
}
catch(Exception e)
{
IsHttpExceptionFound = true;
}
});
request.BeginGetResponse(callback, request);
if (dataReady.WaitOne(nTimeOut))
{
return response;
}
return null;
}
public static WebResponse PostRequest(this HttpWebRequest request, String postData, int nTimeOut = DefaultRequestTimeout)
{
var dataReady = new AutoResetEvent(false);
HttpWebResponse response = null;
var callback = new AsyncCallback(delegate(IAsyncResult asynchronousResult)
{
Stream postStream = request.EndGetRequestStream(asynchronousResult); //End the operation.
byte[] byteArray = Encoding.UTF8.GetBytes(postData); //Convert the string into a byte array.
postStream.Write(byteArray, 0, postData.Length); //Write to the request stream.
postStream.Close();
dataReady.Set();
});
request.BeginGetRequestStream(callback, request);
if (dataReady.WaitOne(nTimeOut))
{
response = (HttpWebResponse)request.GetResponse(nTimeOut);
if (IsHttpExceptionFound)
{
throw new HttpResponseException("Failed to get http response");
}
return response;
}
return null;
}
}
Any suggestion on using asynchronous web request to solve my case?
There's an example here of using asynchronous web services in a chained manner to call the Microsoft Translator service on WP7
Maybe it will give you some pointers?
http://blogs.msdn.com/b/translation/p/wp7translate.aspx

Resources