Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.
Advertência
Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.
HttpContext encapsula toda a informação sobre um pedido e resposta HTTP individual. Uma HttpContext instância é inicializada quando um pedido HTTP é recebido. A instância HttpContext é acessível através de middleware e frameworks de aplicações como Blazor Web Apps, controladores de API Web, Razor Pages, SignalR, gRPC, entre outros.
HttpRequest
HttpContext.Request fornece acesso a HttpRequest.
HttpRequest tem informação sobre o pedido HTTP recebido, e é inicializado quando um pedido HTTP é recebido pelo servidor.
HttpRequest não é apenas de leitura, e o middleware pode alterar os valores dos pedidos no pipeline.
Os membros mais usados em HttpRequest incluem:
| Propriedade | Description | Example |
|---|---|---|
| HttpRequest.Path | O caminho da solicitação. | /en/article/getstarted |
| HttpRequest.Method | O método de pedido. | GET |
| HttpRequest.Headers | Uma coleção de cabeçalhos de pedidos. | user-agent=Edgex-custom-header=MyValue |
| HttpRequest.RouteValues | Uma coleção de valores de rotas. A coleção é definida quando a requisição é correspondida a uma rota. | language=enarticle=getstarted |
| HttpRequest.Query | Uma coleção de valores de consulta analisados a partir de QueryString. | filter=hellopage=1 |
| HttpRequest.ReadFormAsync() | Um método que lê o corpo do pedido como um formulário e devolve uma coleção de valores de formulário. Para informações sobre o motivo pelo qual ReadFormAsync deve ser usado para aceder a dados de formulários, veja Preferir ReadFormAsync em vez de Request.Form. |
email=user@contoso.com |
| HttpRequest.Body | A Stream para ler o corpo do pedido. | Carga útil UTF-8 JSON |
Obter cabeçalhos de requisição
HttpRequest.Headers fornece acesso aos cabeçalhos do pedido enviados com o pedido HTTP. Existem duas formas de aceder a cabeçalhos usando esta coleção:
- Forneça o nome do cabeçalho ao indexador na coleção de cabeçalhos. O nome do cabeçalho não distingue entre maiúsculas e minúsculas. O indexador pode aceder a qualquer valor de cabeçalho.
- A coleção de cabeçalhos também tem propriedades para obter e definir cabeçalhos HTTP comumente usados. As propriedades fornecem uma forma rápida e baseada no IntelliSense de aceder aos cabeçalhos.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", (HttpRequest request) =>
{
var userAgent = request.Headers.UserAgent;
var customHeader = request.Headers["x-custom-header"];
return Results.Ok(new { userAgent = userAgent, customHeader = customHeader });
});
app.Run();
Para informações sobre como lidar eficientemente com cabeçalhos que aparecem mais do que uma vez, veja Uma breve análise a StringValues.
Ler o corpo do pedido
Um pedido HTTP pode incluir um corpo de pedido. O corpo do pedido são dados associados ao pedido, como o conteúdo de um formulário HTML, carga útil UTF-8 JSON ou um ficheiro.
HttpRequest.Bodypermite que o corpo do pedido seja lido com:Stream
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/uploadstream", async (IConfiguration config, HttpContext context) =>
{
var filePath = Path.Combine(config["StoredFilesPath"], Path.GetRandomFileName());
await using var writeStream = File.Create(filePath);
await context.Request.Body.CopyToAsync(writeStream);
});
app.Run();
HttpRequest.Body pode ser lido diretamente ou utilizado com outras APIs que aceitam fluxo.
Observação
As APIs mínimas suportam a ligação HttpRequest.Body direta a um Stream parâmetro.
Ativar o buffering do corpo de pedidos
O corpo do pedido só pode ser lido uma vez, do início ao fim. A leitura apenas direta do corpo do pedido evita a sobrecarga de armazenar em buffer todo o corpo do pedido e reduz o uso de memória. No entanto, em alguns cenários, é necessário ler o corpo do pedido várias vezes. Por exemplo, o middleware pode precisar de ler o corpo do pedido e depois rebobinar o corpo do pedido para que fique disponível para o endpoint.
O EnableBuffering método de extensão permite o buffering do corpo do pedido HTTP e é a forma recomendada para permitir múltiplas leituras. Como um pedido pode ter qualquer tamanho, EnableBuffering suporta opções para armazenar grandes corpos de pedido no disco ou rejeitá-los completamente.
O middleware no exemplo seguinte:
- Permite múltiplas leituras com
EnableBuffering. Deve ser chamado antes de ler o conteúdo do pedido. - Ler o corpo do pedido.
- Rebobina o corpo do pedido até ao início para que outro middleware ou o endpoint possam lê-lo.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
context.Request.EnableBuffering();
await ReadRequestBody(context.Request.Body);
context.Request.Body.Position = 0;
await next.Invoke();
});
app.Run();
BodyReader
Uma forma alternativa de ler o corpo do pedido é usar a HttpRequest.BodyReader propriedade. A BodyReader propriedade expõe o corpo do pedido como um PipeReader. Esta API provém dos pipelines de I/O, uma forma avançada e de alto desempenho de ler o corpo do pedido.
O leitor acede diretamente ao corpo do pedido e gere a memória em nome do chamador. Ao contrário de HttpRequest.Body, o leitor não copia os dados do pedido para um buffer. No entanto, um leitor é mais complicado de usar do que um stream e deve ser usado com cautela.
Para informações sobre como ler conteúdo de BodyReader, veja PipeReader de pipelines de I/O.
HttpResponse
HttpContext.Response fornece acesso a HttpResponse.
HttpResponse é usado para definir a informação da resposta HTTP enviada de volta ao cliente.
Os membros mais usados em HttpResponse incluem:
| Propriedade | Description | Example |
|---|---|---|
| HttpResponse.StatusCode | O código de resposta. Deve ser definido antes de escrever para o corpo da resposta. | 200 |
| HttpResponse.ContentType | O cabeçalho da resposta content-type. Deve ser definido antes de escrever para o corpo da resposta. |
application/json |
| HttpResponse.Headers | Uma coleção de cabeçalhos de resposta. Deve ser definido antes de escrever para o corpo da resposta. | server=Kestrelx-custom-header=MyValue |
| HttpResponse.Body | A Stream para escrever o corpo da resposta. | Página web gerada |
Definir cabeçalhos de resposta
HttpResponse.Headers fornece acesso aos cabeçalhos de resposta enviados com a resposta HTTP. Existem duas formas de aceder a cabeçalhos usando esta coleção:
- Forneça o nome do cabeçalho ao indexador na coleção de cabeçalhos. O nome do cabeçalho não distingue entre maiúsculas e minúsculas. O indexador pode aceder a qualquer valor de cabeçalho.
- Use as propriedades da coleção de cabeçalhos para obter e definir cabeçalhos HTTP mais comuns. As propriedades fornecem uma forma rápida e baseada no IntelliSense de aceder aos cabeçalhos.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", (HttpResponse response) =>
{
response.Headers.CacheControl = "no-cache";
response.Headers["x-custom-header"] = "Custom value";
return Results.File(File.OpenRead("helloworld.txt"));
});
app.Run();
Uma aplicação não pode modificar cabeçalhos depois de a resposta ter começado. Assim que a resposta começa, os cabeçalhos são enviados ao cliente. Uma resposta é iniciada ao limpar o corpo da resposta ou ao chamar HttpResponse.StartAsync(CancellationToken). A HttpResponse.HasStarted propriedade indica se a resposta já começou. Um erro é lançado ao tentar modificar cabeçalhos depois de a resposta ter começado:
System.InvalidOperationException: Os cabeçalhos são só de leitura, a resposta já começou.
Observação
A menos que o buffering de resposta esteja ativado, todas as operações de escrita (por exemplo, WriteAsync) limpam internamente o corpo da resposta e marcam a resposta como iniciada. O buffering de resposta está desativado por padrão.
Escrever o corpo da resposta
Uma resposta HTTP pode incluir um corpo de resposta. O corpo da resposta são dados associados à resposta, como conteúdo de página web gerado, payload UTF-8 JSON ou um ficheiro.
HttpResponse.Body permite que o corpo da resposta seja escrito com Stream:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/downloadfile", async (IConfiguration config, HttpContext context) =>
{
var filePath = Path.Combine(config["StoredFilesPath"], "helloworld.txt");
await using var fileStream = File.OpenRead(filePath);
await fileStream.CopyToAsync(context.Response.Body);
});
app.Run();
HttpResponse.Body pode ser escrito diretamente ou usado com outras APIs que escrevem num fluxo.
BodyWriter
Uma forma alternativa de escrever o corpo da resposta é usar a HttpResponse.BodyWriter propriedade. A BodyWriter propriedade expõe o corpo de resposta como um PipeWriter. Esta API é oriunda de I/O pipelines, e é uma forma avançada e de alto desempenho de escrever a resposta.
O escritor fornece acesso direto ao corpo da resposta e gere a memória em nome do chamador. Ao contrário de HttpResponse.Body, a operação de escrita não copia os dados da solicitação para um buffer. No entanto, um gravador é mais complicado de usar do que um stream, e o código do gravador deve ser testado minuciosamente.
Para informações sobre como escrever conteúdo para BodyWriter, consulte PipeWriter de pipelines de I/O.
Trailers de resposta
Trailers de resposta de suporte HTTP/2 e HTTP/3. Os trailers são cabeçalhos enviados com a resposta depois de o corpo da resposta estar concluído. Como os trailers são enviados após o corpo da resposta, os trailers podem ser adicionados à resposta a qualquer momento.
O seguinte código define os reboques usando AppendTrailer:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", (HttpResponse response) =>
{
// Write body
response.WriteAsync("Hello world");
if (response.SupportsTrailers())
{
response.AppendTrailer("trailername", "TrailerValue");
}
});
app.Run();
RequestAborted
O HttpContext.RequestAborted token de cancelamento pode ser usado para notificar que o pedido HTTP foi abortado pelo cliente ou servidor. O token de cancelamento deve ser passado para tarefas de longa duração para que possam ser canceladas caso a requisição seja interrompida. Por exemplo, abortar uma consulta à base de dados ou um pedido HTTP para obter dados de volta na resposta.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var httpClient = new HttpClient();
app.MapPost("/books/{bookId}", async (int bookId, HttpContext context) =>
{
var stream = await httpClient.GetStreamAsync(
$"http://contoso/books/{bookId}.json", context.RequestAborted);
// Proxy the response as JSON
return Results.Stream(stream, "application/json");
});
app.Run();
O RequestAborted token de cancelamento não precisa de ser usado para operações de leitura do corpo do pedido porque as leituras são sempre lançadas imediatamente quando o pedido é abortado. O RequestAborted token também é geralmente desnecessário ao escrever corpos de resposta, porque escreve imediatamente no-op quando o pedido é abortado.
Em alguns casos, passar o RequestAborted token nas operações de escrita pode ser uma forma conveniente de forçar um loop de escrita a sair mais cedo com um OperationCanceledException. No entanto, normalmente é melhor passar o RequestAborted token para operações assíncronas que são responsáveis por obter o conteúdo do corpo da resposta.
Observação
As APIs mínimas suportam a ligação HttpContext.RequestAborted direta a um CancellationToken parâmetro.
Abort()
O HttpContext.Abort() método pode ser usado para abortar um pedido HTTP do servidor. Abortar o pedido HTTP dispara imediatamente o HttpContext.RequestAborted token de cancelamento e envia uma notificação ao cliente de que o servidor abortou o pedido.
O middleware no exemplo seguinte:
- Adiciona uma verificação personalizada para pedidos maliciosos.
- Aborta o pedido HTTP se o pedido for malicioso.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
if (RequestAppearsMalicious(context.Request))
{
// Malicious requests don't even deserve an error response (e.g. 400).
context.Abort();
return;
}
await next.Invoke();
});
app.Run();
User
A HttpContext.User propriedade é usada para obter ou definir o utilizador, representado por ClaimsPrincipal, para o pedido. O ClaimsPrincipal é normalmente definido por autenticação ASP.NET Core.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/user/current", [Authorize] async (HttpContext context) =>
{
var user = await GetUserAsync(context.User.Identity.Name);
return Results.Ok(user);
});
app.Run();
Observação
As APIs mínimas suportam a ligação HttpContext.User direta a um ClaimsPrincipal parâmetro.
Features
A HttpContext.Features propriedade fornece acesso à coleção de interfaces de funcionalidades para o pedido atual. Como a coleção de funcionalidades é mutável mesmo no contexto de um pedido, o middleware pode ser usado para modificar a coleção e adicionar suporte para funcionalidades adicionais. Algumas funcionalidades avançadas só estão disponíveis acedendo à interface associada através da coleção de funcionalidades.
O exemplo a seguir:
- Obtém IHttpMinRequestBodyDataRateFeature da coleção de funcionalidades.
- Define MinDataRate como null. Isto remove a taxa mínima de dados que o corpo do pedido deve ser enviado pelo cliente para este pedido HTTP.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/long-running-stream", async (HttpContext context) =>
{
var feature = context.Features.Get<IHttpMinRequestBodyDataRateFeature>();
if (feature != null)
{
feature.MinDataRate = null;
}
// await and read long-running stream from request body.
await Task.Yield();
});
app.Run();
Para mais informações sobre o uso de funcionalidades de pedido e HttpContext, veja Funcionalidades de Pedido em ASP.NET Core.
O HttpContext não é seguro para threads
Este artigo discute principalmente a utilização de HttpContext no fluxo de pedidos e respostas de componentes Blazor Web App, Páginas Razor, controladores, middleware, entre outros. Considere o seguinte ao usar HttpContext fora do fluxo de pedido e resposta:
-
HttpContexté seguro para rosca. Aceder a ela a partir de múltiplos threads pode resultar em resultados imprevisíveis, como exceções e corrupção de dados. - A IHttpContextAccessor interface deve ser usada com cautela. Como sempre, o
HttpContextnão deve ser capturado fora do fluxo de requisição.IHttpContextAccessor:- Depende de AsyncLocal<T>, o que pode ter um impacto negativo no desempenho das chamadas assíncronas.
- Cria uma dependência do "estado ambiente", o que pode tornar os testes mais difíceis.
-
IHttpContextAccessor.HttpContext Pode ser
null, se for acedido fora do fluxo de pedidos. - Para aceder à informação fora
HttpContextdo fluxo de pedido, copie a informação dentro do fluxo de pedido. Tem cuidado em copiar os dados reais e não apenas referências. Por exemplo, em vez de copiar uma referência para umIHeaderDictionary, copie os valores relevantes do cabeçalho ou copie toda a chave do dicionário, uma por uma, antes de sair do fluxo de solicitação. - Não capture
IHttpContextAccessor.HttpContextnum construtor.
O seguinte exemplo regista os ramos do GitHub quando solicitado do /branch endpoint:
using System.Text.Json;
using HttpContextInBackgroundThread;
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpContextAccessor();
builder.Services.AddHostedService<PeriodicBranchesLoggerService>();
builder.Services.AddHttpClient("GitHub", httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
// The GitHub API requires two headers. The Use-Agent header is added
// dynamically through UserAgentHeaderHandler
httpClient.DefaultRequestHeaders.Add(
HeaderNames.Accept, "application/vnd.github.v3+json");
}).AddHttpMessageHandler<UserAgentHeaderHandler>();
builder.Services.AddTransient<UserAgentHeaderHandler>();
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.MapGet("/branches", async (IHttpClientFactory httpClientFactory,
HttpContext context, Logger<Program> logger) =>
{
var httpClient = httpClientFactory.CreateClient("GitHub");
var httpResponseMessage = await httpClient.GetAsync(
"repos/dotnet/AspNetCore.Docs/branches");
if (!httpResponseMessage.IsSuccessStatusCode)
return Results.BadRequest();
await using var contentStream =
await httpResponseMessage.Content.ReadAsStreamAsync();
var response = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(contentStream);
app.Logger.LogInformation($"/branches request: " +
$"{JsonSerializer.Serialize(response)}");
return Results.Ok(response);
});
app.Run();
A API do GitHub requer dois cabeçalhos. O User-Agent cabeçalho é adicionado dinamicamente pelo UserAgentHeaderHandler:
using System.Text.Json;
using HttpContextInBackgroundThread;
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpContextAccessor();
builder.Services.AddHostedService<PeriodicBranchesLoggerService>();
builder.Services.AddHttpClient("GitHub", httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
// The GitHub API requires two headers. The Use-Agent header is added
// dynamically through UserAgentHeaderHandler
httpClient.DefaultRequestHeaders.Add(
HeaderNames.Accept, "application/vnd.github.v3+json");
}).AddHttpMessageHandler<UserAgentHeaderHandler>();
builder.Services.AddTransient<UserAgentHeaderHandler>();
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.MapGet("/branches", async (IHttpClientFactory httpClientFactory,
HttpContext context, Logger<Program> logger) =>
{
var httpClient = httpClientFactory.CreateClient("GitHub");
var httpResponseMessage = await httpClient.GetAsync(
"repos/dotnet/AspNetCore.Docs/branches");
if (!httpResponseMessage.IsSuccessStatusCode)
return Results.BadRequest();
await using var contentStream =
await httpResponseMessage.Content.ReadAsStreamAsync();
var response = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(contentStream);
app.Logger.LogInformation($"/branches request: " +
$"{JsonSerializer.Serialize(response)}");
return Results.Ok(response);
});
app.Run();
A UserAgentHeaderHandler:
using Microsoft.Net.Http.Headers;
namespace HttpContextInBackgroundThread;
public class UserAgentHeaderHandler : DelegatingHandler
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger _logger;
public UserAgentHeaderHandler(IHttpContextAccessor httpContextAccessor,
ILogger<UserAgentHeaderHandler> logger)
{
_httpContextAccessor = httpContextAccessor;
_logger = logger;
}
protected override async Task<HttpResponseMessage>
SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
var contextRequest = _httpContextAccessor.HttpContext?.Request;
string? userAgentString = contextRequest?.Headers["user-agent"].ToString();
if (string.IsNullOrEmpty(userAgentString))
{
userAgentString = "Unknown";
}
request.Headers.Add(HeaderNames.UserAgent, userAgentString);
_logger.LogInformation($"User-Agent: {userAgentString}");
return await base.SendAsync(request, cancellationToken);
}
}
No código anterior, quando o HttpContext é null, a cadeia userAgent é definida como "Unknown". Se possível, HttpContext deve ser explicitamente transmitido ao serviço. Passagem explícita dos dados HttpContext :
- Torna a API de serviço mais utilizável fora do fluxo de pedidos.
- É melhor para o desempenho.
- Torna o código mais fácil de entender e de raciocinar do que depender do estado ambiente.
Quando o serviço tem de aceder a HttpContext, deve ter em conta a possibilidade de HttpContext ser null chamado quando não é chamado de um thread de pedido.
A aplicação também inclui PeriodicBranchesLoggerService, que regista as ramificações abertas do GitHub do repositório especificado a cada 30 segundos.
using System.Text.Json;
namespace HttpContextInBackgroundThread;
public class PeriodicBranchesLoggerService : BackgroundService
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger _logger;
private readonly PeriodicTimer _timer;
public PeriodicBranchesLoggerService(IHttpClientFactory httpClientFactory,
ILogger<PeriodicBranchesLoggerService> logger)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
_timer = new PeriodicTimer(TimeSpan.FromSeconds(30));
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (await _timer.WaitForNextTickAsync(stoppingToken))
{
try
{
// Cancel sending the request to sync branches if it takes too long
// rather than miss sending the next request scheduled 30 seconds from now.
// Having a single loop prevents this service from sending an unbounded
// number of requests simultaneously.
using var syncTokenSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
syncTokenSource.CancelAfter(TimeSpan.FromSeconds(30));
var httpClient = _httpClientFactory.CreateClient("GitHub");
var httpResponseMessage = await httpClient.GetAsync("repos/dotnet/AspNetCore.Docs/branches",
stoppingToken);
if (httpResponseMessage.IsSuccessStatusCode)
{
await using var contentStream =
await httpResponseMessage.Content.ReadAsStreamAsync(stoppingToken);
// Sync the response with preferred datastore.
var response = await JsonSerializer.DeserializeAsync<
IEnumerable<GitHubBranch>>(contentStream, cancellationToken: stoppingToken);
_logger.LogInformation(
$"Branch sync successful! Response: {JsonSerializer.Serialize(response)}");
}
else
{
_logger.LogError(1, $"Branch sync failed! HTTP status code: {httpResponseMessage.StatusCode}");
}
}
catch (Exception ex)
{
_logger.LogError(1, ex, "Branch sync failed!");
}
}
}
public override Task StopAsync(CancellationToken stoppingToken)
{
// This will cause any active call to WaitForNextTickAsync() to return false immediately.
_timer.Dispose();
// This will cancel the stoppingToken and await ExecuteAsync(stoppingToken).
return base.StopAsync(stoppingToken);
}
}
PeriodicBranchesLoggerService é um serviço alojado, que funciona fora do fluxo de pedidos e respostas. O registo a partir do PeriodicBranchesLoggerService tem um null HttpContext. O PeriodicBranchesLoggerService foi escrito para não depender do HttpContext.
using System.Text.Json;
using HttpContextInBackgroundThread;
using Microsoft.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpContextAccessor();
builder.Services.AddHostedService<PeriodicBranchesLoggerService>();
builder.Services.AddHttpClient("GitHub", httpClient =>
{
Recursos adicionais
Para mais informações sobre como aceder ao HttpContext, veja Aceder ao HttpContext no ASP.NET Core.