I've build a very simple logging middleware that I use to get request and response body. Everything works just fine if I return IHttpActionResult
or Task<IHttpActionResult>
from my methods, but when I change them to HttpResponseMessage
or Task<HttpResponseMessage>
I get System.AccessViolationException
I've created simple controller that shows my issue:
[RoutePrefix("test")]
public class TestController : BaseApiController
{
[AllowAnonymous]
[HttpGet]
[Route("a")]
public IHttpActionResult A()
{
return Ok("A");
}
[AllowAnonymous]
[HttpGet]
[Route("b")]
public async Task<IHttpActionResult> B()
{
//some long running operation
await Task.Delay(1);
return Ok("B");
}
[AllowAnonymous]
[HttpGet]
[Route("c")]
public HttpResponseMessage C()
{
const string xml = "<response><body>C</body></response>";
return new HttpResponseMessage()
{
Content = new StringContent(xml, Encoding.UTF8, "application/xml")
};
}
[AllowAnonymous]
[HttpGet]
[Route("d")]
public async Task<HttpResponseMessage> D()
{
//some long running operation
await Task.CompletedTask;
const string xml = "<response><body>D</body></response>";
return new HttpResponseMessage()
{
Content = new StringContent(xml, Encoding.UTF8, "application/xml")
};
}
}
and my Middleware:
public static class RequestLoggerMiddlewareExtensions
{
public static void UseRequestLogger(this IAppBuilder app)
{
app.Use<RequestLoggerMiddleware>();
}
}
internal class RequestLoggerMiddleware : OwinMiddleware
{
public RequestLoggerMiddleware(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
Stream oldResponseBody;
string ip = context.Request.RemoteIpAddress;
var reqContentType = context.Request.ContentType;
//get the response body and put it back for the downstream items to read
var reqHeaders = string.Join(",",context.Request.Headers.Select(h=>"["+h.Key+"]"+h.Value[0]).ToArray());
string reqBody = new StreamReader(context.Request.Body).ReadToEnd();
byte[] requestData = Encoding.UTF8.GetBytes(reqBody);
context.Request.Body = new MemoryStream(requestData);
var queryString = context.Request.QueryString.ToString();
//read the path and the request method
string reqPath = context.Request.Path.ToUriComponent();
string reqMethod = context.Request.Method;
//buffer the response stream
oldResponseBody = context.Response.Body;
//create a memory stream replacement
MemoryStream replacement = new MemoryStream();
//assing the replacement to the resonse body
context.Response.Body = replacement;
//Do the next thing
await Next.Invoke(context);
//grab the response code
int responseCode = context.Response.StatusCode;
//grab the response stream buffer
//move to the beginning
//copy it to the old response stream
replacement.Seek(0, SeekOrigin.Begin);
//**********************Put the response back on the request body - this sends it down stream to the caller!
await replacement.CopyToAsync(oldResponseBody);
//move back to the head of the memory stream
replacement.Seek(0, SeekOrigin.Begin);
//read the response for logging
StreamReader sr = new StreamReader(replacement);
string responseBody = sr.ReadToEnd();
//grab the response phrase
string responseReasonPhrase = context.Response.ReasonPhrase;
var responseContentType = context.Response.ContentType;
Debug.WriteLine("IP:{0}, REQ:{1},REQ_PATH:{2},REQ_METHOD:{3},REQ_CONTENT_TYPE:{4},RES_STATUS:{5},RES_REASON:{6},RES_CONTENT_TYPE:{7},RES_BODY:{8}", ip, reqBody, reqPath, reqMethod, reqContentType, responseCode, responseReasonPhrase, responseContentType, responseBody);
}
}
I'm adding this middleware inside Startup.cs in Configuration method adding app.UseRequestLogger();
Exception shows inside Visual Studio, so I can't even debug ad check what is happening.
What can be the cause of that error? Any clues are welcome.