Микрофреймворк Golang Джин

Go Язык программирования Gin

так называемая рама

Фреймворк всегда был мощным инструментом гибкой разработки, позволяя разработчикам быстро приступить к работе и создавать приложения.Иногда даже без фреймворка некоторые разработчики не смогут писать программы. Рост не всегда происходит в одночасье, от написания программ, чтобы получить чувство достижения, до освоения фреймворков и быстрого создания приложений.Когда эти аспекты удобны, вы можете попробовать трансформировать некоторые фреймворки или создать их самостоятельно.

Когда-то я думал, что в мире Python достаточно фреймворков, но позже обнаружил, что это ничто по сравнению с golang. Библиотека net/http, предоставляемая golang, уже очень хороша, и реализация протокола http очень хороша, перестроить фреймворк на основе этого не составит труда, так что фреймворков в экологии много. Теперь, когда порог построения кадра стал ниже, низкий порог также будет приводить к получению кадров неравномерного качества.

Изучение нескольких фреймворков с точки зрения их активности на github, групп обслуживания и использования в производственных средах. НаходитьGinВсе еще легкая структура, чтобы учиться.

Gin

Gin — это микрофреймворк golang с элегантной упаковкой, дружественным API и понятными комментариями к исходному коду.Выпущена версия 1.0. Он обладает характеристиками быстрой, гибкой и отказоустойчивой. На самом деле, для golang зависимость от веб-фреймворка намного меньше, чем у Python и Java. Его собственный net/http достаточно прост, и его производительность очень высока. Фреймворк больше похож на набор часто используемых функций или инструментов. С помощью разработки фреймворка можно не только сэкономить время, вызванное часто используемой инкапсуляцией, но и улучшить стиль кодирования команды и сформировать спецификации.

Ниже приводится краткое введение в использование Gin.

Сначала вам нужно установить, установка относительно проста, просто используйте go get:

go get gopkg.in/gin-gonic/gin.v1

Версия gin размещена на сайте gopkg. В процессе установки gokpg застрял, и тогда мне пришлось скачать исходный код ответа с github по файлу godep в gin, а затем скопировать его в соответствующую директорию.

Об управлении пакетами и зависимостях golang мы поговорим позже.

Hello World

Реализовано с джиномHello worldОчень просто, создайте маршрутизатор и используйте его метод Run:

import (
    "gopkg.in/gin-gonic/gin.v1"
    "net/http"
)

func main(){
    
    router := gin.Default()

    router.GET("/", func(c *gin.Context) {
        c.String(http.StatusOK, "Hello World")
    })
    router.Run(":8000")
}

С помощью всего нескольких строк кода вы можете реализовать веб-службу. Создайте обработчик маршрута, используя метод gin по умолчанию. Затем привяжите правила маршрутизации и функции маршрутизации с помощью методов HTTP. В отличие от функции маршрутизации библиотеки net/http, gin инкапсулирован, и запрос, и ответ инкапсулированы в контекст gin.Context. Наконец, метод Run, запускающий маршрут, прослушивает порт. Небольшой, но полный. Конечно, в дополнение к методу GET, gin также поддерживает POST, PUT, DELETE, OPTION и другие часто используемые методы отдыха.

спокойная маршрутизация

Маршруты для gin взяты из библиотеки httprouter. Поэтому функции, которые есть у httprouter, и у gin тоже есть, но gin не поддерживает регулярные выражения маршрутизации:

func main(){
    router := gin.Default()
    
    router.GET("/user/:name", func(c *gin.Context) {
        name := c.Param("name")
        c.String(http.StatusOK, "Hello %s", name)
    })
}

двоеточие:Добавьте имя параметра, чтобы сформировать параметр маршрута. Его значение можно прочитать с помощью методов c.Params. Конечно, это значение является строкой string. Такие как/user/rsj217/user/helloможет совпасть и/user/и/user/rsj217/не будет совпадать.

☁  ~  curl http://127.0.0.1:8000/user/rsj217
Hello rsj217%                                                                 ☁  ~  curl http://127.0.0.1:8000/user/rsj217/
404 page not found%                                                               ☁  ~  curl http://127.0.0.1:8000/user/
404 page not found%

