Давайте улучшим внешний вид домашней страницы нашего веб-приложения, добавив в проект несколько статических файлов, таких как CSS стили, изображения, а также добавим небольшой фрагмент JavaScript кода который поможет нам выделить активный элемента навигации.

Содержание статьи

Все наши статические файлы будут сохранены в созданную папку ui/static, скачиваем необходимые нам стили и изображения следующим образом:

Премиум 👑 канал по Golang

Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎

Подписаться на канал

Уроки, статьи и Видео

Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.

Go в ВК ЧАТ в Telegram

Содержимое папки ui/static будет выглядеть следующим образом:

Создание сайта Golang

Обработчик статических файлов  http.FileServer

В Go есть модуль net/http который поставляется вместе со встроенным обработчиком http.FileServer, который можно использовать для доступа к статическим файлам из определенной директории по HTTP протоколу. Давайте добавим в приложение новый маршрут, чтобы все HTTP-запросы, начинающиеся с "/static/", обрабатывались с  помощью http.FileServer:

Метод URL Обработчик Действие
ANY / home Отображение домашней страницы
ANY /snippet?id=1 showSnippet Отображение определенной заметки
POST /snippet/create createSnippet Создание новой заметки
ANY /static/ http.FileServer Обслуживание определенного статического файла

Для инициализации обработчика http.FileServer нужно использовать функцию http.FileServer() следующим образом:

Когда данный обработчик получает HTTP-запрос, он удаляет ведущую косую черту из URL пути, а затем ищет в папке ./ui/static соответствующий файл для его отправки пользователю.

Для того чтобы все сработало правильно, требуется удалить  "/static" из URL перед его отправкой в http.FileServer. В противном случае, Go будет искать файла, которого не существует, и пользователь получит ошибку 404 page not found. К счастью, в Go специально для этой задачи есть помощник http.StripPrefix().

Откройте файл main.go и добавьте следующий код. В итоге файл должен выглядеть следующим образом:

После завершения перезапустите приложение и откройте страницу http://127.0.0.1:4000/static/ в браузере. Вы должны увидеть список, состоящую из папок директории ui/static.

Статические файлы для сайта на Golang

Можете поэкспериментировать, зайдя в разные папки и посмотреть на файлы. К примеру, при переходе на http://127.0.0.1:4000/static/css/main.css вы увидите, что CSS файл открылся в браузере:

CSS стили в Golang

Подключаем CSS и JS к шаблону

Если HTTP-обработчик статических файлов работает правильно, то можно обновить файл ui/html/base.layout.tmpl подключения к нему наших статических файлов, такие как CSS стили и JS, ну и про лого не забудем:

Не забудьте сохранить изменения и затем перейти на страницу http://127.0.0.1:4000. Ваша домашняя страница теперь должна выглядеть следующим образом:

Создание сайта на Golang

Особенности обработчика статических файлов

Обработчик статических файлов в Go обладает несколькими интересными особенностями, о которых стоит упомянуть:

  • Он очищает все пути HTTP-запросов, запуская их через функцию path.Clean() перед тем как приступить к поиску запрашиваемого файла. Так удаляются потенциально опасные символы, такие как . и .. из URL, это поможет предотвратить атаки по обходу нижних уровней директорий. Эта функция особенно полезна, если вы используете статические файлы вместе с маршрутизатором, который не очищает URL пути в автоматическом порядке;
  • Сегментирование HTTP запросов полностью поддерживаются. Это необходимо, если ваше веб-приложение предоставляет пользователям возможность скачивания больших файлов или вы предоставляете видео поток и требуется поддерживать возобновляемые загрузки. Увидеть эту возможность в действии можно при помощи curl для сохранения картинки logo.png в несколько запросов по 100-199 байт каждый. К примеру:
  • Такие заголовки как Last-Modified и If-Modified-Since тоже поддерживаются. Если файл не изменился с момента последнего запроса пользователя, http.FileServer отправит код состояния 304 Not Modified вместо самого файла, так как сам файл уже есть у клиента в кэше браузера. Это помогает уменьшить задержку и расходы на обработку запроса для клиента и для сервера;
  • Заголовок Content-Type автоматически устанавливается в зависимости от расширения файла с помощью функции mime.TypeByExtension(). В зависимости от задачи, вы можете добавить свои собственные расширения и типы контента с помощью функции mime.AddExtensionType().

