Джин (два): использование маршрутизации

Go Gin

больше статейСумасшедшая точка -> 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 и имею некоторое интуитивное представление о некоторых распространенных методах использования.

Код для этой главы

Github