Menu
Coddy logo textTech

Middleware como Decorator

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy. Lição 97 de 107.

Middleware é uma das aplicações mais práticas do padrão Decorator em Go. No desenvolvimento web, as funções de middleware envolvem manipuladores HTTP para adicionar preocupações transversais, como registro de logs, autenticação ou medição de tempo, sem modificar o manipulador original.

A principal ideia é que o middleware recebe um handler e retorna um novo handler com comportamento adicional. Isso segue exatamente o padrão Decorator: a mesma interface na entrada e na saída, com funcionalidade aprimorada:

type Handler func(request string) string

func LoggingMiddleware(next Handler) Handler {
    return func(request string) string {
        result := next(request)
        return "[LOG] " + result
    }
}

func AuthMiddleware(next Handler) Handler {
    return func(request string) string {
        return "[AUTH] " + next(request)
    }
}

Cada middleware envolve o próximo manipulador na cadeia, adicionando seu comportamento antes ou depois de chamar o manipulador envolvido. Você pode empilhar vários middlewares compondo-os:

func MainHandler(request string) string {
    return "Response to: " + request
}

handler := LoggingMiddleware(AuthMiddleware(MainHandler))
fmt.Println(handler("GET /users"))
// [LOG] [AUTH] Resposta para: GET /users

A ordem de encapsulamento determina a ordem de execução: o middleware mais externo é executado primeiro. Esse padrão é amplamente utilizado em frameworks web Go para pipelines de processamento de requisições, facilitando a adição ou remoção de funcionalidades sem alterar a lógica central do handler.

challenge icon

Desafio

Fácil

Vamos criar um pipeline de processamento de requisições usando o padrão Middleware como Decorator! Você criará um sistema em que funções de middleware envolvem handlers para adicionar funcionalidades como temporização, validação e formatação — tudo isso sem modificar a lógica principal do handler.

Você organizará seu código em três arquivos:

  • handler.go: defina seu tipo de handler e o handler base que processa as requisições.

    Crie um tipo Handler como uma função que recebe uma string de requisição e retorna uma string de resposta:

    type Handler func(request string) string

    Implemente uma função BaseHandler que retorne Processed: [request] para qualquer requisição recebida.

  • middleware.go: crie suas funções de middleware que envolvem handlers com comportamentos adicionais.

    Crie três funções de middleware, cada uma recebendo um Handler e retornando um novo Handler:

    • TimingMiddleware: envolve a resposta com informações de temporização, retornando [TIMING] [response]
    • ValidationMiddleware: adiciona um prefixo de validação, retornando [VALID] [response]
    • UppercaseMiddleware: converte toda a resposta para letras maiúsculas

    Cada middleware deve chamar o handler envolvido e aprimorar seu resultado.

  • main.go: crie um pipeline de processamento compondo os middlewares.

    Leia o número de camadas de middleware a serem aplicadas. Em seguida, para cada camada, leia o tipo de middleware (timing, validation ou uppercase) e envolva seu handler atual com esse middleware. Aplique-os na ordem em que forem lidos: o primeiro middleware lido se torna o wrapper mais externo.

    Depois de criar o pipeline, leia a string de requisição, processe-a por meio do handler composto e imprima o resultado.

As seguintes entradas serão fornecidas:

  • Linha 1: número de camadas de middleware
  • Linhas seguintes: um tipo de middleware por linha (timing, validation ou uppercase)
  • Última linha: a requisição a ser processada

Por exemplo, considerando:

2
timing
validation
GET /users

Sua saída deverá ser:

[TIMING] [VALID] Processed: GET /users

E considerando:

3
uppercase
timing
validation
POST /data

Sua saída deverá ser:

[TIMING] [VALID] PROCESSED: POST /DATA

E considerando:

1
uppercase
hello world

Sua saída deverá ser:

PROCESSED: HELLO WORLD

E considerando:

0
simple request

Sua saída deverá ser:

Processed: simple request

Observe como a ordem de envolvimento dos middlewares afeta a saída: o primeiro middleware aplicado (o mais externo) é executado primeiro e vê o resultado final de todos os middlewares internos. O middleware de letras maiúsculas no segundo exemplo é executado por último (o mais interno), portanto converte a resposta base para letras maiúsculas antes que a temporização e a validação adicionem seus prefixos!

Experimente você mesmo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Leia o número de camadas de middleware
	nLine, _ := reader.ReadString('\n')
	n, _ := strconv.Atoi(strings.TrimSpace(nLine))

	// Leia os tipos de middleware
	middlewareTypes := make([]string, n)
	for i := 0; i < n; i++ {
		line, _ := reader.ReadString('\n')
		middlewareTypes[i] = strings.TrimSpace(line)
	}

	// Leia a requisição
	request, _ := reader.ReadString('\n')
	request = strings.TrimSpace(request)

	// TODO: Comece com o BaseHandler

	// TODO: Construa o pipeline envolvendo o handler com middleware
	// Aplique os middlewares em ordem - o primeiro middleware lido se torna o wrapper mais externo
	// Dica: Percorra middlewareTypes em ordem REVERSA para conseguir isso
	// O último middleware aplicado será o mais externo (executa primeiro)

	// TODO: Processe a requisição através do handler composto e imprima o resultado
	fmt.Println("TODO: implement pipeline")
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Compilador de Go online