Desafio Go

Middleware com closures

Enunciado

Crie um sistema de middlewares usando closures. Defina um tipo Handler que é func(string) string. Crie uma função aplicarMiddlewares que recebe um Handler e uma lista de middlewares (cada um é func(Handler) Handler) e retorna o Handler final. Implemente dois middlewares: comPrefixo que adiciona ">> " ao início da resposta, e comMaiusculas que converte a resposta para maiúsculas. No main, aplique ambos a um handler que retorna "olá, " + nome e teste com "mundo".

Requisitos

  • O tipo Handler é func(string) string.
  • Os middlewares têm assinatura func(Handler) Handler.
  • A função aplicarMiddlewares aplica os middlewares na ordem em que aparecem na lista.
  • A saída final para "mundo" é ">> OLÁ, MUNDO".

Código inicial

package main

import (
	"fmt"
	"strings"
)

type Handler func(string) string

// comPrefixo adiciona ">> " ao início da resposta.
func comPrefixo(h Handler) Handler {
	// implemente aqui
	return nil
}

// comMaiusculas converte a resposta para maiúsculas.
func comMaiusculas(h Handler) Handler {
	// implemente aqui
	return nil
}

// aplicarMiddlewares aplica os middlewares na ordem.
func aplicarMiddlewares(h Handler, middlewares ...func(Handler) Handler) Handler {
	// implemente aqui
	return nil
}

func main() {
	// handler base
	base := func(nome string) string {
		return "olá, " + nome
	}
	// aplique os middlewares e imprima o resultado
}

Saída esperada

>> OLÁ, MUNDO
Ver dica

Cada middleware retorna uma nova função que chama o handler original e modifica o resultado. Em aplicarMiddlewares, itere sobre os middlewares e vá envolvendo o handler. A ordem importa: o primeiro middleware da lista é o mais externo.

Mostrar solução
package main

import (
	"fmt"
	"strings"
)

type Handler func(string) string

// comPrefixo adiciona ">> " ao início da resposta.
func comPrefixo(h Handler) Handler {
	return func(s string) string {
		return ">> " + h(s)
	}
}

// comMaiusculas converte a resposta para maiúsculas.
func comMaiusculas(h Handler) Handler {
	return func(s string) string {
		return strings.ToUpper(h(s))
	}
}

// aplicarMiddlewares aplica os middlewares na ordem em que aparecem.
func aplicarMiddlewares(h Handler, middlewares ...func(Handler) Handler) Handler {
	for _, m := range middlewares {
		h = m(h)
	}
	return h
}

func main() {
	base := func(nome string) string {
		return "olá, " + nome
	}

	// Aplica comPrefixo e depois comMaiusculas
	handler := aplicarMiddlewares(base, comPrefixo, comMaiusculas)
	fmt.Println(handler("mundo"))
}

Passo a passo

  1. Definimos Handler como func(string) string, uma função que recebe e retorna string.
  2. comPrefixo recebe um Handler e retorna uma nova closure. Essa closure chama o handler original e adiciona ">> " ao resultado.
  3. comMaiusculas segue o mesmo padrão, mas aplica strings.ToUpper no resultado do handler original.
  4. aplicarMiddlewares itera sobre a lista de middlewares. Para cada um, substitui h por m(h), envolvendo o handler anterior.
  5. Como comPrefixo vem primeiro, ele envolve o base. Depois comMaiusculas envolve o resultado de comPrefixo.
  6. No main, chamamos handler("mundo"). A execução ocorre de dentro para fora: base gera "olá, mundo", comPrefixo adiciona ">> " resultando em ">> olá, mundo", e comMaiusculas transforma em ">> OLÁ, MUNDO".
  7. O resultado é impresso.

Por que funciona

Cada middleware é uma closure que captura o handler anterior. Ao encadear, criamos uma composição de funções. A ordem da lista define a ordem de execução: o primeiro middleware é o mais externo, então sua transformação ocorre por último na saída. Isso é o padrão de middlewares em servidores HTTP, por exemplo.

Erros comuns

  • Inverter a ordem dos middlewares: se passar comMaiusculas antes de comPrefixo, a saída seria ">> OLÁ, MUNDO"? Não, seria ">> OLÁ, MUNDO"? Vamos ver: comMaiusculas envolve base, depois comPrefixo envolve esse resultado. A execução: base -> "olá, mundo", comMaiusculas -> "OLÁ, MUNDO", comPrefixo -> ">> OLÁ, MUNDO". O resultado é o mesmo! Mas em middlewares reais, a ordem altera o comportamento.
  • Esquecer de retornar a closure: se comPrefixo retornar nil, o programa entra em pânico ao chamar o handler.
  • Modificar o handler original em vez de envolver: não faça h = func... dentro do middleware; retorne uma nova função.

Outra forma de resolver

Usar uma struct com método Aplicar para middlewares, mas closures são mais concisas e idiomáticas em Go para esse caso.

Saída esperada

>> OLÁ, MUNDO