Кроме:, джин также обеспечивает*параметры обработки чисел,*Есть больше правил, которые можно сопоставить.

func main(){
    router := gin.Default()
    
    router.GET("/user/:name/*action", func(c *gin.Context) {
        name := c.Param("name")
        action := c.Param("action")
        message := name + " is " + action
        c.String(http.StatusOK, message)
    })
}

Эффект доступа следующий

☁  ~  curl http://127.0.0.1:8000/user/rsj217/
rsj217 is /%                                                                  ☁  ~  curl http://127.0.0.1:8000/user/rsj217/中国
rsj217 is /中国%

параметр строки запроса и параметр тела

Услуга, предоставляемая Интернетом, обычно представляет собой взаимодействие между клиентом и сервером. В котором клиент отправляет запрос на сервер, помимо параметров маршрутизации есть только два других параметра, строка запроса и параметр тела сообщения. Так называемая строка запроса, используемая для маршрутизации, использует?подключился позжеkey1=value2&key2=value2параметры формы. Конечно, это ключ-значение закодировано urlencode.

query string

Для обработки параметров часто бывают случаи, когда параметры не существуют.Чтобы предоставить значения по умолчанию, gin также рассматривает это и дает элегантное решение:

func main(){
    router := gin.Default()
    router.GET("/welcome", func(c *gin.Context) {
        firstname := c.DefaultQuery("firstname", "Guest")
        lastname := c.Query("lastname")

        c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
    })
  router.Run()
}

Используйте метод c.DefaultQuery для чтения параметров, который предоставляет значение по умолчанию, когда параметр не существует. Используйте метод Query для чтения обычных параметров. Если параметр не существует, верните пустую строку:

☁  ~  curl http://127.0.0.1:8000/welcome
Hello Guest %                                                                 ☁  ~  curl http://127.0.0.1:8000/welcome\?firstname\=中国
Hello 中国 %                                                                  ☁  ~  curl http://127.0.0.1:8000/welcome\?firstname\=中国\&lastname\=天朝
Hello 中国 天朝%                                                              ☁  ~  curl http://127.0.0.1:8000/welcome\?firstname\=\&lastname\=天朝
Hello  天朝%
☁  ~  curl http://127.0.0.1:8000/welcome\?firstname\=%E4%B8%AD%E5%9B%BD
Hello 中国 %

Причиной использования китайского языка является иллюстрация urlencode. Обратите внимание, что если имя представляет собой пустую строку, значение гостя по умолчанию не будет использоваться, а пустое значение также является значением.Запрос по умолчанию работает только в том случае, если ключ не существует, предоставляя значение по умолчанию.

body

Тело HTTP-сообщения передает данные немного сложнее, чем строка запроса, и существует четыре распространенных формата. Напримерapplication/json,application/x-www-form-urlencoded, application/xmlиmultipart/form-data. Последний в основном используется для загрузки изображений. Формат json хорошо понятен, а urlencode на самом деле не представляет сложности, это не что иное, как помещение содержимого строки запроса в тело, для чего также требуется urlencode. По умолчанию c.PostFROM разрешается вx-www-form-urlencodedилиfrom-dataпараметр.

func main(){
    router := gin.Default()
    router.POST("/form_post", func(c *gin.Context) {
        message := c.PostForm("message")
        nick := c.DefaultPostForm("nick", "anonymous")

        c.JSON(http.StatusOK, gin.H{
            "status":  gin.H{
                "status_code": http.StatusOK,
                "status":      "ok",
            },
            "message": message,
            "nick":    nick,
        })
    })
}

Как и get обрабатывает параметры запроса, метод post также предоставляет случаи для обработки параметров по умолчанию. Точно так же, если параметр не существует, вы получите пустую строку.

☁  ~  curl -X POST http://127.0.0.1:8000/form_post -H "Content-Type:application/x-www-form-urlencoded" -d "message=hello&nick=rsj217" | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   104  100    79  100    25  48555  15365 --:--:-- --:--:-- --:--:-- 79000
{
    "message": "hello",
    "nick": "rsj217",
    "status": {
        "status": "ok",
        "status_code": 200
    }
}

