1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/issue9-mux

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

mux

Go Go версия Go Report Card лицензия codecov PkgGoDev

Mux — это мощный Go маршрутизатор:

  • маршрутизация параметров;
  • поддержка регулярных выражений в качестве метода совпадения маршрутов;
  • перехват поведения регулярных выражений;
  • автоматическое создание способа обработки запросов OPTIONS;
  • автоматическое создание способа обработки запросов HEAD;
  • обратное сопоставление адресов на основе маршрута;
  • маршруты любого стиля, например, такие как Discuz, которые не используют "/" в качестве разделителя;
  • группировка маршрутов, например, по доменам или номерам версий;
  • обработка CORS (Cross-Origin Resource Sharing);
  • поддержка промежуточных слоев;
  • автоматическое создание способа обработки запросов OPTIONS;
  • система обслуживания статических файлов;
  • поддержка метода запроса TRACE;
  • обработка паники;
  • поддержка шаблонов, что позволяет легко реализовать пользовательские способы маршрутизации.
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).

Введение

Функциональный маршрутизатор Go. Развернуть Свернуть
MIT
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/issue9-mux.git
git@api.gitlife.ru:oschina-mirror/issue9-mux.git
oschina-mirror
issue9-mux
issue9-mux
master