Skip to content

HttpRouter

Repositório: julienschmidt/httprouter: A high performance HTTP request router that scales well (github.com)

O componente de roteamento do Gin usa HttpRouter, que também é um componente de roteamento leve e de alto desempenho. Todo o componente consiste em apenas três arquivos .go, o código é bastante conciso, e possui as seguintes características principais.

Correspondência Um-para-Um: Uma requisição só pode corresponder a zero ou uma rota, e é benéfico para otimização de SEO.

Correção Automática de Caminho: Escolha livremente o estilo de URL preferido, mesmo que haja uma barra a mais ou a menos, haverá redirecionamento automático. Se houver erros de maiúsculas/minúsculas, a busca ignorará maiúsculas/minúsculas para redirecionamento correto.

Análise Automática de Parâmetros de Rota: Basta dar um nome ao segmento de caminho, e o roteador passará os valores dinâmicos para você. Devido ao design do roteador, a sobrecarga de análise de parâmetros de caminho é muito baixa.

Zero Lixo: Durante o processo de alocação e agendamento de rotas, nenhum lixo de memória é gerado.

Suporte a RefstfulAPI: O design do roteador incentiva APIs Restful razoavelmente hierárquicas.

Tratamento de Erros: É possível definir um manipulador de erros para lidar com anomalias nas requisições, o roteador as capturará e registrará, depois redirecionará para a página de erro.

Uso Básico

Assim como no springboot, uma função é vinculada a uma URL e corresponde a um processador.

go
package main

import (
   "fmt"
   "github.com/julienschmidt/httprouter"
   "log"
   "net/http"
)

func Hello(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
   fmt.Fprintf(w, "<h1>Hello World!")
}

func main() {
   router := httprouter.New()
   router.GET("/hello", Hello)
   log.Fatal(http.ListenAndServe(":8080", router))
}

Em seguida, usando um navegador ou qualquer ferramenta de teste de API, acesse 127.0.0.1:8080 para ver o conteúdo correto. Podemos ver que HttpRouter apenas faz o roteamento, e na verdade ainda usa o componente padrão net/http, o gin também é assim, apenas o encapsulamento é relativamente mais profundo.

Parâmetros Nomeados

go
package main

import (
   "fmt"
   "github.com/julienschmidt/httprouter"
   "log"
   "net/http"
)

func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
   fmt.Fprintf(w, "<h1>Hello World!")
}

func main() {
   router := httprouter.New()
   router.GET("/hello:name", Hello)
   log.Fatal(http.ListenAndServe(":8080", router))
}

Desta vez, um :name foi adicionado após a rota, onde name é um parâmetro nomeado, e é possível acessar o slice de parâmetros através de httprouter.Params, podendo obter o parâmetro por índice ou ByName(name). Da mesma forma, é possível usar http.Handler e http.HandlerFunc como httprouter.Handle, a rota em si implementa sua interface, como no exemplo abaixo.

go
func Hello(w http.ResponseWriter, r *http.Request) {
    params := httprouter.ParamsFromContext(r.Context())
    //params := r.Context().Value(httprouter.ParamsKey) também funciona

    fmt.Fprintf(w, "hello, %s!\n", params.ByName("name"))
}

Quando a rota vinculada ao método é /user/:user, as seguintes situações de correspondência de URL ocorrem:

 /user/gordon              corresponde
 /user/you                 corresponde
 /user/gordon/profile      não corresponde
 /user/                    não corresponde

Você não pode registrar /user/new e /user/:user no mesmo método de requisição, cada método de requisição deve ser independente.

Capturar Todos os Parâmetros

O segundo tipo é capturar todos os parâmetros, como o nome sugere, eles correspondem a tudo, portanto devem estar no final do Pattern.

go
Pattern: /src/*filepath

 /src/                     corresponde
 /src/somefile.go          corresponde
 /src/subdir/somefile.go   corresponde

O princípio de funcionamento do HttpRouter é construir uma grande quantidade de árvores de prefixo, interessados podem consultar: httprouter package - github.com/julienschmidt/httprouter - Go Packages.

OPTIONS & CORS

Algumas pessoas podem desejar modificar a resposta automática para OPTIONS e definir alguns cabeçalhos de resposta para adaptar a requisição de pré-voo CORS, essas necessidades podem ser atendidas usando o manipulador Router.GlobalOPTIONS.

go
router.GlobalOPTIONS = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    if r.Header.Get("Access-Control-Request-Method") != "" {
        // Define cabeçalhos de resposta CORS
        header := w.Header()
        header.Set("Access-Control-Allow-Methods", r.Header.Get("Allow"))
        header.Set("Access-Control-Allow-Origin", "*")
    }

    // Adapta código de status 204
    w.WriteHeader(http.StatusNoContent)
})

Manipulador NOT FOUND

TIP

Pode ser necessário desativar Router.HandleMethodNotAllowed para evitar alguns problemas.

go
router.NotFound = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
  // sua lógica
})

Validação Básica

go
package main

import (
  "fmt"
  "log"
  "net/http"

  "github.com/julienschmidt/httprouter"
)

func BasicAuth(h httprouter.Handle, requiredUser, requiredPassword string) httprouter.Handle {
  return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    // Obtém credenciais de autenticação básica
    user, password, hasAuth := r.BasicAuth()

    if hasAuth && user == requiredUser && password == requiredPassword {
      // Delega a requisição ao manipulador fornecido
      h(w, r, ps)
    } else {
      // Caso contrário, solicita autenticação
      w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
      http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
    }
  }
}

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
  fmt.Fprint(w, "Not protected!\n")
}

func Protected(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
  fmt.Fprint(w, "Protected!\n")
}

func main() {
  user := "gordon"
  pass := "secret!"

  router := httprouter.New()
  router.GET("/", Index)
  router.GET("/protected/", BasicAuth(Protected, user, pass))

  log.Fatal(http.ListenAndServe(":8080", router))
}

Golang por www.golangdev.cn edit