Производительность

В приведенном выше коде мы настроили обработчик статических файлов таким образом, чтобы он обслуживал только файлы из папки ./ui/static на вашем жестком диске.

Обратите внимание, что после запуска веб-приложения, обработчик  http.FileServer, возможно, не будет читать файлы напрямую с жесткого диска. Операционные системы на базе Windows и Unix кэшируют недавно использованные файлы в RAM, поэтому (по крайней мере, для часто использованных файлов) http.FileServer будет доставать их из RAM, а не выполнять медленную обработку файлов с жесткого диска.

Обслуживание отдельных файлов

Иногда может потребоваться обслужить только один статический файл. Для этой задачи, функция http.ServeFile() используется следующим образом:

Внимание: Обработчик http.ServeFile() автоматически не очищает путь файла. Если вы указываете в данную функцию путь к файлу полученный от пользователя напрямую, во избежание атак обхода директории, перед использование этих данных, обязательно очистите их с помощью функции filepath.Clean(). Иначе, пользователь сможет скачать различные файлы с сервера включая файл с настройками к базе данных.

Ограничение просмотра файлов из директории

Есть несколько способов отключения просмотра списка файлов из директории. Какой самый простой?

Добавьте пустой файл index.html в ту директорию, где требуется отключить вывод списка файлов. Веб-сервер всегда ищет сперва файл index.html, и пользователь увидит пустую страницу с кодом состояния 200 OK. Если вы хотите использоваться данный способ для всех директорий в ./ui/static, можно задействовать следующую команду:

Более сложным способом (однако он считается лучшим) является создание настраиваемой имплементации файловой системы http.FileSystem, с помощью которой будет возвращаться ошибка os.ErrNotExist для любого HTTP запроса напрямую к папке.

Например, если пользователь попытается открыть в браузере ссылку http://127.0.0.1:4000/static/ в браузере, то он из соображений безопасности, не должен увидеть список файлов. Он должен получить ошибку 404 страница не найдена. Ниже вы увидите пример как это реализовать.

Использование настраиваемой файловой системы

Последним вариантом, который мы рассмотрим, будет использование настраиваемой файловой системы и ее последующая передача в http.FileServer.

Обновляем наш main.go добавив новую структуру neuteredFileSystem и метод Open для неё.

В данном коде мы создаем настраиваемый тип neuteredFileSystem, который включает в себя http.FileSystem. Затем мы создаем метод Open(), который вызывается каждый раз, когда http.FileServer получает запрос.

В методе Open() мы открываем вызываемый путь. Используя метод IsDir() мы проверим если вызываемый путь является папкой или нет. Если это папка, то с помощью метода Stat("index.html") мы проверим если файл index.html существует внутри данной папки.

Если файл index.html не существует, то метод вернет ошибку os.ErrNotExist (которая, в свою очередь, будет преобразована через http.FileServer в ответ 404 страница не найдена). Мы также вызываем метод Close() для закрытия только, что открытого index.html файла, чтобы избежать утечки файлового дескриптора.

Во всех остальных случаях мы просто возвращаем файл и даем http.FileServer сделать то, что он должен.

Попробуем открыть http://127.0.0.1:4000/static/, в примере выше есть скрин на котором виден список всех внутренних папок из ./static.

Как это выглядит сейчас?

PS: Если у вас есть файл index.html в папке /code/snippetbox/ui/static/, то можете его удалить, иначе у вас будет белая страница.

Запрещаем просмотр содержимого папок

Теперь все работает довольно неплохо:

  • Все прямые HTTP запросы к папкам (в которых нет файла index.html) возвращают ответ 404 Страница не найдена, вместо показа всего списка из папок и файлов которые находятся в этой папки. Это отлично работает для запросов на конце с косой чертой, а также без нее;
  • Поведение http.FileServer по умолчанию никак не меняется, и файлы index.html отображаются в соответствии со стандартной документацией библиотеки.

Исходный код веб-приложения на Golang

Это было весьма весело! По традиции, ниже вы можете скачать готовый код который является актуальным на данный момент прочтения этой статьи.

Скачать: snippetbox-8.zip