Ранее мы использовали c.String для возврата ответа, который, как следует из названия, возвращает строковый тип. content-type — простой или текстовый. Вызов c.JSON возвращает данные json. Среди них gin.H инкапсулирует способ генерации json и является мощным инструментом. Используя golang, вы можете написать литерал json как динамический язык Для реализации вложенного json вы можете вложить gin.H.

Отправка данных на сервер осуществляется не методом post, а методом put. При этом querystring и body не разделены, и оба могут быть отправлены одновременно:

func main(){
    router := gin.Default()
    
    router.PUT("/post", func(c *gin.Context) {
        id := c.Query("id")
        page := c.DefaultQuery("page", "0")
        name := c.PostForm("name")
        message := c.PostForm("message")
        fmt.Printf("id: %s; page: %s; name: %s; message: %s \n", id, page, name, message)
        c.JSON(http.StatusOK, gin.H{
            "status_code": http.StatusOK,
        })
    })
}

В приведенном выше примере показана отправка данных на сервер с использованием как строки запроса, так и параметра body.

Файл загружен

Загрузить один файл

Основные данные отправки были введены ранее, гдеmultipart/form-dataПреобразовано в загрузку файла. Загрузка файла gin также очень удобна, похожа на собственный метод net/http, разница в том, что gin инкапсулирует собственный запрос в c.Request.

func main(){
    router := gin.Default()
    
    router.POST("/upload", func(c *gin.Context) {
        name := c.PostForm("name")
        fmt.Println(name)
        file, header, err := c.Request.FormFile("upload")
        if err != nil {
            c.String(http.StatusBadRequest, "Bad request")
            return
        }
        filename := header.Filename

        fmt.Println(file, err, filename)

        out, err := os.Create(filename)
        if err != nil {
            log.Fatal(err)
        }
        defer out.Close()
        _, err = io.Copy(out, file)
        if err != nil {
            log.Fatal(err)
        }
        c.String(http.StatusCreated, "upload successful")
    })
    router.Run(":8000")
}

использоватьc.Request.FormFileРазберите атрибут имени файла клиента. Если вы не передадите файл, он выдаст ошибку, поэтому вам нужно обработать эту ошибку. Один из способов — вернуться напрямую. Затем используйте операцию os, чтобы скопировать данные файла на жесткий диск.

Используйте следующую команду, чтобы проверить загрузку, обратите внимание, что загрузкаc.Request.FormFileУказанный параметр, значение которого должно быть абсолютным путем:

curl -X POST http://127.0.0.1:8000/upload -F "upload=@/Users/ghost/Desktop/pic.jpg" -H "Content-Type: multipart/form-data"

Загрузить несколько файлов

Загрузить один файл легко, не думайте, что несколько файлов будут громоздкими. Согласно рисунку тыквы, так называемые множественные файлы — это не что иное, как еще один просмотр файлов, а затем однократное копирование хранилища данных. Следующее записывает только обработчик, опуская маршрут инициализации основной функции и включая мониторинг сервера:

router.POST("/multi/upload", func(c *gin.Context) {
        err := c.Request.ParseMultipartForm(200000)
        if err != nil {
            log.Fatal(err)
        }

        formdata := c.Request.MultipartForm 

        files := formdata.File["upload"] 
        for i, _ := range files { /
            file, err := files[i].Open()
            defer file.Close()
            if err != nil {
                log.Fatal(err)
            }

            out, err := os.Create(files[i].Filename)

            defer out.Close()

            if err != nil {
                log.Fatal(err)
            }

            _, err = io.Copy(out, file)

            if err != nil {
                log.Fatal(err)
            }

            c.String(http.StatusCreated, "upload successful")

        }

    })

Аналогично загрузке одного файла, но используетc.Request.MultipartFormПолучите дескриптор файла, затем получите данные файла, а затем выполните чтение и запись.

Загрузить с помощью curl

