Как сбросить PipeReader, чтобы иметь возможность перечитывать тело запроса в ASP. NET Core - PullRequest
2 голосов
/ 17 февраля 2020

У меня есть некоторое ASP. NET Базовое промежуточное программное обеспечение (AuthorizationHandler), которому необходим доступ к телу запроса для проверки прав доступа. Я использую новые конвейерные API для доступа к телу запроса. Код выглядит следующим образом:

PipeReader bodyReader = httpContext.Request.BodyReader;

ReadResult readResult = await bodyReader.ReadAsync();
ReadOnlySequence<byte> readResultBuffer = readResult.Buffer;

var utf8JsonReader = new Utf8JsonReader(bytes);
while (utf8JsonReader.Read()) { ... }

Когда этот код выполняется, основной поток считывается. Контроллер, который я хочу вызвать, выдает ошибку проверки, потому что он не видит тело запроса, потому что оно уже прочитано.

Так как мне сбросить PipeReader, чтобы тело запроса могло быть повторно читать?

Я знаю, что когда вы не используете BodyReader, а Body, вы можете использовать EnableBuffering для включения запрос перечитывает. Однако при использовании конвейеров это больше не работает (или я делаю что-то еще неправильно).

Ответы [ 2 ]

1 голос
/ 19 февраля 2020

Я создаю проблему вчера. См. комментарий jkotalik :

Передав readResult.Buffer.Start для использованного, вы говорите, что не использовали никакого ReadResult. Передавая readResult.Buffer.End, вы говорите, что изучили все, что говорит каналу о необходимости не освобождать буферы, и в следующий раз, когда ReadAsyn c вернется, больше данных будет в буфере

Это побуждает меня вызывать bodyReader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.Start); для сброса исследуемой позиции.

Например, ваше промежуточное программное обеспечение можно записать так:

async Task IMiddleware.InvokeAsync(HttpContext httpContext, RequestDelegate next)
{
    if(httpContext.Request.Method != HttpMethods.Post){
        return;
    }
    var bodyReader = httpContext.Request.BodyReader; // don't get it by `PipeReader.Create(httpContext.Request.Body);`

    // demo:  read all the body without consuming it
    ReadResult readResult;
    while(true){
        readResult = await bodyReader.ReadAsync();
        if(readResult.IsCompleted) { break; }
        // don't consume them, but examine them
        <b>bodyReader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);</b>
    }
    // now all the body payload has been read into buffer
    var buffer = readResult.Buffer; 
    Process(ref buffer);              // process the payload (ReadOnlySequence)

    // Finally, <b>reset the EXAMINED POSITION</b> here
    <b>bodyReader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.Start);</b>
    await next(httpContext);
}

void Process(ref ReadOnlySequence<byte> buffer){
    var utf8JsonReader = new Utf8JsonReader(buffer);
    while (utf8JsonReader.Read()) {  
        Console.WriteLine($"{utf8JsonReader.TokenType}");
        ...
    }
}

(Ключ - AdvanceTo() может не только пересылать потребляемую позицию, но и может изменять проверяемую позицию)

0 голосов
/ 18 февраля 2020

Я работал над чтением и сбросом тела запроса в методе OnActionExecuting контроллера. Вот что у меня сработало:

httpContext.Request.Body.Position = 0;

Может быть, это также работает в конвейере?

...