Изучаем Go - создание загрузчика (часть 2)
В прошлый раз мы опеределились с планом создания простого сервера на Go, который бы загружал удаленные файлы. В этот раз я бы уже хотел приступить к написанию настоящего кода. Я постараюсь документировать все, что помогло мне собрать программу вместе со всеми ссылками, которые я использовал. Я также пытался ясно и понятно именовать переменные. Например, используя ответ http.ResponseWriter , запросите *http.Request вместо w http.ResponseWriter, r *http.Request. Надеюсь на то, что любой человек с любым опытом сможет разобраться в коде.
Шаг первый
Наша первая задача - наконец то увидеть код на экране. Мы собираемся создать несколько пакетов приложения, один формата main и другой формата fmt , чтобы можно было выводить результат в стандартный вывод.
package main import "fmt"
Мы будем использовать объект JSON, с которым определились ранее, и создадим его структуру на сайте JSON-to-Go. Он позволяет удобно и просто работать с JSON, поэтому, если собираетесь работать с JSON в Go, рекомендую добавить его в закладки.
type download struct { Title string `json:"title"` Location string `json:"location"` }
Для начала заставим программу при запуске выводить слово «Downloader» и сделаем это все в функции main . После этого создадим тестовую структуру, которую потом тоже выведем.
func main() { fmt.Printf("Downloader") download := download{ Title: "title-test", Location: "location-test", } fmt.Printf("%v", download) }
И при запуске мы увидим следующее:
$ go run downloader.go Downloader {title-test location-test}
Шаг второй
Первое, на что обратим внимание, это то, что теперь мы используем два новых импорта. Эти импорты: io/ioutil и net/http являются частью стандартной библиотеки Go и возьмут на себя некоторую функциональность. Стандартная библиотека предоставляет разработчику множество инструментов, необходимых для создания различных программ. Надо лишь научится использовать их друг с другом. А далее вы узнаете, что даже если стандартного библиотечного пакета для функции не существует, скорей всего кто то в сообществе уже подумал и исправил это.
package main import ( "fmt" "io/ioutil" "net/http" ) type download struct { Title string `json:"title"` Location string `json:"location"` }
Ну вот мы и подошли к первой новой функции - status (статусу). В настоящее время запрос «/» на сервер выдаст код ответа 200 с телом «Hello!». Вскоре мы поменяем ответ и будем возвращать информацию о сервере для проверки его работоспособности.
func status(response http.ResponseWriter, request *http.Request) { fmt.Fprintf(response, "Hello!") }
Новая функция handleDownloadRequest будет основной задачей серверной части нашей программы. Пока что она примет запрос «/ download», выведет тело запроса в консоль и слово «Download!». в браузер.
func handleDownloadRequeset(response http.ResponseWriter, request *http.Request) { r, err := ioutil.ReadAll(request.Body) if err != nil { fmt.Println(err) } defer request.Body.Close() fmt.Println(string(r)) fmt.Fprintf(response, "Download!") }
Обновим функцию main, чтобы использовать новые функции для настройки обработчиков HTTP( HTTP handlers ). Наконец, мы запускаем сервер, прослушивающий порт 3000.
func main() { fmt.Println("Downloader") http.HandleFunc("/", status) http.HandleFunc("/download", handleDownloadRequest) http.ListenAndServe(":3000", nil) }
Шаг третий
На этот раз большинство изменений происходят в функции handleDownloadRequest. Однако, если вы посмотрите внимательно, вы увидите, что я также добавил новые импорты encoding/json и log. Так как у нас серверное приложение, я хотел использовать встроенные функции логера и выводить их в консоль. Я пока не уверен, как поступить лучше, отправлять логи в файл или просто выводить в консоль.
package main import ( "encoding/json" "fmt" "io/ioutil" "log" "net/http" ) type download struct { Title string `json:"title"` Location string `json:"location"` } func status(response http.ResponseWriter, request *http.Request) { fmt.Fprintf(response, "Hello!") }
А вот и сердце нашей программы. К тому же, здесь видны некоторые существенные изменения. Сначала, мы создаем структуру с именем downloadReqest для хранения нашего входящего заголовка и URL. Я так же улучшил обработку ошибок, добавил при необходимости возможность вывода реальных ошибок в браузере.
func handleDownloadRequest(response http.ResponseWriter, request *http.Request) { var downloadRequest download r, err := ioutil.ReadAll(request.Body) if err != nil { http.Error(response, "bad request", 400) log.Println(err) return } defer request.Body.Close()
Предполагая, что начальный запрос прошел мы можем прочитать тело и далее вставляем запрос JSON в нашу структуру. Вызов json.Unmarshal сделает всю грязную работу за нас. Если он не справится, мы можем вернуть ошибку в браузер. Вот как мы выводим сообщение об ошибке в json.Unmarshal
err = json.Unmarshal(r, &downloadRequest) if err != nil { http.Error(response, "bad request: "+err.Error(), 400) log.Println(err) return } log.Printf("%#v", downloadRequest) fmt.Fprintf(response, "Download!") } func main() { log.Println("Downloader") http.HandleFunc("/", status) http.HandleFunc("/download", handleDownloadRequest) http.ListenAndServe(":3000", nil) }
Мы с вами довольно много успели сделать! Быстро прошли путь от базового скелета до рабочего сервера. Теперь займемся изменением функции handleDownloadRequest, добавив к ней еще одну функцию getFile, которая нам понадобится для получения фактического файла.
Другие статьи из цикла:
- Изучаем Go - создание загрузчика (часть 1)
- Изучаем Go - создание загрузчика (часть 2)
- Изучаем Go - создание загрузчика (часть 3)
- Изучаем Go - создание загрузчика (часть 4)
- Изучаем Go - создание загрузчика (часть 5)
- Изучаем Go - Использование REST API
- Изучаем Go - Продолжаем работать с REST API
- Изучаем Go - Отправка REST-запросов
- Изучаем Go - Используем REST API в паре с шаблонами проектирования
- Изучаем Go - Повторная отправка электронной почты через API
Изучаем Go - Давайте станем модульными!скороИзучаем Go - Давайте станем модульными снова!скороИзучаем Go - Сборка DevLog Часть 01скороИзучаем Go - Сборка DevLog Часть 02скоро