curl -X POST http://127.0.0.1:8000/multi/upload -F "upload=@/Users/ghost/Desktop/pic.jpg" -F "upload=@/Users/ghost/Desktop/journey.png" -H "Content-Type: multipart/form-data"

загрузка формы

Выше мы все используем curl для загрузки, на самом деле пользователи загружают картинки больше через формы или ajax и некоторые запросы. Ниже показано, как загрузить форму веб-формы.

Сначала нам нужно написать страницу формы, поэтому нам нужно представить, как gin отображает шаблон. Ранее мы видели c.String и c.JSON. Давайте посмотрим на метод c.HTML.

Сначала вам нужно определить папку для шаблонов. Затем вызовите c.HTML для рендеринга шаблона, и вы сможете передавать значения в шаблон через gin.H. Пока что, будь то String, JSON или HTML, а также следующие XML и YAML, вы можете видеть, что интерфейс, инкапсулированный Gin, лаконичен и прост в использовании.

Создайте папку templates, а затем создайте в ней html файл upload.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>upload</title>
</head>
<body>
<h3>Single Upload</h3>
<form action="/upload", method="post" enctype="multipart/form-data">
    <input type="text" value="hello gin" />
    <input type="file" name="upload" />
    <input type="submit" value="upload" />
</form>


<h3>Multi Upload</h3>
<form action="/multi/upload", method="post" enctype="multipart/form-data">
    <input type="text" value="hello gin" />
    <input type="file" name="upload" />
    <input type="file" name="upload" />
    <input type="submit" value="upload" />
</form>

</body>
</html>

загрузка проста и не имеет параметров. Один для загрузки одного файла и один для загрузки нескольких файлов.

    router.LoadHTMLGlob("templates/*")
    router.GET("/upload", func(c *gin.Context) {
        c.HTML(http.StatusOK, "upload.html", gin.H{})
    })

Используйте LoadHTMLGlob для определения пути к файлу шаблона.

привязка параметра

мы уже встречалисьx-www-form-urlencodedОбработка параметра типа, все больше и больше приложений используют JSON для связи, то есть независимо от возвращаемого ответа или отправленного запроса тип содержимогоapplication/jsonформат. И для некоторых старых страниц веб-форм илиx-www-form-urlencodedформе, которая требует, чтобы наш сервер мог изменять параметры удержания этих различных типов контента.

Это легко решить в мире Python, ведь динамические языки не нуждаются в реализации определенной модели данных. Следовательно, можно написать декоратор для инкапсуляции данных двух форматов в модель данных. В golang разобраться с этим непросто, благо есть gin, и функция привязки модели у них очень мощная.

type User struct {
    Username string `form:"username" json:"username" binding:"required"`
    Passwd   string `form:"passwd" json:"passwd" bdinding:"required"`
    Age      int    `form:"age" json:"age"`
}

func main(){
    router := gin.Default()
    
    router.POST("/login", func(c *gin.Context) {
        var user User
        var err error
        contentType := c.Request.Header.Get("Content-Type")

        switch contentType {
        case "application/json":
            err = c.BindJSON(&user)
        case "application/x-www-form-urlencoded":
            err = c.BindWith(&user, binding.Form)
        }

        if err != nil {
            fmt.Println(err)
            log.Fatal(err)
        }

        c.JSON(http.StatusOK, gin.H{
            "user":   user.Username,
            "passwd": user.Passwd,
            "age":    user.Age,
        })

    })

}

сначала определитеUserструктура модели, а затем для типа контента клиента используйтеBindJSONиBindWithметод.

☁  ~  curl -X POST http://127.0.0.1:8000/login -H "Content-Type:application/x-www-form-urlencoded" -d "username=rsj217&passwd=123&age=21" | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    79  100    46  100    33  41181  29543 --:--:-- --:--:-- --:--:-- 46000
{
    "age": 21,
    "passwd": "123",
    "username": "rsj217"
}
☁  ~  curl -X POST http://127.0.0.1:8000/login -H "Content-Type:application/x-www-form-urlencoded" -d "username=rsj217&passwd=123&new=21" | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    78  100    45  100    33  37751  27684 --:--:-- --:--:-- --:--:-- 45000
{
    "age": 0,
    "passwd": "123",
    "username": "rsj217"
}
☁  ~  curl -X POST http://127.0.0.1:8000/login -H "Content-Type:application/x-www-form-urlencoded" -d "username=rsj217&new=21" | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0curl: (52) Empty reply from server
No JSON object could be decoded

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

