больше статейСумасшедшая точка -> ISLAND
проходить черезпредыдущая глава, построить простойGin
web
Проект очень прост, а также вводит некоторые новые концепции, такие как:маршрутизация Router
.
маршрутизацияЭто очень важная концепция, и все интерфейсы должны управляться маршрутизацией.
метод запроса
Gin
поддержка маршрутизацииGET
, POST
, PUT
, DELETE
, PATCH
, HEAD
, OPTIONS
запрос вместе сAny
Функции, вы можете поддерживать все предыдущие запросы.
Добавьте код из предыдущей главы в маршрутизацию других методов запроса и напишите модульные тесты.
// 省略其他代码
// 添加 Get 请求路由
router.GET("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin get method")
})
// 添加 Post 请求路由
router.POST("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin post method")
})
// 添加 Put 请求路由
router.PUT("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin put method")
})
// 添加 Delete 请求路由
router.DELETE("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin delete method")
})
// 添加 Patch 请求路由
router.PATCH("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin patch method")
})
// 添加 Head 请求路由
router.HEAD("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin head method")
})
// 添加 Options 请求路由
router.OPTIONS("/", func(context *gin.Context) {
context.String(http.StatusOK, "hello gin options method")
})
// 省略其他代码
модульные тесты, показывающие только одинPost
функция запроса, в основном такая же, какGet
Запрос согласован. Другие коды можно найти по адресу Github в конце статьи.
// router("/") post 测试
func TestIndexPostRouter(t *testing.T) {
router := initRouter.SetupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, "/", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "hello gin post method", w.Body.String())
}
На этом этапе выполняются модульные тесты, и все тесты проходят отлично.
Но есть и проблема: функции в роутах, соответствующих всем запросам, в принципе одинаковые, но есть тонкие отличия, но мы их прописали полностью в каждом роуте, поэтому нужно вычленять общую логику.
func retHelloGinAndMethod(context *gin.Context) {
context.String(http.StatusOK, "hello gin "+strings.ToLower(context.Request.Method)+" method")
}
Мы извлекаем общую часть метода и передаемcontext.Request.Method
Извлеките запрошенный метод и преобразуйте его в нижний регистр. На этом этапе мы можем преобразовать нашу маршрутизацию и заменить функции исходной маршрутизации новыми функциями, которые мы написали.
// 添加 Get 请求路由
router.GET("/", retHelloGinAndMethod)
// 添加 Post 请求路由
router.POST("/", retHelloGinAndMethod)
// 添加 Put 请求路由
router.PUT("/", retHelloGinAndMethod)
// 添加 Delete 请求路由
router.DELETE("/", retHelloGinAndMethod)
// 添加 Patch 请求路由
router.PATCH("/", retHelloGinAndMethod)
// 添加 Head 请求路由
router.HEAD("/", retHelloGinAndMethod)
// 添加 Options 请求路由
router.OPTIONS("/", retHelloGinAndMethod)
Выполнение модульного теста на этом этапе по-прежнему проходит отлично.
Обработчик
После демонстрации и работы приведенного выше простого примера, теперь мы, наверное, можем понять, что маршруту нужно передать два параметра, один — это путь, а другой — метод, выполняемый маршрутом, мы называем его процессором.Handler
, а параметр является параметром переменной длины. Другими словами, несколько обработчиков могут быть переданы для формирования цепочки обработчиков.
В то же время есть некоторые требования к функции-обработчику, которую необходимо передать вGin.Context
указатель, и в то же время необходимо обрабатывать значение через этот указатель.
Функция Handler может возвращать строки, Json, Html и другие форматы или файлы форм во внешний интерфейс, и мы представим их один за другим позже.
Получить параметры в пути маршрутизации
Зная поддерживаемые маршрутом методы и соответствующие процессоры, вы должны понимать, как получить параметры из маршрута.
Напишите новый маршрут следующим образом:
//省略其他代码
// 添加 user
router.GET("/user/:name",handler.Save)
// 省略其他代码
На этот раз мы обнаружили, что в оригинале только/
Появился в случае, когда разделитель/:
Этот символ указывает, что следующая строка является заполнителем для передаваемого значения. , на данный момент наш маршрут/user/{name}
Нам не нужно писать все Handlers в папку, она будет раздута, поэтому создаем новую папкуhandler
, создать в папкеuserHandler.go
файл, напишите этот файл.
package handler
import (
"github.com/gin-gonic/gin"
"net/http"
)
func UserSave(context *gin.Context) {
username := context.Param("name")
context.String(http.StatusOK, "用户已经保存")
}
Так же мы используемcontext.Param
Можно получить параметры пути маршрутизации.
На данный момент пришло время написать наши модульные тесты.
создать новыйuser_test.go
документ.
func TestUserSave(t *testing.T) {
username := "lisi"
router := initRouter.SetupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/user/"+username, nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "用户"+username+"已经保存", w.Body.String())
}
Запустите модульные тесты, тесты пройдены. Точно так же мы можем запустить наш проект, набрав в браузереlocalhost:8080/user/lisi
Также доступно на странице браузера用户lisi已经保存
Конечно, есть несколько способов получить параметры. Для разных маршрутов Gin предоставляет разные методы получения параметров, например:/user?name=lisi&age=18
.
Снова добавляемHandler
, как обработка. существуетuserHandler
Добавьте следующий метод в .
// 通过 query 方法进行获取参数
func UserSaveByQuery(context *gin.Context) {
username := context.Query("name")
age := context.Query("age")
context.String(http.StatusOK, "用户:"+username+",年龄:"+age+"已经保存")
}
Заодно добавить и улучшить маршрутизацию.
router.GET("/user", handler.UserSaveByQuery)
После того, как маршрутизация завершена, модульные тесты могут быть переписаны для уточнения проекта.
func TestUserSaveQuery(t *testing.T) {
username := "lisi"
age := 18
router := initRouter.SetupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/user?name="+username+"&age="+strconv.Itoa(age), nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "用户:"+username+",年龄:"+strconv.Itoa(age)+"已经保存", w.Body.String())
}
Запустите тест, тест проходит. и доступ через браузерlocalhost:8080/user?name=lisi
, напечатано на странице用户:lisi,年龄:18已经保存
.
Конечно, вы также можетеcontext.DefaultQuery
метод при выборке присваивает значение по умолчанию, если такого значения нет.
Повторно отредактируйте код для получения возраста и измените его на следующий код
age := context.DefaultQuery("age", "20")
Перепишите наши модульные тесты и запустите.
func TestUserSaveWithNotAge(t *testing.T) {
username := "lisi"
router := initRouter.SetupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/user?name="+username, nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "用户:"+username+",年龄:20已经保存", w.Body.String())
}
Доступ также возможен через браузер/user?name=lisi
можно посмотреть в браузере用户:lisi,年龄:20已经保存
.
Конечно, также предусмотрены другие методы получения параметров,QueryArray
получить массив иQueryMap
Получите карту.
пакеты маршрутизации
Здесь мы снова смотримSetupRouter
При использовании этого метода внутренние маршруты можно в основном разделить на две категории./
и/user
, если функция будет добавлена в будущем, то неизбежно появится большое количество маршрутов, поэтому нам нужно управлять маршрутами.Gin предоставляет нам группы маршрутизации.
первый/
Маршруты пишутся вместе и выполняютсяindex_test.go
модульный тест.
index := router.Group("/")
{
// 添加 Get 请求路由
index.GET("", retHelloGinAndMethod)
// 添加 Post 请求路由
index.POST("", retHelloGinAndMethod)
// 添加 Put 请求路由
index.PUT("", retHelloGinAndMethod)
// 添加 Delete 请求路由
index.DELETE("", retHelloGinAndMethod)
// 添加 Patch 请求路由
index.PATCH("", retHelloGinAndMethod)
// 添加 Head 请求路由
index.HEAD("", retHelloGinAndMethod)
// 添加 Options 请求路由
index.OPTIONS("", retHelloGinAndMethod)
}
пройти черезrouter.Group
Возвращается новый пакетный маршрут, а предыдущий маршрут просто изменяется новым пакетным маршрутом. Конечно, группы по-прежнему могут быть вложены в группы.
Ранее в методе запроса было сказано, что естьAny
Функция может передать любой запрос, в этот момент мы можем поставитьindex
Замените все запросы внутри наAny
index := router.Group("/")
{
index.Any("", retHelloGinAndMethod)
}
Запустите модульные тесты, и все тесты пройдут.
В это время также были обнаружены преимущества модульного тестирования.Хотя раньше для написания модульных тестов требовалось время и опыт, но в будущем, если функция будет изменена, нам нужно только запустить модульный тест, чтобы узнать, является ли наша функция правильно.В более позднем функциональном тесте Сильно сокращается опыт и время.
Так же кладемuser
Также выполняется группировка.Группировка не только объединяет один и тот же логический код, но и предоставляет тот же префикс маршрутизации, а модифицированная маршрутизация остается такой же, как и раньше. запускать модульные тестыuser_test.go
, Модульный тест можно полностью пройти, что доказывает, что с нашим кодом проблем нет.
userRouter := router.Group("/user")
{
userRouter.GET("/:name", handler.UserSave)
userRouter.GET("", handler.UserSaveByQuery)
}
Суммировать
Благодаря использованию простой маршрутизации я в основном понимаю статус маршрутизации в Gin и имею некоторое интуитивное представление о некоторых распространенных методах использования.