Middlewares
Resumidamente, middlewares são funções a serem executadas durante uma requisição antes da ação principal propriamente dita. Um exemplo de middleware muito comum é a verificação de autenticidade do usuário, verificar se ele possui permissão antes de poder editar ou ver determinada página ou registro.
Existe a possibilidade de criar middleware global, por aplicação e por rota. Middlewares globais serão executados em todas as aplicações e em todas as suas rotas. Middlewares por aplicação quando definidos serão executados apenas nas rotas da aplicação em questão. E por fim, os middlewares por rota são aqueles que serão executados apenas quando a rota específica for requisitada.
Middleware global e por aplicação
Os middlewares globais são inseridos em /dev/src/http/kernel.ts, e os middlewares por aplicação em /dev/src/app/NomeDaAplicacao/http/kernel.ts, ambos possuem a mesma estrutura de criação. Por motivo de melhor organização, é recomendado a criação de middlewares em arquivos externos e em um pasta que condiz com o seu objetivo.
No exemplo a seguir criaremos um middleware global que simplesmente mostra uma mensagem no console log antes de chamar a ação principal.
Criaremos um arquivo /dev/src/http/Middlewares/Log.ts com o seguinte código:
import { Request, Response } from '@jsagon/core'
export const LogMiddleware = (req: Request, res: Response, next: Function): void => {
console.log("Log middleware")
next()
}
No exemplo anterior, exportamos uma função seguindo a mesma convenção de uma rota padrão para podermos usá-la fora do arquivo. O terceiro parâmetro, next, é responsável por prosseguir com a execução para a próxima execução.
Agora é preciso editar o arquivo /dev/src/http/kernel.ts para registrar o middleware anterior. Para isso, faça como o exemplo a seguir:
import { LogMiddleware } from './Middlewares/Log'
import { AuthMiddleware } from './Middlewares/Auth'
export default {
// Middlewares that should run for every route in project - Middleware que rodará em todas as rotas do projeto
globalMiddlewares: {
registered: {
auth: AuthMiddleware
},
// Middlewares that should run before a route - Middleware a ser executado antes da rota
before: [
LogMiddleware,
'auth'
],
// Middlewares that should run after a route - Middleware a ser executado depois da rota
after: []
}
}
No código anterior, os middlewares de Log e Auth são importados de seus arquivos e adicionados ao kernel. O Auth é registrado com um alias, um apelido, e posteriormente adicionado ao before dos middlewares globais. O Log é passado diretamente para o before. Inicializando a aplicação e executando qualquer requisição no sistema, as funções deverão ser executadas e suas mensagens mostradas no console.
Conceitos: before e after
Tanto os middlewares globais como os por aplicação possuem as chaves "before" e "after". A lista de middlewares registrada em "before" serão executadas antes da rota principal ser executada, sendo este o modelo de middleware mais comumente usado. E a lista de middlewares registrada em "after" é para aquelas funções que serão executadas após a execução da rota principal, sendo estes usados para propósitos muito específicos.
Obs.: os middlewares adicionados em "before" necessitam da execução do "next" para prosseguir na cadeia de execução.
Abaixo veremos como adicionar os middlewares diretamente nas rotas em dois cenários, utilizando decorators ou o construtor de rotas.
Middlewares - Decorators
Um controlador com middlewares definidos em suas actions fica assim:
import { ControllerBase, Request, Response, Controller, Post, Get, Middleware} from '@jsagon/core'
@Controller()
class HomeController extends ControllerBase {
@Get('/admin')
@Middleware('auth')
public async admin(req: Request, res: Response) {
// tela de administrador
}
@Post('/login')
@Middleware([], ['auth'])
public async login(req: Request, res: Response) {
// realizar login no sistema
}}
}
export default HomeController
No exemplo anterior foi definido que para acessar a tela de admin, o middleware de autenticação precisa ser executado previamente. Caso o middleware 'auth' esteja registrado globalmente, não é necessário especificar como no exemplo anterior. No método de login, o auth foi passado como segundo parâmetro, skip, ou seja, pular a execução do middleware quando chamado a rota /login.
Definição:
Middleware(string | string[] | Function | Function[], string | string[]) | Primeiro parâmetro, middlewares a serem executados na chamada da rota. Segundo parâmetro, middlewares que deverão ser pulados na execução da rota. |
Middlewares - Construtor de Rota
Utilizando do construtor de rotas, é possível adicionar middlewares específicos para determinadas rotas.
É possível adicionar middlewares no instanciamento do construtor, ou chamando seus métodos setBaseMiddleware ou addBaseMiddleware. Tanto a passagem do middleware no instancionamento quanto no setBaseMiddleware aceitam um único middleware ou um array de middlewares, já o addBaseMiddleware aceitando apenas um único.
Obs.: O setBaseMiddleware sobrescreve a lista de middlewares anteriores já definidas. E o addBaseMiddleware adiciona um middleware a lista atual de middlewares.
Um exemplo prático do exemplo anterior:
import { Route } from '@jsagon/core'
import Log from '../Middlewares/Log'
import Auth from '../Middlewares/Auth'
import LogAuth from '../Middlewares/LogAuth'
import HomeController from '../Controller/HomeController'
// Passando um middleware Log no construtor.
const route = Route('/', HomeController, [Log])
.index()
.get({ uri: '/entrar', action: 'login' })
// Registrando um único ou uma nova lista de middlewares base. Todos os middlewares anteriores não serão mais executados nas rotas seguintes
// Podendo ser passado também como .setBaseMiddleware(Auth) por ser um único.
.setBaseMiddleware([Auth])
.get({ uri: '/admin', action: 'viewAdmin' })
// Adicionando um novo middleware sem a remoção dos anteriores.
.addBaseMiddleware(LogAuth)
.get({ uri: '/sair', action: 'logout' })
export default [ route ]
Quando um middleware é definido, todas as rotas registradas após ele farão parte do seu escopo. No exemplo anterior, quando foi registrado o middleware Auth (".setBaseMiddleware([Auth])"), em todas as rotas posteriores, admin e sair, será verificado se o usuário tem permissão de acesso. Neste caso, até que um novo setBaseMiddleware seja registrado.
Ordem de execução
Os middlewares possuem a seguinte ordem de execução: Middlewares globais > Middlewares por aplicação > Middlewares por rota. E cada um seguindo a sua ordenação de como foi adicionada a lista.