Изменено в JSON:

☁  ~  curl -X POST http://127.0.0.1:8000/login -H "Content-Type:application/json" -d '{"username": "rsj217", "passwd": "123", "age": 21}' | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    96  100    46  100    50  32670  35511 --:--:-- --:--:-- --:--:-- 50000
{
    "age": 21,
    "passwd": "123",
    "username": "rsj217"
}
☁  ~  curl -X POST http://127.0.0.1:8000/login -H "Content-Type:application/json" -d '{"username": "rsj217", "passwd": "123", "new": 21}' | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    95  100    45  100    50  49559  55066 --:--:-- --:--:-- --:--:-- 50000
{
    "age": 0,
    "passwd": "123",
    "username": "rsj217"
}
☁  ~  curl -X POST http://127.0.0.1:8000/login -H "Content-Type:application/json" -d '{"username": "rsj217", "new": 21}' | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0curl: (52) Empty reply from server
No JSON object could be decoded
☁  ~  curl -X POST http://127.0.0.1:8000/login -H "Content-Type:application/json" -d '{"username": "rsj217", "passwd": 123, "new": 21}' | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0curl: (52) Empty reply from server
No JSON object could be decoded

Еще один момент, который следует учитывать при использовании json, заключается в том, что json имеет типы данных, поэтому для{"passwd": "123"}и{"passwd": 123}являются разными типами данных, и синтаксический анализ должен соответствовать соответствующему типу данных, иначе произойдет ошибка.

Конечно, gin также предоставляет более продвинутый метод c.Bind, который автоматически определяет, является ли тип содержимого формой привязки или параметром json.

    router.POST("/login", func(c *gin.Context) {
        var user User
        
        err := c.Bind(&user)
        if err != nil {
            fmt.Println(err)
            log.Fatal(err)
        }

        c.JSON(http.StatusOK, gin.H{
            "username":   user.Username,
            "passwd":     user.Passwd,
            "age":        user.Age,
        })

    })

Мультиформатный рендеринг

Поскольку запросы могут использовать разные типы контента, то же самое могут делать и ответы. Обычно ответ будет иметь html, текст, обычный, json и xml и т. д. gin предоставляет очень элегантный метод рендеринга. До сих пор мы видели c.String, c.JSON, c.HTML, давайте представим c.XML.

    router.GET("/render", func(c *gin.Context) {
        contentType := c.DefaultQuery("content_type", "json")
        if contentType == "json" {
            c.JSON(http.StatusOK, gin.H{
                "user":   "rsj217",
                "passwd": "123",
            })
        } else if contentType == "xml" {
            c.XML(http.StatusOK, gin.H{
                "user":   "rsj217",
                "passwd": "123",
            })
        }

    })

Результат выглядит следующим образом:

☁  ~  curl  http://127.0.0.1:8000/render\?content_type\=json
{"passwd":"123","user":"rsj217"}
☁  ~  curl  http://127.0.0.1:8000/render\?content_type\=xml
<map><user>rsj217</user><passwd>123</passwd></map>%

перенаправить

gin довольно прост для перенаправленных запросов. Вызовите метод Redirect контекста:

    router.GET("/redict/google", func(c *gin.Context) {
        c.Redirect(http.StatusMovedPermanently, "https://google.com")
    })

маршрутизация пакетов

Студенты, знакомые с Flask, должны иметь хорошее представление о группировке чертежей. Flask предоставляет схемы для управления API группировки организаций. Gin также предоставляет такую ​​функцию, делая логику вашего кода более модульной, а группировка также позволяет легко определить область использования промежуточного программного обеспечения.

    v1 := router.Group("/v1")

    v1.GET("/login", func(c *gin.Context) {
        c.String(http.StatusOK, "v1 login")
    })

    v2 := router.Group("/v2")

    v2.GET("/login", func(c *gin.Context) {
        c.String(http.StatusOK, "v2 login")
    })

