Go 1.22 e o novo router HTTP
Tags: go, webdev, programming, beginners
Published: 2024-02-07 00:50:47

## Introdução

O `http.ServeMux` do Go ganhou novas features muito aguardadas pelos desenvolvedores, e como sempre, essas features são retrocompatíveis.

Em resumo, as features são:

- **Match com base no método HTTP**

```go
http.Handle("GET /users", ...)
```

- **Variavéis no path da requisição**

```go
http.Handle("GET /users/{id}", ...)
```

- **Match exato de paths terminados em barra**

```go
// match para qualquer path iniciado com /static/
http.Handle("GET /static/", ...)

// match somente GET /static/
http.Handle("GET /static/{$}", ...)
```

Essas mudanças vão reduzir bastante a necessidade de utilizar bibliotecas de routers, e como vai para a stdlib, vai ter a garantia de compatibilidade do Go 1.

## 1. Como funciona o `http.ServeMux`

O `ServeMux` tem um comportamento diferente da maioria dos routers.

**Caminhos terminados em barra são como pastas**

```go
http.Handle("/assets/", ...)
```

Esse handler processa qualquer requisição com prefixo `/assets/`, como:

- `GET /assets/`
- `GET /assets/img/img.png`
- `DELETE /assets/abcd`

**O match é feito com o path exato se não for terminado em barra**

```go
http.Handle("/", ...)
http.Handle("/assets", ...)
```

Nesse caso o segundo pattern não termina em barra, então só requisições para exatamente `/assets` vão ser processadas por esse handler, e qualquer outra será processada pelo primeiro handler.

Obs.: O caminho `/` dá match em qualquer requisição que não dê match em outro handler, então ele é geralmente usado para a página raiz **e** página de not found.

**O match é feito com o path mais similar**

Se você tiver:

```go
http.Handle("/users/", ...)
http.Handle("/users/123", ...)
http.Handle("/users/books/", ...)
```

- Uma requisição para `/users/` vai cair no primeiro handler.
- Uma requisição para `/users/123` vai cair no segundo handler.
- Uma requisição para `/users/1234` vai cair no primeiro handler.
- Uma requisição para `/users/books/123` vai cair no terceiro handler.
- Uma requisição para `/users/a/b/c/d` também cai no primeiro handler

## 2. Entendendo o match com base no método HTTP

O comportamento das funções `Handle` e `HandleFunc` vai se manter igual para códigos existentes por conta da retrocompatibilidade da linguagem, então se você não especificar um método HTTP, seu handler vai continuar dando match em qualquer método.

Obs.: Um handler para o método GET vai também bater para requisições do método HEAD.

```go
// GET /users
// HEAD /users
http.HandleFunc("GET /users", ...)


// somente DELETE /users
http.HandleFunc("DELETE /users", ...)
```

## 3. Utilizando variáveis de path

Podemos capturar o valor de uma variável de path usando o `PathValue`.

```go
http.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")
    ...
})
```

## 4. Utilizando matches exatos

Ao utilizar o `{$}` indicamos que um handler terminado em barra deve somente bater com aquele path, e não com qualquer path com aquele prefixo.

```go
// redirecionar um request de / para /home
http.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) {
    http.Redirect(w, r, "/home")
})

// qualquer outro path que não de match em outro handler vai dar um not found
http.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
    http.NotFound(w, r)
})
```

## Conclusão

Apesar de simples, essa foi uma mudança bem importante na stdlib do Go, tendo em vista que é uma linguagem muito utilizada para desenvolver servidores Web.

Mas é claro que essas foram só algumas das muitas novidades do Go 1.22 e pretendo falar de outras que foram bem expressivas, como no `for` loop, pacote `rand`, e outros em próximos artigos.