나는've 얻기 위해 고군분투하는응답입니다.본체의 재산서 ASP.NET 핵심 활동 및 해결책이 나는've 를 식별할 수 있는 것 sub-optimal. 이 솔루션을 필요로 교환하는
응답입니다.체와
MemoryStream읽는 동안 스트림으로 문자열 변수,그 후 교환하기 전에 그것을 다시 보내는 클라이언트입니다. 아래 예에서 나는'm trying to get 는
응답입니다.몸값을 사용자 정의 미들웨어 클래스입니다.
응답입니다.체_set_ 만 숙박 시설에 ASP.NET 코어에 대한 몇 가지 이유? 나는 이제 여기에,또는 이것이 감독/버그/디자인 문제입니까? 더 좋은 방법이 있을 읽는
응답입니다.몸`?
류(sub-optimal)솔루션
public class MyMiddleWare
{
private readonly RequestDelegate _next;
public MyMiddleWare(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
using (var swapStream = new MemoryStream())
{
var originalResponseBody = context.Response.Body;
context.Response.Body = swapStream;
await _next(context);
swapStream.Seek(0, SeekOrigin.Begin);
string responseBody = new StreamReader(swapStream).ReadToEnd();
swapStream.Seek(0, SeekOrigin.Begin);
await swapStream .CopyToAsync(originalResponseBody);
context.Response.Body = originalResponseBody;
}
}
}
시도한 솔루션을 사용하여 EnableRewind():
이만 작동에 대한 요청을 합니다.몸
,지 않는응답입니다.몸
. 이 결과를 읽고 빈 문자열에서`응답입니다.몸이 아닌 실제 응답 본문 내용입니다.
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifeTime)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.Use(async (context, next) => {
context.Request.EnableRewind();
await next();
});
app.UseMyMiddleWare();
app.UseMvc();
// Dispose of Autofac container on application stop
appLifeTime.ApplicationStopped.Register(() => this.ApplicationContainer.Dispose());
}
MyMiddleWare.cs
public class MyMiddleWare
{
private readonly RequestDelegate _next;
public MyMiddleWare(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
await _next(context);
string responseBody = new StreamReader(context.Request.Body).ReadToEnd(); //responseBody is ""
context.Request.Body.Position = 0;
}
}
래서 우리는 응답을 했는 완전히 잘못을 질문하고 생각했 포스터 요청을 읽는 방법을요청을 합니다.몸
그러나 그는 어떻게 요청을 읽는응답입니다.몸
. 나는'm 을 떠나 내 원래의 답변을 보존하는 역사뿐만 아니라 그것을 업데이트하는 방법을 보여 내가 질문에 대답하면 그것을 읽습니다.
원본 대답
당신이 원하는 버퍼링되는 스트림을 지원하는 독서 여러 번 설정해야
context.Request.EnableRewind()
이상적으로 수행 초기에는 미들웨어의 앞에 아무것도 필요를 읽는 몸입니다.
그래서 예를 들어 배치할 수 있습니다 다음 코드의 시작 부분에서구성하는
방식의 시작을 시작합니다.cs file:
app.Use(async (context, next) => {
context.Request.EnableRewind();
await next();
});
을 사용하도록 설정하기 전에 뒤 스트림에 관련된요청을 합니다.체
앞으로만 스트리밍하지 않는't 지원을 찾는 읽기 또는 스트림이 있습니다. 이렇게 만들의 기본 구성 요청을 처리로 가볍고 성능으로 가능합니다. 하지만 일단 당신이 활성화 되감기 스트림은 업그레이드는 스트림을 지원하고 여러 번입니다. 관찰할 수 있습이"업그레이드"중단점을 설정하여만 전 그냥한 후에 호출하여EnableRewind
을 관찰하는요청을 합니다.
몸 속성입니다. 그래서 예를 들어요청을 합니다.체.CanSeek
에서 변경됩false
할진정한
.
업데이트:에서 시작 ASP.NET 코어 2.1요청을 합니다.EnableBuffering()
사용할 수 있는 업그레이드는요청을 합니다.몸
을FileBufferingReadStream
다음요청을 합니다.EnableRewind()
그리고요청을 합니다.EnableBuffering()
은 공용 네임스페이스 보다는 오히려 내부의 하나가 그것을 선호되어야 합 EnableRewind(). (덕분에@ArjanEinbu 을 가리키)
다음을 읽는 몸이 스트림할 수 있을위한 예제다:
string bodyContent = new StreamReader(Request.Body).ReadToEnd();
Don't 포장StreamReader
창조에서 사용하여 문지만 또는 그것이 가까운 근본적인 체 스트림에서의 결론을 사용하여 블록고 나중에 코드 요청에서 수명 주기 늘를 읽을 수 있는 몸입니다.
또한 안전을,그것도 좋을 아이디어를 따라하에 위의 라인의 코드를 읽어 본문 내용으로 이 줄의 코드를 재설정 신체's 림 위치를 0 으로 다시.
request.Body.Position = 0;
는 방법으로 모든 코드를 나중에 요청에서 수명 주기 찾을 수 있습니다.몸 상태에서처럼 그't 되었습니다.
업데이트에 대답
죄송하는 원래는 오해의 질문입니다. 개념의 업그레이드 관련 스트림을 수퍼드 스트림에도 적용됩니다. 그러나 당신이 그것을 할 수동으로 I'm 를 인지하지 못하는 내장되어 있습니다.Net 핵심할 수 있는 기능을 읽고 스트림 응답을 한 번에 기록하는 방식EnableRewind()
할 수 있는 개발자 다시 읽는 스트림 요청 후's 읽.
H"해키"접근 방식은 가능성이 완전히 적합합니다. 당신은 기본적으로 변환이 스트림할 수 있는't 찾을 수 있습니다. 에서 하루의 끝에는응답입니다.체
스트림을 얻을 수 있으로 교체 흐름과 함께하는 버퍼링과 지원을 찾고 있습니다. 여기에는 다른 걸릴 미들웨어를 하는 하지만 당신은 그것을 알 수 있's 매우 유사하다 당신의 접근 방식이다. 나는 그러나 사용하도록 선택 마지막으로 블록으로 추가 보호하기 위해 원래 스트림을 다시에는응답입니다.체
와 내가 사용하는위치
등의 스트림보다는검색
방법이 이 문법은 비트 간단하지만 효과가 없이 다른 것보다 당신의 접근 방식이다.
public class ResponseRewindMiddleware {
private readonly RequestDelegate next;
public ResponseRewindMiddleware(RequestDelegate next) {
this.next = next;
}
public async Task Invoke(HttpContext context) {
Stream originalBody = context.Response.Body;
try {
using (var memStream = new MemoryStream()) {
context.Response.Body = memStream;
await next(context);
memStream.Position = 0;
string responseBody = new StreamReader(memStream).ReadToEnd();
memStream.Position = 0;
await memStream.CopyToAsync(originalBody);
}
} finally {
context.Response.Body = originalBody;
}
}
당신은 무엇을 설명하으로 해킹을 실제로 제안된 접근 방식을 관리하는 방법 스트림 응답에 사용자 정의합니다.
기의 파이프라인 성격의 상품 설계는 각각의 중웨어는 인식의 이전 또는 다음을 처리기에서 파이프 라인에 있습니다. 는 보장은 없는 현재 중앙 도자기는 것은 하나 쓰기가 응답하지 않는 한 그것에 보유하는 스트림 응답이 주어진 전에 전달되는 스트림(현재 중 ware)제어합니다. 이는 디자인에서 볼 수 있었다 OWIN 고 결국 구운로 asp.net-핵심입니다.
일단 당신이 쓰기 시작하면 스트림 응답을 보내는 몸과 헤더(응답이)클라이언트. 는 경우 다른 처리기 파이프 라인을 내려가기 전에 현재 처리기를 한 다음 그것을 할 수 있게 아무것도 추가 응답되면을 이미 전송됩니다.
는 다시 보장되어 있지 않은 실제 스트림 응답하는 경우 이전의 미들웨어 파이프라인에 따라 동일한 전략을 전달하는 또 다른 스트림 라인.
***경고
주의 수정
HttpResponse
후 호출하고다음
기 때문에, 반응할 수 있 이미 클라이언트로 보내집니다. 당신이 사용할 수 있는 [HttpResponse.HasStarted](https://docs.microsoft.com/en-us/aspnet/core/api/microsoft.aspnetcore.http.features.httpresponsefeature#Microsoft_AspNetCore_Http_Features_HttpResponseFeature_HasStarted 용)여부를 확인하는 헤더가 발송되었습니다.***경고
지 않화
다.불러오는
호출 한 후 awrite
방법입니다. 미들웨어 구성 요소 중 하나 생산하는 응답 또는 전화다.Invoke
지 니다.
예제의 내장에 기초한 미들웨어에서aspnet/BasicMiddlewareGithub repo
ResponseCompressionMiddleware.cs
/// <summary>
/// Invoke the middleware.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
if (!_provider.CheckRequestAcceptsCompression(context))
{
await _next(context);
return;
}
var bodyStream = context.Response.Body;
var originalBufferFeature = context.Features.Get<IHttpBufferingFeature>();
var originalSendFileFeature = context.Features.Get<IHttpSendFileFeature>();
var bodyWrapperStream = new BodyWrapperStream(context, bodyStream, _provider,
originalBufferFeature, originalSendFileFeature);
context.Response.Body = bodyWrapperStream;
context.Features.Set<IHttpBufferingFeature>(bodyWrapperStream);
if (originalSendFileFeature != null)
{
context.Features.Set<IHttpSendFileFeature>(bodyWrapperStream);
}
try
{
await _next(context);
// This is not disposed via a using statement because we don't want to flush the compression buffer for unhandled exceptions,
// that may cause secondary exceptions.
bodyWrapperStream.Dispose();
}
finally
{
context.Response.Body = bodyStream;
context.Features.Set(originalBufferFeature);
if (originalSendFileFeature != null)
{
context.Features.Set(originalSendFileFeature);
}
}
}
사용할 수 있습니다middleware 에서 요청한 파이프라인,하기 위해 로그인 요청 및 응답합니다.
그러나 증가 위험의메모리 누수가
인 facth 는:
할 수 있는 끝까지큰 객체를 힙(는 경우에는 본체의 요청 또는 반응이 보다 큰 85,000 바이트). 이 증가하고 위험의 메모리 누수 응용 프로그램입니다. 을 방지하기 위해 LOH,메모리 스트림에 의해 대체 될 수있다재상할 수 있는 메모리를 스트림사용하여 관련된 라이브러리](https://github.com/Microsoft/Microsoft.IO.RecyclableMemoryStream).
구현을 사용하는 재상할 수 있는 메모리를 스트림:
public class RequestResponseLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
private const int ReadChunkBufferLength = 4096;
public RequestResponseLoggingMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
_next = next;
_logger = loggerFactory
.CreateLogger<RequestResponseLoggingMiddleware>();
_recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();
}
public async Task Invoke(HttpContext context)
{
LogRequest(context.Request);
await LogResponseAsync(context);
}
private void LogRequest(HttpRequest request)
{
request.EnableRewind();
using (var requestStream = _recyclableMemoryStreamManager.GetStream())
{
request.Body.CopyTo(requestStream);
_logger.LogInformation($"Http Request Information:{Environment.NewLine}" +
$"Schema:{request.Scheme} " +
$"Host: {request.Host} " +
$"Path: {request.Path} " +
$"QueryString: {request.QueryString} " +
$"Request Body: {ReadStreamInChunks(requestStream)}");
}
}
private async Task LogResponseAsync(HttpContext context)
{
var originalBody = context.Response.Body;
using (var responseStream = _recyclableMemoryStreamManager.GetStream())
{
context.Response.Body = responseStream;
await _next.Invoke(context);
await responseStream.CopyToAsync(originalBody);
_logger.LogInformation($"Http Response Information:{Environment.NewLine}" +
$"Schema:{context.Request.Scheme} " +
$"Host: {context.Request.Host} " +
$"Path: {context.Request.Path} " +
$"QueryString: {context.Request.QueryString} " +
$"Response Body: {ReadStreamInChunks(responseStream)}");
}
context.Response.Body = originalBody;
}
private static string ReadStreamInChunks(Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
string result;
using (var textWriter = new StringWriter())
using (var reader = new StreamReader(stream))
{
var readChunk = new char[ReadChunkBufferLength];
int readChunkLength;
//do while: is useful for the last iteration in case readChunkLength < chunkLength
do
{
readChunkLength = reader.ReadBlock(readChunk, 0, ReadChunkBufferLength);
textWriter.Write(readChunk, 0, readChunkLength);
} while (readChunkLength > 0);
result = textWriter.ToString();
}
return result;
}
}
NB. 의 위험 LOH 전에 근절해textWriter.ToString()
에 다른 손으로 사용할 수 있습 로깅을 클라이언트 라이브러리를 지원하는 구조화된 로깅(ie. Serilog)과 주입의 인스턴스를 재상할 수 있는 메모리다.