Skip to content

HttpRouter

Адрес репозитория: julienschmidt/httprouter: A high performance HTTP request router that scales well (github.com)

В качестве компонента маршрутизации Gin используется HttpRouter, который также является лёгким и высокопроизводительным компонентом маршрутизации. Весь компонент состоит всего из трёх файлов .go, код очень лаконичный. Его основные особенности:

Однозначное сопоставление: Один запрос может быть сопоставлен только с нулём или одним маршрутом, что благоприятно для SEO-оптимизации.

Автоматическое исправление пути: Выбирайте любой стиль URL, который вам нравится. Даже если добавлен или пропущен слэш, произойдёт автоматическое перенаправление. Если есть ошибки регистра, поиск также проигнорирует регистр и выполнит правильное перенаправление.

Автоматический разбор параметров маршрута: Просто дайте имя сегменту пути, и маршрутизатор передаст вам динамические значения. Благодаря дизайну маршрутизатора накладные расходы на разбор параметров пути очень низки.

Нулевой мусор: В процессе распределения и планирования маршрутов не создаётся никакого мусора памяти.

Поддержка RestfulAPI: Дизайн маршрутизатора поощряет разумную многоуровневую Restful API.

Обработка ошибок: Можно установить обработчик ошибок для обработки исключений в запросах, маршрутизатор перехватит и запишет их, затем перенаправит на страницу ошибки.

Базовое использование

Как и в springboot, одна функция привязывается к одному URL и соответствует одному обработчику.

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))
}

Затем, используя браузер или любой инструмент тестирования интерфейсов, введите 127.0.0.1:8080, и вы увидите правильное содержимое. Мы можем видеть, что HttpRouter выполняет только маршрутизацию, фактически всё ещё используется компонент net/http по умолчанию. gin также работает аналогично, просто инкапсуляция немного глубже.

Именованные параметры

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))
}

На этот раз после маршрута добавлен :name, где name — это именованный параметр. Доступ к срезу параметров можно получить через httprouter.Params, можно получить параметр по индексу или через ByName(name). Аналогично, вы можете использовать http.Handler и http.HandlerFunc как httprouter.Handle, поскольку маршрутизатор реализует этот интерфейс. Например, следующий пример.

go
func Hello(w http.ResponseWriter, r *http.Request) {
    params := httprouter.ParamsFromContext(r.Context())
    //params := r.Context().Value(httprouter.ParamsKey) также можно

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

Когда метод привязан к маршруту /user/:user, следующие несколько URL будут сопоставлены так:

 /user/gordon              сопоставление
 /user/you                 сопоставление
 /user/gordon/profile      нет сопоставления
 /user/                    нет сопоставления

Вы не можете зарегистрировать /user/new и /user/:user для одного и того же метода запроса. Каждый метод запроса должен быть независимым.

Захват всех параметров

Второй тип — захват всех параметров. Как следует из названия, они сопоставляют всё, поэтому должны находиться в конце Pattern.

go
Pattern: /src/*filepath

 /src/                     сопоставление
 /src/somefile.go          сопоставление
 /src/subdir/somefile.go   сопоставление

Принцип работы HttpRouter заключается в построении большого количества префиксных деревьев. Заинтересованные могут ознакомиться: httprouter package - github.com/julienschmidt/httprouter - Go Packages.

OPTIONS & CORS

Некоторые могут захотеть изменить автоматический ответ для OPTIONS и установить некоторые заголовки ответа для адаптации к предварительным запросам CORS. Эти требования могут быть реализованы с помощью обработчика Router.GlobalOPTIONS.

go
router.GlobalOPTIONS = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    if r.Header.Get("Access-Control-Request-Method") != "" {
        // Установить заголовки ответа CORS
        header := w.Header()
        header.Set("Access-Control-Allow-Methods", r.Header.Get("Allow"))
        header.Set("Access-Control-Allow-Origin", "*")
    }

    // Адаптировать код состояния 204
    w.WriteHeader(http.StatusNoContent)
})

Обработчик NOT FOUND

TIP

Возможно, потребуется отключить Router.HandleMethodNotAllowed, чтобы избежать некоторых проблем.

go
router.NotFound = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
  // Ваша логика
})

Базовая аутентификация

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) {
    // Получить базовые учётные данные
    user, password, hasAuth := r.BasicAuth()

    if hasAuth && user == requiredUser && password == requiredPassword {
      // Делегировать запрос предоставленному обработчику
      h(w, r, ps)
    } else {
      // Иначе запросить аутентификацию
      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 by www.golangdev.cn edit