- PVSM.RU - https://www.pvsm.ru -
В этой части я попытаюсь кратко пройтись по пропущенным местам нашего очень упрощенного веб-приложения на Go.
Немного поворчу: мне не нравится слово “middleware”. Концепция обертки существует с начала вычислений, поэтому не вижу необходимости изобретать для нее новые слова.
Но отбросим это в сторону, допустим, нам потребовалась аутентификация для определенного URL. Сейчас наш обработчик главной страницы выглядит так:
func indexHandler(m *model.Model) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, indexHTML)
})
}
Мы можем написать функцию, которая принимает http.Handler
в качестве аргумента и возвращает (другой) http.Handler
. Возвращенный обработчик проверяет, аутентифицирован ли пользователь, с помощью m.IsAuthenticated()
(неважно, что конкретно там происходит) и перенаправляет пользователя на страницу входа или выполняет оригинальный обработчик, вызывая его метод ServeHTTP()
.
func requireLogin(h http.Handler, m *model.Model) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !m.IsAuthenticated(r) {
http.Redirect(w, r, loginURL, http.StatusFound)
return
}
h.ServeHTTP(w, r)
})
}
С учетом этого, регистрация обработчика теперь будет выглядеть так:
http.Handle("/", requireLogin(indexHandler(m), m))
Таким способом можно обернуть обработчики в любое необходимое количество слоев и этот очень гибкий подход. Все, начиная от установки заголовков до сжатия вывода, может быть выполнено с помощью обертки. Также отмечу, что мы можем передавать любые нужные нам аргументы, например, нашу *model.Model
.
В какой-то момент нам могут понадобиться параметры в URL, например, /Person/3
, где 3
— это идентификатор человека. Стандартная библиотека Go ничего для этого не предоставляет, оставляя эту задачу в качестве упражнения для разработчика. Программный компонент, ответственный за такую штуку, называется Mux [4], т.е. «мультиплексор», или «маршрутизатор», и его можно заменить нестандартной реализацией. Маршрутизатор также реализует метод ServeHTTP()
, что означает, что он удовлетворяет интерфейсу http.Handler, то есть он является обработчиком.
Очень популярным вариантом маршрутизатора является Gorilla Mux [5]. Ему можно делегировать целые пути в тех местах, где требуется больше гибкости. Например, мы можем решить, что все, от /person
и ниже, обрабатывается маршрутизатором Gorilla, и мы хотим, чтобы все это еще было аутентифицировано, тогда это может выглядеть так:
// import "github.com/gorilla/mux"
pr := mux.NewRouter().PathPrefix("/person").Subrouter()
pr.Handle("/{id}", personGetHandler(m)).Methods("GET")
pr.Handle("/", personPostHandler(m)).Methods("POST")
pr.Handle("/{id}", personPutHandler(m)).Methods("PUT")
http.Handle("/person/", requireLogin(pr))
Примечание: я обнаружил, что слэши в конце важны, а правила на счет того, когда они требуются, немного запутаны.
Есть еще много других реализаций маршрутизатора/мультиплексора. Прелесть в том, что не привязываясь ни к какому фреймворку, мы можем выбрать тот маршрутизатор, который лучше всего нам подходит, или написать свой собственный (их нетрудно реализовать).
Одна из самых изящных вещей в Go — это то, что скомпилированная программа представляет собой единственный двоичный файл, а не большую кучу файлов, как это часто бывает с большинством скриптовых языков и даже с некоторыми компилируемыми. Но если наша программа зависит от статических файлов (JS, CSS, изображений и других файлов), нам нужно будет скопировать и их на сервер во время развертывания.
Мы можем сохранить эту характерную особенность — «один бинарник» — нашей программы, включив статику как часть самого двоичного файла. Для этого существует проект go-bindata [6] и его племянник go-bindata-assetsfs [7].
Поскольку упаковка статики в двоичный файл несколько выходит за пределы того, что может сделать go build
, нам понадобится какой-то скрипт, который об этом позаботится. Лично я предпочитаю использовать проверенный и "трушный" make
, и не так уж редко можно встретиться с «Makefile» в проекте Go.
Вот пример подходящего правила Makefile:
ASSETS_DIR = "assets"
build:
@export GOPATH=$${GOPATH-~/go} &&
go get github.com/jteeuwen/go-bindata/... github.com/elazarl/go-bindata-assetfs/... &&
$$GOPATH/bin/go-bindata -o bindata.go -tags builtinassets ${ASSETS_DIR}/... &&
go build -tags builtinassets -ldflags "-X main.builtinAssets=${ASSETS_DIR}"
Это правило создает файл bindata.go
, который помещается в тот же каталог, где находится main.go
, соответственно, он становится частью пакета main
. main.go
как-то узнает, что статические файлы встроены, — это получается с помощью трюка -ldflags "-X main.builtinAssets=${ASSETS_DIR}"
, так мы можем присваивать значения переменным на этапе компиляции. Это значит, что теперь наш код может проверять значение builtinAssets
, чтобы решить, что делать дальше, например:
if builtinAssets != "" {
log.Printf("Running with builtin assets.")
cfg.UI.Assets = &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: AssetInfo, Prefix: builtinAssets}
} else {
log.Printf("Assets served from %q.", assetsPath)
cfg.UI.Assets = http.Dir(assetsPath)
}
Вторая важная вещь заключается в том, что мы определяем build tag [8] с именем builtinassets
. Мы также сообщаем go-bindata о нем, что означает «скомпилируй меня, только когда установлен builtinassets», а это позволяет контролировать, при каких условиях необходимо компилировать bindata.go
(который содержит нашу статику в виде кода Go).
Напоследок (по порядку, а не по важности), я хочу кратко упомянуть упаковку веб-статики. Для описания этого должным образом материала достаточно для целой новой серии статей, и это не имело бы ничего общего с Go. Но я могу хотя бы перечислить некоторые моменты.
В принципе, вы можете пойти на уступки — установить npm [9] и настроить файл package.json
.
Как только npm установлен, банально устанавливается компилятор командной строки Babel — babel-cli
, который является одним из способов транспиляции JavaScript.
Более сложный, в чем-то разочаровывающий, но в конечном счете более гибкий способ — использовать webpack [10]. Webpack будет не только претранспиливать JS-код, но и объединит все JS-файлы в один, а также минимизирует его.
import
и export
, но нет реализации, и даже Babel предполагает, что реализовывать их для вас будет кто-нибудь другой. В конце концов, я остановился на SystemJS [11]. Некоторое осложнение с SystemJS заключается в том, что внутрибраузерная транспиляция Babel должна быть чем-то, понятным для SystemJS, поэтому мне пришлось использовать его плагин для Babel. Webpack, в свою очередь (если я правильно понял), предоставляет свою собственную реализацию поддержки модулей, поэтому при упаковке SystemJS не нужен. В любом случае, это было довольно неприятно.Я бы сказал, что в примере, который я описываю в этой серии из четырех частей, Go безусловно блистает, а вот JavaScript — не особо. Но как только я преодолел начальные трудности с тем, чтобы заставить все это заработать, React/JSX оказался простым и, возможно, даже приятным для работы.
На этом я, пожалуй, закончу. Надеюсь, статьи оказались вам полезными.
Автор: Сергей Соломеин
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/golang/256366
Ссылки в тексте:
[1] Часть 1: https://habrahabr.ru/post/329582/
[2] Часть 2: https://habrahabr.ru/post/329584/
[3] Часть 3: https://habrahabr.ru/post/329612/
[4] Mux: https://golang.org/pkg/net/http/#ServeMux
[5] Gorilla Mux: https://github.com/gorilla/mux
[6] go-bindata: https://github.com/jteeuwen/go-bindata/
[7] go-bindata-assetsfs: https://github.com/elazarl/go-bindata-assetfs
[8] build tag: https://dave.cheney.net/2013/10/12/how-to-use-condition-compilation-with-the-go-build-tool
[9] npm: https://www.npmjs.com/
[10] webpack: https://webpack.github.io/
[11] SystemJS: https://github.com/systemjs/systemjs
[12] Источник: https://habrahabr.ru/post/329622/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.