Эффект доступа выглядит следующим образом:

☁  ~  curl http://127.0.0.1:8000/v1/login
v1 login%                                                                                 ☁  ~  curl http://127.0.0.1:8000/v2/login
v2 login%

промежуточное ПО

Основная особенность архитектуры golang net/http заключается в том, что создавать промежуточное ПО особенно просто. gin также предоставляет аналогичное промежуточное ПО. Следует отметить, что промежуточное ПО работает только с зарегистрированными функциями маршрутизации. Для маршрутизации пакетов вложенное использование ПО промежуточного слоя может ограничить область действия ПО промежуточного слоя. Промежуточное ПО делится на глобальное промежуточное ПО, промежуточное ПО одиночной маршрутизации и промежуточное ПО групп.

глобальное промежуточное ПО

Сначала определите функцию промежуточного программного обеспечения:

func MiddleWare() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("before middleware")
        c.Set("request", "clinet_request")
        c.Next()
        fmt.Println("before middleware")
    }
}

Функция очень проста, она только добавит атрибут в контекст c и присвоит ему значение.позжеОбработчик маршрута может извлечь свое значение после оформления промежуточным программным обеспечением. Следует отметить, что хотя оно и называется глобальным промежуточным ПО, до тех пор, пока маршрут, установленный перед процессом регистрации промежуточного ПО, не будет затронут зарегистрированным промежуточным ПО. Только правила функции маршрутизации кода ниже промежуточного программного обеспечения будут оформлены промежуточным программным обеспечением.

    router.Use(MiddleWare())
    {
        router.GET("/middleware", func(c *gin.Context) {
            request := c.MustGet("request").(string)
            req, _ := c.Get("request")
            c.JSON(http.StatusOK, gin.H{
                "middile_request": request,
                "request": req,
            })
        })
    }

Используйте маршрутизатор для украшения промежуточного программного обеспечения, затем в/middlerwareВы можете прочитать значение запроса, обратите внимание наrouter.Use(MiddleWare())Функция маршрутизации над кодом не будет иметь эффект украшения промежуточным программным обеспечением.

Использование фигурных скобок для включения декорированной функции маршрутизации — это просто спецификация кода.Даже если функция маршрутизации не включена, пока маршрутизатор используется для маршрутизации, он приравнивается к декорированному. Чтобы различать область разрешений, вы можете использовать объект, возвращаемый группой, для регистрации промежуточного программного обеспечения.

☁  ~  curl  http://127.0.0.1:8000/middleware
{"middile_request":"clinet_request","request":"clinet_request"}

Используйте, если не зарегистрированыMustGetМетод для чтения значения c выдаст ошибку, вместо этого вы можете использовать метод Get.

Приведенный выше метод оформления регистрации заставит весь код, написанный ниже, по умолчанию использовать зарегистрированное промежуточное ПО маршрутизатора.

ПО промежуточного слоя для единой маршрутизации

Конечно, gin также предоставляет регистрацию для указанной функции маршрутизации.

    router.GET("/before", MiddleWare(), func(c *gin.Context) {
        request := c.MustGet("request").(string)
        c.JSON(http.StatusOK, gin.H{
            "middile_request": request,
        })
    })

Напишите приведенный выше код перед router.Use(Middleware()), вы также можете увидеть/beforeукрашен промежуточным ПО.

ПО промежуточного слоя группы

Промежуточное ПО группы похоже, если функция промежуточного ПО зарегистрирована на соответствующем групповом маршруте:

authorized := router.Group("/", MyMiddelware())
// 或者这样用:
authorized := router.Group("/")
authorized.Use(MyMiddelware())
{
    authorized.POST("/login", loginEndpoint)
}

Группы могут быть вложенными, поскольку промежуточное ПО также может быть вложенным в соответствии с правилами вложенности группы.

