Как написать веб приложение на golang
Разработка Web-приложений на языке Go
Комментарии переводчика
Эта книга - русский перевод 《Go Web编程》, оригинальную версию которой написал AstaXie, а на английский язык перевели Unknown и Larry Battle. Русский перевод сделал Alex Saskevich
Эта книга о том, как разрабатывать приложения на языке Go. В первых разделах автор этой книги рассмотрит некоторые базовые концепции языка. Тем не менее, чтобы добиться максимальной пользы от чтения, Вам желательно иметь общее представление о языке Go и понимать концепцию веб-программирования. Заметим, что если Вы новичок, то эта книга не является вводным курсом, потому перед прочтением этой книги Вам желательно изучить другую литературу, например, книгу автора Caleb Doxsey An Introduction to Programming in Go.
Если из-за качества перевода что-то оказалось недостаточно понятно, например по причине некорректности языковых формулировок, не стесняйтесь сообщить об этом мне на репозитории перевода этой книги
Благодарности за помощь в переводе на английский язык
Цель этой книги
Так как я заинтересован в разработке Web-приложений, я использовал свое свободное время, чтобы написать эту книгу в версии с открытым исходным кодом. Но все же это не значит, что у меня хорошие знания в теме создания Web-приложений; я всего-лишь хотел бы поделиться теми навыками, которые получил при работе с языком Go, разрабатывая Web-приложения.
- Если вы PHP/Python/Ruby разработчик, то вы узнаете, как создавать приложения с помощью Go.
- Если вы C/C++ разработчик, вы узнаете, как работает сам Web, изнутри.
Я считаю, что самая главная цель изучения - обмен знаниями с другими. Потому что самая лучшая вещь в моей жизни - делиться тем, что я знаю, с остальными людьми.
Если вам понравилась эта книга, вы можете перейти по этой ссылке, чтобы поддержать оригинального автора, а также помочь ему написать еще больше книг с еще более полезным и интересным содержанием.
Exchanging Learning Go
Если Вы знаете, что такое QQ, вступайте в нашу группу 259316004. Но если же нет, следуйте этой ссылке, чтобы получить больше информации. Кстати, Вы также можете посетить наш форум.
-
(примеры кода) (примеры кода) (написал конфигурационные файлы к Vim и Emacs для разработки под Go) (примеры кода) (предложил некоторые картинки) (пересмотрел всю книгу) (пересмотрел разделы 2 и 3)
This book is licensed under the CC BY-SA 3.0 License, the code is licensed under a BSD 3-Clause License, unless otherwise specified.
Привет, Хабр! Представляю вашему вниманию перевод статьи "How to build your first web application with Go" автора Ayooluwa Isaiah.
Это руководство к вашему первому веб-приложению на Go. Мы создадим новостное приложение, которое использует News API для получения новостных статей по определенной теме, и развернём его на продакшн сервере в конце.
Вы можете найти полный код, используемый для этого урока в этом GitHub репозитории.
Требования
Единственное требование для этого задания — чтобы на вашем компьютере был установлен Go, и вы немного знакомы с его синтаксисом и конструкциями. Версия Go, которую я использовал при создании приложения, также является самой последней на момент написания: 1.12.9. Чтобы просмотреть установленную версию Go, используйте команду go version .
Если вы считаете это задание слишком сложным для вас, перейдите к моему предыдущему вводному уроку по языку, который должен помочь вам освоиться.
Итак, начнем!
Клонируем репозиторий стартовых файлов на GitHub и cd в созданный каталог. У нас есть три основных файла: В файле main.go мы напишем весь код Go для этого задания. Файл index.html — это шаблон, который будет отправлен в браузер, а стили для приложения находятся в assets/styles.css .
Создадим базовый веб-сервер
Давайте начнем с создания базового сервера, который отправляет текст «Hello World!» в браузер при выполнении запроса GET к корню сервера. Измените ваш файл main.go так, чтобы он выглядел следующим образом:
Затем скомпилируйте и выполните код, который вы только что написали:
Шаблоны в Go
Давайте рассмотрим основы шаблонизации в Go. Если вы знакомы с шаблонами на других языках, это должно быть достаточно просто для понимания.
Шаблоны предоставляют простой способ настроить вывод вашего веб-приложения в зависимости от маршрута без необходимости писать один и тот же код в разных местах. Например, мы можем создать шаблон для панели навигации и использовать его на всех страницах сайта, не дублируя код. Кроме того, мы также получаем возможность добавить некоторую базовую логику на наши веб-страницы.
Go предоставляет две библиотеки шаблонов в своей стандартной библиотеке: text/template и html/template . Оба предоставляют один и тот же интерфейс, однако пакет html/template используется для генерации HTML-вывода, который защищен от инъекций кода, поэтому мы будем использовать его здесь.
Импортируйте этот пакет в ваш файл main.go и используйте его следующим образом:
tpl — переменная уровня пакета, которая указывает на определение шаблона из предоставленных файлов. Вызов template.ParseFiles анализирует файл index.html в корне каталога нашего проекта и проверяет его на валидность.
Мы оборачиваем вызов template.ParseFiles в template.Must , чтобы код вызывал панику при возникновении ошибки. Причина, по которой мы паникуем здесь вместо того, чтобы пытаться обработать ошибку, заключается в том, что нет смысла продолжать выполнение кода, если у нас невалидный шаблон. Это проблема, которая должна быть устранена перед попыткой перезапустить сервер.
В функции indexHandler мы выполняем созданный ранее шаблон, предоставляя два аргумента: куда мы хотим записать выходные данные и данные, которые мы хотим передать в шаблон.
В приведенном выше случае мы записываем выходные данные в интерфейс ResponseWriter и, поскольку у нас нет никаких данных для передачи в наш шаблон в настоящее время, в качестве второго аргумента передается nil .
Остановите запущенный процесс в вашем терминале с помощью Ctrl-C и запустите его снова с помощью go run main.go , затем обновите ваш браузер. Вы должны увидеть текст «News App Demo» на странице, как показано ниже:
Добавляем панель навигации на страницу
Замените содержимое тега <body> в вашем файле index.html, как показано ниже:
Затем перезагрузите сервер и обновите ваш браузер. Вы должны увидеть что-то похожее на это:
Работа со статическими файлами
Обратите внимание, что панель навигации, которую мы добавили выше, не имеет стилей, несмотря на тот факт, что мы уже указали их в <head> нашего документа.
Но необходимость объявлять явные обработчики для всех наших статических файлов нереальна и не может масштабироваться. К счастью, мы можем создать один обработчик для обслуживания всех статических ресурсов.
Первое, что нужно сделать, — создать экземпляр объекта файлового сервера, передав каталог, в котором находятся все наши статические файлы:
Далее нам нужно указать нашему маршрутизатору использовать этот объект файлового сервера для всех путей, начинающихся с префикса /assets/ :
Теперь всё вместе:
Перезагрузите сервер и обновите браузер. Стили должны включиться, как показано ниже:
Создаем роут /search
Давайте создадим роут, который обрабатывает поисковые запросы для новостных статей. Мы будем использовать News API для обработки запросов, поэтому вам нужно зарегистрироваться для получения бесплатного ключа API здесь.
Этот маршрут ожидает два параметра запроса: q представляет запрос пользователя, а page используется для пролистывания результатов. Этот параметр page является необязательным. Если он не включен в URL, мы просто предположим, что номер страницы результатов имеет значение «1».
Добавьте следующий обработчик под indexHandler в ваш файл main.go :
Приведенный выше код извлекает параметры q и page из URL-адреса запроса и выводит их оба в терминал.
Затем зарегистрируйте функцию searchHandler в качестве обработчика пути /search , как показано ниже:
Не забудьте импортировать пакеты fmt и net/url сверху:
Теперь перезапустите сервер, введите запрос в поле поиска и проверьте терминал. Вы должны увидеть ваш запрос в терминале, как показано ниже:
Создаём модель данных
Когда мы делаем запрос к конечной точке News API/everything , мы ожидаем ответ json в следующем формате:
Чтобы работать с этими данными в Go, нам нужно сгенерировать структуру, которая отражает данные при декодировании тела ответа. Конечно, вы можете сделать это вручную, но я предпочитаю использовать веб-сайт JSON-to-Go, который делает этот процесс действительно простым. Он генерирует структуру Go (с тегами), которая будет работать для этого JSON.
Все, что вам нужно сделать, это скопировать объект JSON и вставить его в поле, помеченное JSON, затем скопировать вывод и вставить его в свой код. Вот что мы получаем для вышеуказанного объекта JSON:
Я сделал несколько изменений в структуре AutoGenerated , отделив фрагмент Articles в его собственную структуру и обновив имя структуры. Вставьте следующее ниже объявление переменной tpl в main.go и добавьте пакет time в ваш импорт:
Как вы, возможно, знаете, Go требует, чтобы все экспортируемые поля в структуре начинались с заглавной буквы. Однако принято представлять поля JSON с помощью camelCase или snake_case, которые не начинаются с заглавной буквы.
Поэтому мы используем теги поля структуры, такие как json:"id" , чтобы явно отобразить поле структуры в поле JSON, как показано выше. Это также позволяет использовать совершенно разные имена для структурного поля и соответствующего поля json, если это необходимо.
Наконец, давайте создадим другой тип структуры для каждого поискового запроса. Добавьте это ниже структуры Results в main.go :
Эта структура представляет собой каждый поисковый запрос, сделанный пользователем. SearchKey — это сам запрос, поле NextPage позволяет пролистывать результаты, TotalPages — общее количество страниц результатов запроса, а Results — текущая страница результатов запроса.
Отправляем запрос по News API и рендерим результаты
Теперь, когда у нас есть модель данных для нашего приложения, давайте продолжим и сделаем запросы к News API, а затем отрендерим результаты на странице.
Поскольку для News API требуется ключ API, нам нужно найти способ передать его в нашем приложении без жесткого кодирования в коде. Переменные среды являются распространенным подходом, но я решил использовать вместо них флаги командной строки. Go предоставляет пакет flag , поддерживающий базовый анализ флагов командной строки, и это то, что мы собираемся использовать здесь.
Сначала объявите новую переменную apiKey под переменной tpl :
Затем используйте её в функции main следующим образом:
Здесь мы вызываем метод flag.String() , который позволяет нам определять строковый флаг. Первый аргумент этого метода — имя флага, второй — значение по умолчанию, а третий — описание использования.
После определения всех флагов вам нужно вызвать flag.Parse() , чтобы фактически проанализировать их. Наконец, так как apikey является обязательным компонентом для этого приложения, мы обеспечиваем аварийное завершение программы, если этот флаг не установлен при выполнении программы.
Убедитесь, что вы добавили пакет flag в свой импорт, затем перезапустите сервер и передайте требуемый флаг apikey , как показано ниже:
Замените два вызова метода fmt.Println() в конце функции searchHandler следующим кодом:
После этого мы конвертируем переменную page в целое число и присваиваем результат полю NextPage переменной search . Затем мы создаем переменную pageSize и устанавливаем ее значение равным 20. Эта переменная pageSize представляет количество результатов, которые API новостей будет возвращать в своем ответе. Это значение может находиться в диапазоне от 0 до 100.
Затем мы создаем конечную точку с помощью fmt.Sprintf() и делаем запрос GET к ней. Если ответ от News API не 200 OK, мы вернем клиенту общую ошибку сервера. В противном случае тело ответа парсится в search.Results .
Затем мы вычисляем общее количество страниц путем деления поля TotalResults на pageSize . Например, если запрос возвращает 100 результатов, а мы одновременно просматриваем только 20, нам нужно будет пролистать пять страниц, чтобы просмотреть все 100 результатов по этому запросу.
После этого мы рендерим наш шаблон и передаем переменную search в качестве интерфейса данных. Это позволяет нам получать доступ к данным из объекта JSON в нашем шаблоне, как вы увидите.
Прежде чем перейти к index.html , обязательно обновите ваши импорты, как показано ниже:
Давайте продолжим и отобразим результаты на странице, изменив файл index.html следующим образом. Добавьте это под тегом <header> :
Чтобы получить доступ к полю структуры в шаблоне, мы используем оператор точки. Этот оператор ссылается на объект структуры (в данном случае search ), а затем внутри шаблона мы просто указываем имя поля (как > ).
Блок range позволяет нам перебирать слайс в Go и выводить некоторый HTML для каждого элемента в слайсе. Здесь мы перебираем слайс структур Article , содержащихся в поле Articles , и выводим HTML на каждой итерации.
Перезагрузите сервер, обновите браузер и выполните поиск новостей по популярной теме. Вы должны получить список из 20 результатов на странице, как показано на скрине ниже.
Сохраняем поисковый запрос в инпуте
Обратите внимание, что поисковый запрос исчезает из ввода, когда страница обновляется с результатами. В идеале запрос должен сохраняться до тех пор, пока пользователь не выполнит новый поиск. Вот как Google Search работает, например.
Перезапустите браузер и выполните новый поиск. Поисковый запрос будет сохранен, как показано ниже:
Форматируем дату публикации
Если вы посмотрите на дату в каждой статье, вы увидите, что она плохо читаема. Текущий вывод показывает, как News API возвращает дату публикации статьи. Но мы можем легко изменить это, добавив метод в структуру Article и используя его для форматирования даты вместо использования значения по умолчанию.
Давайте добавим следующий код чуть ниже структуры Article в main.go :
Здесь новый метод FormatPublishedDate создан в структуре Article , и этот метод форматирует поле PublishedAt в Article и возвращает строку в следующем формате: 10 января 2009 .
Чтобы использовать этот новый метод в вашем шаблоне, замените .PublishedAt на .FormatPublishedDate в вашем файле index.html . Затем перезагрузите сервер и повторите предыдущий поисковый запрос. Это выведет результаты с правильно отформатированным временем, как показано ниже:
Выводим общее количество результатов
Все, что вам нужно сделать, это добавить следующий код как дочерний элемент .container , чуть выше элемента .search-results в вашем файле index.html :
Шаблоны Go поддерживают несколько функций сравнения, некоторые из которых используются выше. Мы используем функцию gt , чтобы проверить, что поле TotalResults структуры Results больше нуля. Если это так, общее количество результатов будет напечатано в верхней части страницы.
После этого сделайте еще один поисковый запрос на этот раз с популярной темой. Количество результатов будет выведено вверху страницы, как показано ниже:
Пагинация
Так как мы отображаем только 20 результатов одновременно, нам нужен способ, чтобы пользователь мог перейти на следующую или предыдущую страницу результатов в любое время.
Сначала добавим кнопку Next внизу результатов, если последняя страница результатов еще не достигнута. Чтобы определить, была ли достигнута последняя страница результатов, создайте этот новый метод ниже объявления структуры Search в main.go :
Этот метод проверяет, больше ли поле NextPage , чем поле TotalPages в экземпляре Search . Чтобы это работало, нам нужно увеличивать NextPage каждый раз, когда отображается новая страница результатов. Вот как это сделать:
Наконец, давайте добавим кнопку, которая позволит пользователю перейти на следующую страницу результатов. Этот код должен быть помещен ниже .search-results в вашем файле index.html .
Пока последняя страница для этого запроса не была достигнута, кнопка Next будет отображаться в нижней части списка результатов.
Как видите, href ссылки указывает на маршрут /search и сохраняет текущий поисковый запрос в параметре q , используя значение NextPage в параметре page .
Давайте добавим кнопку Previous. Эту кнопку следует отображать только в том случае, если текущая страница больше 1. Чтобы сделать это, нам нужно создать новый метод CurrentPage() в Search , чтобы реализовать это. Добавьте это ниже метода IsLastPage :
Текущая страница просто NextPage - 1 , за исключением случаев, когда NextPage равен 1. Чтобы получить предыдущую страницу, просто вычтите 1 из текущей страницы. Следующий метод делает именно это:
Таким образом, мы можем добавить следующий код для отображения кнопки Previous, только если текущая страница больше 1. Измените элемент .pagination в вашем файле index.html следующим образом:
Теперь перезагрузите сервер и сделайте новый поисковый запрос. У вас должно получиться пролистать результаты, как показано ниже:
Показываем текущую страницу
Вместо того, чтобы отображать только общее количество результатов, найденных для запроса, пользователю также полезно просмотреть общее количество страниц для этого запроса и страницу, на которой он в данный момент находится.
Для этого нам нужно всего лишь изменить наш файл index.html следующим образом:
После того, как вы перезапустите сервер и выполните новый поиск, в верхней части страницы будет указана текущая страница и общее количество страниц вместе с общим количеством результатов.
Деплоим на Heroku
Теперь, когда наше приложение полнофункционально, давайте продолжим и развернем его в Heroku. Зарегистрируйте бесплатную учетную запись, затем перейдите по этой ссылке, чтобы создать новое приложение. Укажите для приложения уникальное имя. Я назвал приложение freshman-news.
Затем следуйте инструкциям здесь, чтобы установить интерфейс командной строки Heroku на свой компьютер. Затем выполните команду heroku login в терминале, чтобы войти в свою учетную запись Heroku.
Убедитесь, что вы инициализировали git-репозиторий для своего проекта. Если нет, запустите команду git init в корне каталога вашего проекта, а затем выполните команду ниже, чтобы установить heroku в качестве удаленного git-репозитория. Замените freshman-news названием вашего приложения.
Затем создайте Procfile в корневом каталоге вашего проекта ( touch Procfile ) и вставьте следующее содержимое:
После этого укажите репозиторий GitHub для своего проекта и версию Go, которую вы используете, в своем файле go.mod , как показано ниже. Создайте этот файл, если он еще не существует, в корне проекта.
Перед развертыванием приложения перейдите на вкладку Settings на панели инструментов Heroku и нажмите Reveal Config Vars. Нам нужно установить переменную среды NEWS_API_KEY, чтобы она могла быть передана в бинарный файл при запуске сервера.
Наконец, сделайте коммит своего кода и сделайте пуш в Heroku с помощью следующих команд:
Заключение
В этой статье мы успешно создали приложение News и обучились основам использования Go для веб-разработки. Мы также изучили, как развернуть готовое приложение в Heroku.
Я надеюсь, что эта статья была полезна для вас. Если у вас есть какие-либо вопросы относительно этого туториала, оставьте комментарий ниже, и я перезвоню вам.
Теперь, когда все правильно настроено, давайте создадим первую версию нашего веб-приложения на Golang.
Начнем с трех основных пунктов:
Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎
Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.
Объединим эти компоненты в файле main.go , чтобы создать работающее приложение.
// Создается функция-обработчик "home", которая записывает байтовый слайс, содержащий // функцию "home" регистрируется как обработчик для URL-шаблона "/". // Мы передаем два параметра: TCP-адрес сети для прослушивания (в данном случае это "localhost:4000") // мы используем функцию log.Fatal() для логирования ошибок. Обратите вниманиеСохраните файл main.go . Затем попробуйте запустить его из терминала с помощью команды go run .
Если вернуться в окно терминала, можно остановить веб-сервер через комбинацию Ctrl+C .
Сетевые интерфейсы в Golang
Сетевой TCP-адрес, который передается в http.ListenAndServe() , должен быть в формате "host:port" . Если не указать хост (как мы сделали с ":4000" ), сервер будет прослушивать все доступные сетевые интерфейсы вашего компьютера. Как правило, нужно указать хост в адресе, только если у компьютер несколько сетевых интерфейсов, и требуется только один из них.
В других проектах или документации по Go иногда можно увидеть сетевые адреса, записанные с использованием именованных портов, таких как ":http" или ":http-alt" вместо числа. Если используется именованный порт, Go попытается найти соответствующий номер порта в файле /etc/services при запуске веб-сервера. Если совпадение не будет найдено, Go вернет ошибку.
Запуск веб-приложения в Golang
Во время разработки команда go run может стать удобным способом опробовать ваш код. По сути, это короткий способ компиляции кода, с помощью которого создается исполняемый бинарный файл в папке /tmp . Затем этот бинарный файл запускается.
Стандартная библиотека языка Go включает в себя множество полезных и функциональных компонентов «из коробки», которые позволяют легко разрабатывать серверные приложения. В статье мы изучим, как написать веб-сервер на Go. Начнем с базового «Hello World!» и закончим приложением, выполняющим следующие функции:
Hello World!
В дальнейшем мы используем этот код в качестве основы, но сначала нужно понять, как он работает.
Обработчиком называется то, что принимает запрос и возвращает ответ. В Go обработчики реализуют интерфейс со следующей сигнатурой:
25–27 ноября, Онлайн, Беcплатно
Таким образом, обработчики формируют ответы на запросы. Но как понять, какой именно обработчик нужно использовать в данный момент?
Маршрутизация запросов
Будьте внимательны при работе со сложными веб-фреймворками. Обычно они используют нестандартные решения, что усложняет интеграцию со стандартными обработчиками. Как правило, комбинации стандартной библиотеки и легковесного маршрутизатора будет достаточно для разработки большинства приложений.
Обработка запросов
Теперь перейдём к более сложным примерам.
Работа с Let’s Encrypt
Если у вас уже есть сертификат и закрытый ключ, просто используйте ListenAndServeTLS , указав корректные пути к файлам сертификата и ключа:
Но можно сделать лучше.
Let’s Encrypt — это удостоверяющий центр, выдающий бесплатные сертификаты с возможностью их автоматического обновления. Для использования этого сервиса в Go-приложениях доступен пакет autocert.
Установка маршрутов
Маршрутизатор из стандартной библиотеки неплох, но недостаточно функционален. Как правило, в приложениях требуется более сложная логика маршрутизации. Например, настройка вложенных и wildcard-маршрутов или установка шаблонов и параметров путей.
В этом случае могут быть полезны пакеты gorilla/mux и go-chi/chi. Ниже описан пример настройки маршрутизации с помощью библиотеки chi.
Допустим, у нас есть файл api/v1/api.go, содержащий маршруты для нашего API:
В основном файле установим для маршрутов префикс api/v1 :
Возможность организации маршрутов и применение продвинутых правил маршрутизации упрощает структуризацию и сопровождение крупных приложений.
Реализация middleware
Некоторые сторонние маршрутизаторы наподобие chi предоставляют полезные и готовые функции для промежуточной обработки.
Раздача статических файлов
Простой пример с уже знакомым нам маршрутизатором:
Корректное завершение работы
Заключение
Читайте также: