Manipulação de requisições
Esta página foi traduzida automaticamente do inglês. Ler o original
Handlers de requisição, também conhecidos como “middlewares”, são funções que são executadas antes ou depois que uma requisição é processada no router. Eles podem ser definidos por rota ou por router.
Existem dois tipos de handlers de requisição:
- BeforeResponse: indica que o handler de requisição será executado antes de chamar a ação do router.
- AfterResponse: indica que o handler de requisição será executado após chamar a ação do router. Enviar uma resposta HTTP neste contexto sobrescreverá a resposta da ação do router.
Ambos os handlers de requisição podem sobrescrever a resposta da função de callback real do router. Além disso, os handlers de requisição podem ser úteis para validar uma requisição, como autenticação, conteúdo ou qualquer outra informação, como armazenar dados, logs ou outras etapas que podem ser realizadas antes ou depois de uma resposta.

Dessa forma, um handler de requisição pode interromper toda essa execução e retornar uma resposta antes de concluir o ciclo, descartando todo o resto no processo.
Exemplo: suponha que um handler de autenticação de usuário não o autentique. Ele impedirá que o ciclo de vida da requisição continue e ficará pendente. Se isso acontecer no handler de requisição na posição dois, o terceiro e os subsequentes não serão avaliados.

Criando um handler de requisição #
Para criar um handler de requisição, podemos criar uma classe que herda a interface IRequestHandler, no seguinte formato:
public class AuthenticateUserRequestHandler : IRequestHandler
{
public RequestHandlerExecutionMode ExecutionMode { get; init; } = RequestHandlerExecutionMode.BeforeResponse;
public HttpResponse? Execute(HttpRequest request, HttpContext context)
{
if (request.Headers.Authorization != null)
{
// Returning null indicates that the request cycle can be continued
return null;
}
else
{
// Returning an HttpResponse object indicates that this response will overwrite adjacent responses.
return new HttpResponse(System.Net.HttpStatusCode.Unauthorized);
}
}
}No exemplo acima, indicamos que se o cabeçalho Authorization estiver presente na requisição, ela deve continuar e o próximo handler de requisição ou o callback do router deve ser chamado, seja qual for o próximo. Se o handler de requisição for executado após a resposta por meio da propriedade ExecutionMode e retornar um valor não nulo, ele sobrescreverá a resposta do router.
Sempre que um handler de requisição retorna null, isso indica que a requisição deve continuar e o próximo objeto deve ser chamado ou o ciclo deve terminar com a resposta do router.
Aviso
Uma instância de handler de requisição é criada uma única vez e compartilhada por todas as requisições que ela manipula, incluindo requisições concorrentes. Nunca armazene estado individual da requisição na própria classe do handler (campos ou propriedades como o usuário atual, um token analisado ou um temporizador), pois esse valor vaza para outras requisições. Armazene valores por requisição no RequestBag do HttpContext em vez disso:
public HttpResponse? Execute(HttpRequest request, HttpContext context)
{
// wrong: this.currentUser = FindUser(request);
context.RequestBag.Set(FindUser(request));
return null;
}Se você herdar da classe incorporada RequestHandler, pode retornar Next() para tornar essa intenção explícita:
public class AuthenticateUserRequestHandler : RequestHandler
{
public override HttpResponse? Execute(HttpRequest request, HttpContext context)
{
if (request.Headers.Authorization is not null)
return Next();
return new HttpResponse(System.Net.HttpStatusCode.Unauthorized);
}
}Para handlers que precisam de I/O, herde de AsyncRequestHandler:
public class LoadUserRequestHandler : AsyncRequestHandler
{
public override async Task<HttpResponse?> ExecuteAsync(HttpRequest request, HttpContext context)
{
var user = await UserRepository.FindAsync(request.Headers.Authorization, request.DisconnectToken);
if (user is null)
return new HttpResponse(System.Net.HttpStatusCode.Unauthorized);
request.Bag.Set(user);
return Next();
}
}Pequenos handlers inline também podem ser criados com RequestHandler.Create ou AsyncRequestHandler.Create:
var requireJson = RequestHandler.Create((request, context) =>
{
if (request.Headers.ContentType?.Contains("application/json") == true)
return null;
return new HttpResponse(System.Net.HttpStatusCode.UnsupportedMediaType);
});Associando um handler de requisição a uma única rota #
Você pode definir um ou mais handlers de requisição para uma rota.
mainRouter.Map(RouteMethod.Get, "/", IndexPage, new IRequestHandler[]
{
new AuthenticateUserRequestHandler(), // before request handler
new ValidateJsonContentRequestHandler(), // before request handler
// -- method IndexPage will be executed here
new WriteToLogRequestHandler() // after request handler
});Ou criando um objeto Route:
Route indexRoute = Route.Get("/", IndexPage);
indexRoute.RequestHandlers = new IRequestHandler[]
{
new AuthenticateUserRequestHandler()
};
mainRouter.Map(indexRoute);Associando um handler de requisição a um router #
Você pode definir um handler de requisição global que será executado em todas as rotas de um router.
mainRouter.GlobalRequestHandlers = new IRequestHandler[]
{
new AuthenticateUserRequestHandler()
};Associando um handler de requisição a um atributo #
Você pode definir um handler de requisição em um atributo de método junto com um atributo de rota.
public class MyController
{
[RouteGet("/")]
[RequestHandler<AuthenticateUserRequestHandler>]
static HttpResponse Index(HttpRequest request)
{
return new HttpResponse() {
Content = new StringContent("Hello world!")
};
}
}Observe que é necessário passar o tipo do handler de requisição desejado e não uma instância de objeto. Dessa forma, o handler de requisição será instanciado pelo analisador do router. Você pode passar argumentos no construtor da classe com a propriedade ConstructorArguments.
Exemplo:
[RequestHandler<AuthenticateUserRequestHandler>("arg1", 123, ...)]
public HttpResponse Index(HttpRequest request)
{
return res = new HttpResponse() {
Content = new StringContent("Hello world!")
};
}Você também pode criar seu próprio atributo que implementa RequestHandler:
public class AuthenticateAttribute : RequestHandlerAttribute
{
public AuthenticateAttribute() : base(typeof(AuthenticateUserRequestHandler), ConstructorArguments = new object?[] { "arg1", 123, ... })
{
;
}
}E usá-lo assim:
[Authenticate]
static HttpResponse Index(HttpRequest request)
{
return res = new HttpResponse() {
Content = new StringContent("Hello world!")
};
}Ignorando um handler de requisição global #
Depois de definir um handler de requisição global em uma rota, você pode ignorar esse handler em rotas específicas.
var myRequestHandler = new AuthenticateUserRequestHandler();
mainRouter.GlobalRequestHandlers = new IRequestHandler[]
{
myRequestHandler
};
Route publicRoute = Route.Get("/", IndexPage);
publicRoute.Name = "My route";
publicRoute.BypassGlobalRequestHandlers = new IRequestHandler[]
{
myRequestHandler, // ok: the same instance of what is in the global request handlers
new AuthenticateUserRequestHandler() // wrong: will not skip the global request handler
};
mainRouter.Map(publicRoute);Nota
Se você estiver ignorando um handler de requisição, deve usar a mesma referência da instância que criou anteriormente para pular. Criar outra instância de handler de requisição não irá pular o handler global, pois sua referência mudará. Lembre-se de usar a mesma referência de handler de requisição usada tanto em GlobalRequestHandlers quanto em BypassGlobalRequestHandlers.