практика промежуточного программного обеспечения

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

    router.GET("/auth/signin", func(c *gin.Context) {
        cookie := &http.Cookie{
            Name:     "session_id",
            Value:    "123",
            Path:     "/",
            HttpOnly: true,
        }
        http.SetCookie(c.Writer, cookie)
        c.String(http.StatusOK, "Login successful")
    })

    router.GET("/home", AuthMiddleWare(), func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"data": "home"})
    })

Функция входа в систему установит файл cookie session_id.Обратите внимание, что путь должен быть указан здесь как/, иначе gin автоматически установит путь к куки/auth, особенно странный вопрос./homneЛогика AuthMiddleWare очень проста: после регистрации в промежуточном программном обеспечении AuthMiddleWare сначала будет выполняться логика AuthMiddleWare, а затем/homeлогика.

Код для AuthMiddleWare выглядит следующим образом:

func AuthMiddleWare() gin.HandlerFunc {
    return func(c *gin.Context) {
        if cookie, err := c.Request.Cookie("session_id"); err == nil {
            value := cookie.Value
            fmt.Println(value)
            if value == "123" {
                c.Next()
                return
            }
        }
        c.JSON(http.StatusUnauthorized, gin.H{
            "error": "Unauthorized",
        })
        c.Abort()
        return
    }
}

Прочитайте файл cookie из контекстного запроса, а затем проверьте файл cookie. Если есть проблема, завершите запрос и вернитесь напрямую. Здесь используется метод c.Abort().

In [7]: resp = requests.get('http://127.0.0.1:8000/home')

In [8]: resp.json()
Out[8]: {u'error': u'Unauthorized'}

In [9]: login = requests.get('http://127.0.0.1:8000/auth/signin')

In [10]: login.cookies
Out[10]: <RequestsCookieJar[Cookie(version=0, name='session_id', value='123', port=None, port_specified=False, domain='127.0.0.1', domain_specified=False, domain_initial_dot=False, path='/', path_specified=True, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={'HttpOnly': None}, rfc2109=False)]>

In [11]: resp = requests.get('http://127.0.0.1:8000/home', cookies=login.cookies)

In [12]: resp.json()
Out[12]: {u'data': u'home'}

асинхронная сопрограмма

Одним из инструментов Golang с высокой степенью параллелизма являются сопрограммы. В gin асинхронные задачи можно реализовать с помощью сопрограмм. Из-за задействованного асинхронного процесса контекст запроса необходимо скопировать в асинхронный контекст, и этот контекст доступен только для чтения.

    router.GET("/sync", func(c *gin.Context) {
        time.Sleep(5 * time.Second)
        log.Println("Done! in path" + c.Request.URL.Path)
    })

    router.GET("/async", func(c *gin.Context) {
        cCp := c.Copy()
        go func() {
            time.Sleep(5 * time.Second)
            log.Println("Done! in path" + cCp.Request.URL.Path)
        }()
    })

В момент запроса засыпайте на 5 секунд, и логика синхронизации увидит, что процесс службы засыпает. Асинхронная логика видит, что ответ возвращается, а затем программа все ещеЗа кулисамиобработка сопрограмм.

пользовательский маршрутизатор

Gin может не только использовать роутер самого фреймворка для запуска, но и использовать функции самого net/http:

func main() {
    router := gin.Default()
    http.ListenAndServe(":8080", router)
}

или

func main() {
    router := gin.Default()

    s := &http.Server{
        Addr:           ":8000",
        Handler:        router,
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }
    s.ListenAndServe()
}

Конечно, есть изящное решение для перезапуска и завершения процессов. Позже мы рассмотрим использование супервизора для управления процессами golang.

Суммировать

Gin — это легкий и мощный веб-фреймворк golang. Мы сделали краткое введение в функции, связанные с общим развитием. Примерами являются запуск службы, обработка параметров запроса и рендеринг формата ответа, а также загрузка и проверка подлинности промежуточного программного обеспечения. Лучшее мастерство приходит с практикой, а исходный код джина очень подробный.Вы можете прочитать исходный код для получения более подробных функций и магических возможностей.

часть текстакод