Evento
Campionato do Mundo de Power BI DataViz
Feb 14, 4 PM - Mar 31, 4 PM
Con 4 posibilidades de entrar, poderías gañar un paquete de conferencias e facelo ao Live Grand Finale en Las Vegas
Máis informaciónEste explorador xa non é compatible.
Actualice a Microsoft Edge para dispoñer das funcionalidades máis recentes, as actualizacións de seguranza e a asistencia técnica.
Nota
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Aviso
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la directiva de compatibilidad de .NET y .NET Core. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión de .NET 9 de este artículo.
Por Justin Kotalik
En este artículo se explica cómo leer el cuerpo de la solicitud y escribir el cuerpo de respuesta. El código para estas operaciones podría ser necesario al escribir middleware. Fuera de la escritura de middleware, el código personalizado no suele ser necesario porque las operaciones se controlan mediante MVC and Razor Pages.
Hay dos abstracciones para los cuerpos de solicitud y respuesta: Stream y Pipe. En la lectura de solicitudes, HttpRequest.Body es Stream y HttpRequest.BodyReader
es PipeReader. En la escritura de respuestas, HttpResponse.Body es Stream y HttpResponse.BodyWriter
es PipeWriter.
Se recomienda el uso de canalizaciones por encima de las secuencias. Las secuencias pueden ser más fáciles de usar en el caso de algunas operaciones sencillas, pero las canalizaciones son más ventajosas para el rendimiento y son más fáciles de usar en la mayoría de los casos. ASP.NET Core está empezando a usar internamente canalizaciones en lugar de secuencias. Ejemplos:
FormReader
TextReader
TextWriter
HttpResponse.WriteAsync
Las secuencias no se quitan del marco. Se seguirán usando en todo .NET, y además muchos tipos de secuencias no tienen equivalentes de canalización, como FileStreams
y ResponseCompression
.
Imagine que el objetivo es crear un middleware que lee el cuerpo de la solicitud entero como una lista de cadenas, que se divide en nuevas líneas. Una implementación de secuencias sencilla podría parecerse al ejemplo siguiente:
Aviso
El código siguiente:
private async Task<List<string>> GetListOfStringsFromStream(Stream requestBody)
{
// Build up the request body in a string builder.
StringBuilder builder = new StringBuilder();
// Rent a shared buffer to write the request body into.
byte[] buffer = ArrayPool<byte>.Shared.Rent(4096);
while (true)
{
var bytesRemaining = await requestBody.ReadAsync(buffer, offset: 0, buffer.Length);
if (bytesRemaining == 0)
{
break;
}
// Append the encoded string into the string builder.
var encodedString = Encoding.UTF8.GetString(buffer, 0, bytesRemaining);
builder.Append(encodedString);
}
ArrayPool<byte>.Shared.Return(buffer);
var entireRequestBody = builder.ToString();
// Split on \n in the string.
return new List<string>(entireRequestBody.Split("\n"));
}
Si quiere que los comentarios de código se traduzcan en más idiomas además del inglés, háganoslo saber en este problema de debate de GitHub.
Este código funciona, pero hay algunos problemas:
StringBuilder
, en el ejemplo se crea otra cadena (encodedString
) que se desecha inmediatamente. Este proceso se produce con todos los bytes de la secuencia, por lo que el resultado es la asignación de memoria adicional del tamaño del cuerpo de la solicitud entera.En este ejemplo se corrigen algunos de los problemas anteriores:
Aviso
El código siguiente:
private async Task<List<string>> GetListOfStringsFromStreamMoreEfficient(Stream requestBody)
{
StringBuilder builder = new StringBuilder();
byte[] buffer = ArrayPool<byte>.Shared.Rent(4096);
List<string> results = new List<string>();
while (true)
{
var bytesRemaining = await requestBody.ReadAsync(buffer, offset: 0, buffer.Length);
if (bytesRemaining == 0)
{
results.Add(builder.ToString());
break;
}
// Instead of adding the entire buffer into the StringBuilder
// only add the remainder after the last \n in the array.
var prevIndex = 0;
int index;
while (true)
{
index = Array.IndexOf(buffer, (byte)'\n', prevIndex);
if (index == -1)
{
break;
}
var encodedString = Encoding.UTF8.GetString(buffer, prevIndex, index - prevIndex);
if (builder.Length > 0)
{
// If there was a remainder in the string buffer, include it in the next string.
results.Add(builder.Append(encodedString).ToString());
builder.Clear();
}
else
{
results.Add(encodedString);
}
// Skip past last \n
prevIndex = index + 1;
}
var remainingString = Encoding.UTF8.GetString(buffer, prevIndex, bytesRemaining - prevIndex);
builder.Append(remainingString);
}
ArrayPool<byte>.Shared.Return(buffer);
return results;
}
Este ejemplo anterior:
StringBuilder
a menos que no haya ningún carácter de nueva línea.Split
en la cadena.Sin embargo, todavía hay algunos problemas:
remainingString
) y agrega al búfer de cadenas, lo que resulta en una asignación adicional.Estos problemas se pueden corregir, pero el código se vuelve cada vez más complicado y las mejoras son pocas. Las canalizaciones ofrecen una manera de resolver estos problemas con una complejidad mínima del código.
En el siguiente ejemplo se describe cómo abordar el mismo escenario usando un objeto PipeReader:
private async Task<List<string>> GetListOfStringFromPipe(PipeReader reader)
{
List<string> results = new List<string>();
while (true)
{
ReadResult readResult = await reader.ReadAsync();
var buffer = readResult.Buffer;
SequencePosition? position = null;
do
{
// Look for a EOL in the buffer
position = buffer.PositionOf((byte)'\n');
if (position != null)
{
var readOnlySequence = buffer.Slice(0, position.Value);
AddStringToList(results, in readOnlySequence);
// Skip the line + the \n character (basically position)
buffer = buffer.Slice(buffer.GetPosition(1, position.Value));
}
}
while (position != null);
if (readResult.IsCompleted && buffer.Length > 0)
{
AddStringToList(results, in buffer);
}
reader.AdvanceTo(buffer.Start, buffer.End);
// At this point, buffer will be updated to point one byte after the last
// \n character.
if (readResult.IsCompleted)
{
break;
}
}
return results;
}
private static void AddStringToList(List<string> results, in ReadOnlySequence<byte> readOnlySequence)
{
// Separate method because Span/ReadOnlySpan cannot be used in async methods
ReadOnlySpan<byte> span = readOnlySequence.IsSingleSegment ? readOnlySequence.First.Span : readOnlySequence.ToArray().AsSpan();
results.Add(Encoding.UTF8.GetString(span));
}
En este ejemplo se solucionan muchos problemas que tenían las implementaciones de secuencias:
PipeReader
controla los bytes que no se han usado.ToArray
y de la memoria que usa la cadena, la creación de cadenas es de libre asignación.Las propiedades Body
, BodyReader
y BodyWriter
están disponibles para HttpRequest
y HttpResponse
. Al establecer Body
en una secuencia diferente, un nuevo conjunto de adaptadores se adaptan automáticamente al tipo del otro. Si establece HttpRequest.Body
en una nueva secuencia, HttpRequest.BodyReader
se establece automáticamente en un nuevo objeto PipeReader
que encapsula a HttpRequest.Body
.
HttpResponse.StartAsync
se usa para indicar que los encabezados no se pueden modificar y para ejecutar devoluciones de llamada OnStarting
. Al usar Kestrel como servidor, si se llama a StartAsync
antes de usar el objeto PipeReader
, se garantiza que la memoria que devuelve GetMemory
pertenezca al objeto interno Pipe de Kestrel en lugar de a un búfer externo.
Comentarios de ASP.NET Core
ASP.NET Core é un proxecto de código aberto. Selecciona unha ligazón para ofrecer comentarios:
Evento
Campionato do Mundo de Power BI DataViz
Feb 14, 4 PM - Mar 31, 4 PM
Con 4 posibilidades de entrar, poderías gañar un paquete de conferencias e facelo ao Live Grand Finale en Las Vegas
Máis informaciónFormación
Módulo
Implementación de operaciones HTTP en aplicaciones web Blazor de ASP.NET Core - Training
Implementación de operaciones HTTP en aplicaciones web Blazor de ASP.NET Core
Documentación
Escritura de middleware de ASP.NET Core personalizado
Obtenga información sobre cómo escribir middleware de ASP.NET Core personalizado.
Obtenga información sobre el middleware de ASP.NET Core y la canalización de solicitudes.
Activación de middleware basada en Factory en ASP.NET Core
Aprenda a usar middleware fuertemente tipado con la implementación de una activación basada en Factory en ASP.NET Core.