DevGang
Авторизоваться

Go: великолепный модуль net/http

Если вы занимаетесь веб-разработкой, вы, вероятно, слышали, как кто-то говорил о том, что Golang - это язык для современной серверной разработки. Он был создан командой людей, действительно знающих свое дело, в компании, которая действительно знает свое дело.

Помимо красивого и простого синтаксиса, одним из его основных преимуществ является стандартная библиотека, которая полностью оснащена модулями, готовыми к производству, и очень хорошо спроектирована (есть также ряд отличных сторонних модулей, но подробнее о некоторых из них в другом статья).

Один из лучших модулей стандартной библиотеки Go, безусловно net/http, имеет смысл, потому что вы действительно не можете работать в Интернете без протокола http.

Он имеет готовый к работе веб-сервер и чрезвычайно простой в использовании http-клиент для отправки запросов. Посмотрим, насколько просто запустить веб-сервер в Go:

package main

import (
    "fmt"
    "net/http"
)

const portNumber = ":8080"

func HomePageHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("We're live !!!"))
}

func main() {

    http.HandleFunc("/", HomePageHandler)
    fmt.Printf("Starting application on port %v\n", portNumber)
    http.ListenAndServe(portNumber, nil)
}

Довольно понятно, правда? Я даже добавил на несколько строк кода больше, чем необходимо, чтобы сделать вещи более очевидными. Метод HandleFunc присоединяет путь к обработчику, и каждая функция обработчика должена принять HTTP request (указатель на него на самом деле), и writer, который отвечает за отправку обратно ответа через сеть к клиенту.

Другой способ написать обработчик:

func HomePageHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "We're live!!!")
}

Это действительно зависит от стиля. Fprintf принимает любой тип, реализующий интерфейс io.Writer (http.Responsewriter замечательный пример этого) в качестве первого аргумента и строку в качестве второго аргумента. Строка - это то, что мы отображаем, а io.Writer - это то, где мы его показываем.

Если мы собираемся вызвать w.Write, мы знаем, что в качестве реализации функции Write интерфейса io.Writer нам нужно предоставить ему часть байтов, а не строку. К счастью, преобразование строки в кусок байтов - это проще простого.

Давайте посмотрим на это *http.Request:

package main

import (
    "fmt"
    "net/http"
)

const portNumber = ":8080"

func HomePageHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello %v, you are at %v. You have sent some query params too: %v.The method you used is %v", r.UserAgent(), r.URL.Path, r.URL.Query(), r.Method)

}

func main() {

    http.HandleFunc("/hello", HomePageHandler)
    fmt.Printf("Starting application on port %v\n", portNumber)
    http.ListenAndServe(portNumber, nil)
}

Я обновил код, чтобы было ясно, что в структуре запроса есть все, что нам нужно для обработки HTTP-запроса, как и в случае с любой классной веб-платформой, такой как Flask или Spring Boot. За исключением того, что это не фреймворк, это стандартный библиотечный модуль http. Хорошо, я признаю, что использование фреймворка, такого как Gin Gonic или Gorilla, ускорит вас и не даст вам писать много шаблонного кода, но для HTTP-модуля стандартной библиотеки это довольно практично.

А что насчет отправки HTTP-запросов на удаленный сервер. Кто-нибудь написал крутой http-клиент для Go, что-то вроде библиотеки requests на Python?

Фактически, первоначальные создатели Go решили никому не давать возможности сделать это, потому что модуль net/http содержит такой простой http-клиент, что действительно не было необходимости в сторонней библиотеке.

Вот вызов API github:

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

func main() {

    req, err := http.Get("https://api.github.com/users")
    if err != nil {
        log.Fatal(err)
    }
    defer req.Body.Close()
    data, err := ioutil.ReadAll(req.Body)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(data))

}

Вероятно, самый сложный код здесь - это вся эта проверка ошибок, но при рассмотрении блоков try / catch на других языках это даже не так уж плохо. Я вызываю defer req.Body.Close(), потому что это хорошая практика - закрывать тело запроса, как если бы вы закрывали файл после того, как закончили чтение из него.

Поскольку тело запроса является реализацией интерфейса io.ReadCloser (что со всеми этими интерфейсами модуля io, верно?), Мы не можем просто распечатать это, нам нужно вызвать ioutil.Readall, который возьмет тело запроса и вернет байтовый фрагмент. Как известно, превратить байтовый фрагмент в строку (и наоборот) несложно.

Наконец, поскольку большая часть серверной разработки сегодня связана с REST API, я думаю, было бы неплохо реализовать базовую конечную точку GET, которая возвращает некоторый JSON, чтобы результат, который мы получаем, хотя бы отдаленно, напоминал то, что вы видели бы в реальном жизнь:

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
)

const portNumber = ":8080"

type User struct {
    Name   string `json:"username"`
    Age    int    `json:"age"`
    Gender string `json:"gender"`
}

func UsersHandler(w http.ResponseWriter, r *http.Request) {

    users := []User{
        {
            Name:   "Chris",
            Age:    22,
            Gender: "Male",
        },
        {
            Name:   "Annie",
            Age:    23,
            Gender: "Female",
        },
        {
            Name:   "Jane",
            Age:    25,
            Gender: "Female",
        },
    }

    usersJson, err := json.Marshal(users)
    if err != nil {
        log.Fatal(err)
    }
    w.Write(usersJson)
}

func main() {

    http.HandleFunc("/users", UsersHandler)
    fmt.Printf("Starting application on port %v\n", portNumber)
    http.ListenAndServe(portNumber, nil)
}

Хорошо, мистер, вам нужно многое объяснить! Что это за странные обратные галочки рядом с полями вашей структуры? и почему все поля в структуре написаны с заглавной буквы?

Хорошие вопросы!

Странные обратные ковычки - отличная особенность Golang. В этом случае мы используем их для описания того, что мы хотим, чтобы ключи наших полей вызывались, когда мы сериализуем их в JSON. Итак, Name становится username. Теперь мы могли бы оставить его как POGS (Plain Old Go Struct?), но тогда поле JSON будет иметь имя с заглавной буквой N, что на самом деле не является соглашением при отображении данных в формате JSON. Но тогда, я полагаю, мы могли бы сделать поле «Name» в нижнем регистре, и это решит проблему, верно? (забудьте на мгновение, что я действительно изменил Name на username, это сработало бы, как вы его назвали)

Нет, не будет. Если вы работаете с Java, вы все знаете об инкапсуляции. Вы бы гордо печатали private String name, и весь мир знал бы, что это имя нельзя трогать, если у него нет сеттера. Ну, Go не любит многословия. Фактически, разработчики Go имели наглость назвать Java языком заикания. Person person = new Person().. ну, это немного похоже на заикание. Однако Go уважает инкапсуляцию. В некотором роде, не так явно, как Java, но это определенно не зависит от всего, что мы все взрослые, бессвязные разработчики Python. Потому что разработчики Go знают, что взрослые довольно безответственны, поэтому на них лучше не полагаться. Как Go заботится об инкапсуляции? Легко, если он написан с большой буквы, он не инкапсулирован (я почти написал инкапсулированный). Так Name поскольку поле доступно для публики, а name не будет. Помимо экспорта или ограничения использования функций и полей структуры для разных пакетов, он чрезвычайно полезен для фильтрации того, какие поля отправлять клиенту, а какие нет. Помимо JSON, обратные галочки используются для многих других целей, например, в ORM gorm, но это определенно будет отдельная статья.

Надеюсь, вам понравилась эта статья, спасибо за чтение!

#Golang
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу