Mux — это мощный Go маршрутизатор:
import "github.com/issue9/middleware/v4/compress"
import "github.com/issue9/mux/v6"
``````markdown
c := compress.New()
router := mux.NewRouter("", mux.Options(Middlewares: []mux.Middleware{c}))
router.Get("/users/1", h).
Post("/login", h).
Get("/pages/{id:\\d+}.html", h). // Соответствует формату /pages/123.html, path = 123
Get("/posts/{path}.html", h). // Соответствует формату /posts/2020/11/11/title.html, path = 2020/11/11/title
// Объединение маршрутов с общим префиксом пути
p := router.Prefix("/api")
p.Get("/logout", h) // Аналогично m.Get("/api/logout", h)
p.Post("/login", h) // Аналогично m.Get("/api/login", h)
// Разные действия над одним ресурсом
res := p.Resource("/users/{id:\\d+}")
res.Get(h) // Аналогично m.Get("/api/users/{id:\\d+}", h)
res.Post(h) // Аналогично m.Post("/api/users/{id:\\d+}", h)
res.URL(map[string]string{"id": "5"}) // Создает путь на основе этого маршрута: /users/5
http.ListenAndServe(":8080", router)
Маршрутные параметры заключаются в фигурные скобки, внутри которых содержится имя и правило: {name:rule}
,
где name
представляет собой имя параметра, а rule
— ограничение на этот параметр. Имя может содержать префикс -
, что указывает на то, что в процессе выполнения значение с таким именем не будет захвачено,
что может повысить производительность.
Правило представляет собой ограничение для параметров, обычно это регулярное выражение или пустое значение, пустое значение означает соответствие любому значению, более продвинутые методы использования правил представлены в разделе про промежуточные слои. Ниже приведены некоторые распространенные примеры:
/posts/{id}.html // Соответствует /posts/1.html
/posts-{id}-{page}.html // Соответствует /posts-1-10.html
/posts/{path:\w+}.html // Соответствует /posts/2020/11/11/title.html
/tags/{tag:\w+}/{path} // Соответствует /tags/abc/title.html
```
### Правила соответствия путей
Может возникнуть ситуация, когда несколько записей соответствуют одному запросу,
в этом случае система выбирает наиболее подходящий маршрут для обработки запроса, используя следующие правила:
1. Обычные маршруты имеют приоритет перед регулярными маршрутами;
2. Промежуточные слои имеют приоритет перед регулярными маршрутами;
3. Регулярные маршруты имеют приоритет перед маршрутизируемыми маршрутами;
Например:
```text
/posts/{id}.html // 1
/posts/{id:\d+}.html // 2
/posts/1.html // 3
/posts/1.html // Соответствует 3
/posts/11.html // Соответствует 2
/posts/index.html // Соответствует 1
```
Соответствие путей происходит в порядке слева направо, родительский узел не расширяется для соответствия,
если его дети не соответствуют. Например, `/posts/{id}-{page:digit}.html` соответствует `/posts/1-1.html`,
но не соответствует `/posts/1-1-1.html`, хотя теоретически `1-1-` могло бы соответствовать `{id}`,
но `1-` уже было выбрано как более приоритетное соответствие.
### Параметры маршрута
Параметры маршрутов, найденные с помощью регулярных выражений, могут быть получены через `GetParams()`:
```go
import "github.com/issue9/mux/v6"
params := mux.GetParams(r)
id, err := params.Int("id")
// или
id := params.MustInt("id", 0) // Возвращает 0 как значение по умолчанию, если параметр id недоступен
```
## Продвинутые методы
### Группировка маршрутов
Можно использовать интерфейс `Matcher`, чтобы определить набор маршрутов со специальными требованиями.
```go
// server.go
import "github.com/issue9/mux/v6"
import "github.com/issue9/mux/v6/muxutil"
m := mux.NewRouters(...)
def := mux.NewRouter("default")
m.AddRouter(muxutil.NewPathVersion("version-key", "v1"), def)
def.Get("/path", h1)
host := mux.NewRouter("host")
m.AddRouter(muxutil.NewHosts("*.example.com"), host)
host.Get("/path", h2)
```
```markdown
http.ListenAndServe(":8080", m)
// client.go
// Доступ к содержимому h2
r := http.NewRequest(http.MethodGet, "https://abc.example.com/path", nil)
r.Do()
// Доступ к содержимому h1
r := http.NewRequest(http.MethodGet, "https://other_domain.com/v1/path", nil)
r.Do()
```
### Интерцепторы
Обычно, пути вроде `/posts/{id:\d+}` или `/posts/{id:[0-9]+}` обрабатываются как регулярные выражения,
но производительность работы с регулярными выражениями может быть низкой. В этом случае можно использовать интерцепторы:
```go
import "github.com/issue9/mux/v6"
func digits(path string) bool {
for _, c := range path {
if c < '0' || c > '9' {
return false
}
}
return len(path) > 0
}
// Для всех маршрутов, где используются \d+ и [0-9]+ будет использоваться функция digits вместо регулярных выражений.
opt := mux.Options{Interceptors: map[string]mux.InterceptorFunc{"\\d+": digits, "[0-9]+": digits}}
r := mux.NewRouter("", opt)
```Таким образом, все маршруты, использующие `[0-9]+` и `\d+`, будут обрабатываться функцией `digits`,
что позволит повысить производительность. Также можно создать свои собственные правила для обработки путей,
например:```text
/posts/{id:цифр}/.html
```
Если не использовать интерцепторы, то конечное регулярное выражение может вызвать ошибку компиляции.
С помощью интерцепторов можно легализовать такие правила.
В настоящее время доступны следующие интерцепторы:
- ИнтерceptorЦифры — ограничивает использование только цифр, эквивалентно регулярному выражению `[0-9]`;
- ИнтерceptorСлово — эквивалентно регулярному выражению `[a-zA-Z0-9]`;
- ИнтерceptorЛюбой — позволяет совпадать со всеми непустыми значениями;
Пользователи также могут реализовать собственные `InterceptorFunc` в качестве интерцепторов. Подробнее см. OptionsOf.Interceptors.
### CORS
CORS больше не предоставляется в виде middleware, а передается через NewRouter вместе с конфигурационной информацией CORS,
что позволяет лучше управлять каждым адресом и его поддерживаемыми методами запросов.
Метод OPTIONS автоматически генерируется системой.
```go
import "github.com/issue9/mux/v6"
r := mux.NewRouter("name", &mux.Options{CORS: РазрешенныеCORS}) // любой запрос съязыканный запрос
r.Get("/posts/{id}", nil) // По умолчанию, OPTIONS имеет заголовок GET, OPTIONS
http.ListenAndServe(":8080", r)
// client.go
// Доступ к содержимому h2
r := http.NewRequest(http.MethodGet, "https://localhost:8080/posts/1", nil)
r.Header.Set("Origin", "https://example.com")
r.Do() // Кросс-доменный запрос, который работает нормально
```
### Предварительные запросы
Можно использовать следующий код для выполнения предварительного запроса:
``````go
r = http.NewRequest(http.MethodOptions, "https://localhost:8080/posts/1", nil)
r.Header.Set("Origin", "https://example.com")
r.Header.Set("Access-Control-Request-Method", "GET")
r.Do() // Предварительный запрос, можно получить доступ нормально
```### Статические файлы
Для реализации доступа к статическим файлам можно использовать `ServeFile` вместе с именованными параметрами:
```go
r := NewRouter("")
r.Get("/assets/{path}", func(w http.ResponseWriter, r *http.Request){
err := muxutil.ServeFile(os.DirFS("/static/"), "path", "index.html", w, r)
if err != nil {
http.Error(err.Error(), http.StatusInternalServerError)
}
})
```
### Пользовательские маршруты
Официально предоставляемый `http.Handler` может не удовлетворять всем требованиям, поэтому через `RouterOf` пользователи могут легко создать свои собственные маршруты. Для этого требуется несколько шагов:
1. Определите специализированный тип маршрута обработчика, который может быть как классом, так и функцией;
2. На основе этого типа сгенерируйте соответствующие типы `RouterOf`, `PrefixOf`, `ResourceOf`, `MiddlewareFuncOf`, `OptionsOf` и т.д.;
3. Определите функцию `CallOf`;
4. Передайте `CallOf` в `NewOf`.
```go
type Context struct {
*http.Request
W http.ResponseWriter
P Params
}
type HandlerFunc func(ctx *Context)
type Router = RouterOf[HandlerFunc]
type Prefix = PrefixOf[HandlerFunc]
type Resource = ResourceOf[HandlerFunc]
type MiddlewareFunc = MiddlewareFuncOf[HandlerFunc]
type Middleware = MiddlewareOf[HandlerFunc]
type Options = OptionsOf[HandlerFunc]
func New(name string, ms []Middleware, o ...Option) *Router {
f := func(w http.ResponseWriter, r *http.Request, ps Params, h HandlerFunc) {
ctx := &Context{
R: r,
W: w,
P: ps,
}
h(ctx)
}
return NewRouterOf[HandlerFunc](name, f, ms, o...)
}
```
Это полный набор функций для создания пользовательских маршрутов, после чего вы можете использовать его напрямую:```go
r := New("router", nil)
r.Get("/path", func(ctx *Context) {
// TODO
ctx.W.WriteHeader(200)
})
r.Prefix("/admin").Get("/login", func(ctx *Context) {
// TODO
ctx.W.WriteHeader(501)
})
```
Более подробное описание пользовательских маршрутов доступно по адресу <https://caixw.io/posts/2022/build-go-router-with-generics.html>
## Производительность
Сравнение производительности с другими фреймворками представлено на сайте <https://caixw.github.io/go-http-routers-testing/>
## Лицензия
Данный проект использует лицензию [MIT](https://opensource.org/licenses/MIT). Полный текст лицензии доступен в файле [LICENSE](LICENSE).
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.