Привет всем, я Jianyu, это адрес этого проекта:GitHub.com/Vicious Genetics/go-…, Если у вас есть какие-либо вопросы, пожалуйста, не стесняйтесь общаться и общаться.
считать
Прежде всего, прежде чем начать первоначальный проект, каждый должен подумать об этом.
-
Программа конфигурации текста написана в коде, хорошо?
-
Код ошибки API жестко запрограммирован в программе?
-
db дескриптор достается кому угодно
Open
, нет единого управления, понятно? -
Чтобы получить общедоступные параметры, такие как пейджинг, каждый может написать свою логику, хорошо?
Очевидно, что в более формальных проектах ответы на эти вопросыНе можетЧтобы решить эти проблемы, мы выбираем библиотеку для чтения и записи файла конфигурации, в настоящее время больше пожараviper, Если вам интересно, вы можете кратко понять это в будущем.Если вам не интересно, подождите, пока вы не соприкоснетесь с этим позже.
Но этот сериал выбираетgo-ini/ini,этокитайский документ. Вы должны просто прочитать его документацию, а затем завершить следующее содержание.
Цель этой статьи
- Напишите простой пакет кода ошибки API.
- Заполните демонстрационный пример.
- Объясните точки знаний, связанные с демонстрацией.
Представьте и инициализируйте проект
Инициализировать каталог проекта
В предыдущей главе мы инициализировалиgo-gin-example
Project, то нам нужно продолжить добавлять следующую структуру каталогов:
go-gin-example/
├── conf
├── middleware
├── models
├── pkg
├── routers
└── runtime
- Conf: используется для хранения файлов конфигурации
- промежуточное ПО: промежуточное ПО приложения
- модели: модели баз данных приложений
- pkg: сторонний пакет
- обработка логики маршрутизации маршрутизаторов
- время выполнения: данные времени выполнения приложения
Добавить модули GO заменять
Открытымgo.mod
Документ, новыйreplace
Элементы конфигурации следующие:
module github.com/EDDYCJY/go-gin-example
go 1.13
require (...)
replace (
github.com/EDDYCJY/go-gin-example/pkg/setting => ~/go-application/go-gin-example/pkg/setting
github.com/EDDYCJY/go-gin-example/conf => ~/go-application/go-gin-example/pkg/conf
github.com/EDDYCJY/go-gin-example/middleware => ~/go-application/go-gin-example/middleware
github.com/EDDYCJY/go-gin-example/models => ~/go-application/go-gin-example/models
github.com/EDDYCJY/go-gin-example/routers => ~/go-application/go-gin-example/routers
)
Может быть, вы не понимаете, почему вы пришли сюда, чтобы добавитьreplace
Элементы конфигурации, сначала вы должны увидеть, что мы используем полный путь ссылки внешнего модуля (github.com/EDDYCJY/go-gin-example/xxx
), и этот модуль не запихнут на удалёнку, нет возможности его скачать, поэтому нужно использоватьreplace
Укажите его для чтения пути локального модуля, чтобы можно было решить проблему чтения локального модуля.
Примечание: при каждом новом последующем локальном каталоге приложения, вам необходимо по инициативе добавить файл go.mod replace (напоминать не буду), если пропустили, то будет ошибка компиляции, нельзя найти модуль.
Исходная база данных проекта
новыйblog
база данных, закодированная какutf8_general_ci
,существуетblog
В базе данных создайте следующую таблицу
1. Таблица этикеток
CREATE TABLE `blog_tag` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT '' COMMENT '标签名称',
`created_on` int(10) unsigned DEFAULT '0' COMMENT '创建时间',
`created_by` varchar(100) DEFAULT '' COMMENT '创建人',
`modified_on` int(10) unsigned DEFAULT '0' COMMENT '修改时间',
`modified_by` varchar(100) DEFAULT '' COMMENT '修改人',
`deleted_on` int(10) unsigned DEFAULT '0',
`state` tinyint(3) unsigned DEFAULT '1' COMMENT '状态 0为禁用、1为启用',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='文章标签管理';
2. Таблица статей
CREATE TABLE `blog_article` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`tag_id` int(10) unsigned DEFAULT '0' COMMENT '标签ID',
`title` varchar(100) DEFAULT '' COMMENT '文章标题',
`desc` varchar(255) DEFAULT '' COMMENT '简述',
`content` text,
`created_on` int(11) DEFAULT NULL,
`created_by` varchar(100) DEFAULT '' COMMENT '创建人',
`modified_on` int(10) unsigned DEFAULT '0' COMMENT '修改时间',
`modified_by` varchar(255) DEFAULT '' COMMENT '修改人',
`deleted_on` int(10) unsigned DEFAULT '0',
`state` tinyint(3) unsigned DEFAULT '1' COMMENT '状态 0为禁用1为启用',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='文章管理';
3. Форма сертификации
CREATE TABLE `blog_auth` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT '' COMMENT '账号',
`password` varchar(50) DEFAULT '' COMMENT '密码',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `blog`.`blog_auth` (`id`, `username`, `password`) VALUES (null, 'test', 'test123456');
Написать пакет конфигурации проекта
существуетgo-gin-example
В каталоге приложения потянитеgo-ini/ini
Пакеты зависимостей следующие:
$ go get -u github.com/go-ini/ini
go: finding github.com/go-ini/ini v1.48.0
go: downloading github.com/go-ini/ini v1.48.0
go: extracting github.com/go-ini/ini v1.48.0
Далее нам нужно написать базовый файл конфигурации приложения, вgo-gin-example
изconf
новый каталогapp.ini
файл, напишите содержимое:
#debug or release
RUN_MODE = debug
[app]
PAGE_SIZE = 10
JWT_SECRET = 23347$040412
[server]
HTTP_PORT = 8000
READ_TIMEOUT = 60
WRITE_TIMEOUT = 60
[database]
TYPE = mysql
USER = 数据库账号
PASSWORD = 数据库密码
#127.0.0.1:3306
HOST = 数据库IP:数据库端口号
NAME = blog
TABLE_PREFIX = blog_
построить конфигурацию вызоваsetting
модуль, вgo-gin-example
изpkg
новый каталогsetting
Каталог (обратите внимание на новую конфигурацию замены), новыйsetting.go
файл, напишите содержимое:
package setting
import (
"log"
"time"
"github.com/go-ini/ini"
)
var (
Cfg *ini.File
RunMode string
HTTPPort int
ReadTimeout time.Duration
WriteTimeout time.Duration
PageSize int
JwtSecret string
)
func init() {
var err error
Cfg, err = ini.Load("conf/app.ini")
if err != nil {
log.Fatalf("Fail to parse 'conf/app.ini': %v", err)
}
LoadBase()
LoadServer()
LoadApp()
}
func LoadBase() {
RunMode = Cfg.Section("").Key("RUN_MODE").MustString("debug")
}
func LoadServer() {
sec, err := Cfg.GetSection("server")
if err != nil {
log.Fatalf("Fail to get section 'server': %v", err)
}
HTTPPort = sec.Key("HTTP_PORT").MustInt(8000)
ReadTimeout = time.Duration(sec.Key("READ_TIMEOUT").MustInt(60)) * time.Second
WriteTimeout = time.Duration(sec.Key("WRITE_TIMEOUT").MustInt(60)) * time.Second
}
func LoadApp() {
sec, err := Cfg.GetSection("app")
if err != nil {
log.Fatalf("Fail to get section 'app': %v", err)
}
JwtSecret = sec.Key("JWT_SECRET").MustString("!@)*#)!@U#@*!@!)")
PageSize = sec.Key("PAGE_SIZE").MustInt(10)
}
Текущая структура каталогов:
go-gin-example
├── conf
│ └── app.ini
├── go.mod
├── go.sum
├── middleware
├── models
├── pkg
│ └── setting.go
├── routers
└── runtime
Написать пакет кода ошибки API
код ошибкиe
модуль, вgo-gin-example
изpkg
новый каталогe
Каталог (обратите внимание на новую конфигурацию замены), создайте новыйcode.go
иmsg.go
файл, напишите содержимое:
1. code.go:
package e
const (
SUCCESS = 200
ERROR = 500
INVALID_PARAMS = 400
ERROR_EXIST_TAG = 10001
ERROR_NOT_EXIST_TAG = 10002
ERROR_NOT_EXIST_ARTICLE = 10003
ERROR_AUTH_CHECK_TOKEN_FAIL = 20001
ERROR_AUTH_CHECK_TOKEN_TIMEOUT = 20002
ERROR_AUTH_TOKEN = 20003
ERROR_AUTH = 20004
)
2. msg.go:
package e
var MsgFlags = map[int]string {
SUCCESS : "ok",
ERROR : "fail",
INVALID_PARAMS : "请求参数错误",
ERROR_EXIST_TAG : "已存在该标签名称",
ERROR_NOT_EXIST_TAG : "该标签不存在",
ERROR_NOT_EXIST_ARTICLE : "该文章不存在",
ERROR_AUTH_CHECK_TOKEN_FAIL : "Token鉴权失败",
ERROR_AUTH_CHECK_TOKEN_TIMEOUT : "Token已超时",
ERROR_AUTH_TOKEN : "Token生成失败",
ERROR_AUTH : "Token错误",
}
func GetMsg(code int) string {
msg, ok := MsgFlags[code]
if ok {
return msg
}
return MsgFlags[ERROR]
}
набор инструментов для письма
существуетgo-gin-example
изpkg
новый каталогutil
каталог (обратите внимание на новую конфигурацию замены) и потянитеcom
Пакеты зависимостей следующие:
go get -u github.com/unknwon/com
Как получить номер страницы пагинации
существуетutil
новый каталогpagination.go
, напишите содержание:
package util
import (
"github.com/gin-gonic/gin"
"github.com/unknwon/com"
"github.com/EDDYCJY/go-gin-example/pkg/setting"
)
func GetPage(c *gin.Context) int {
result := 0
page, _ := com.StrTo(c.Query("page")).Int()
if page > 0 {
result = (page - 1) * setting.PageSize
}
return result
}
написать модели инициализации
Вытащитьgorm
Пакеты зависимостей следующие:
go get -u github.com/jinzhu/gorm
Вытащитьmysql
Зависимости драйвера следующие:
go get -u github.com/go-sql-driver/mysql
После завершения, вgo-gin-example
изmodels
новый каталогmodels.go
, заmodels
инициализация с использованием
package models
import (
"log"
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"github.com/EDDYCJY/go-gin-example/pkg/setting"
)
var db *gorm.DB
type Model struct {
ID int `gorm:"primary_key" json:"id"`
CreatedOn int `json:"created_on"`
ModifiedOn int `json:"modified_on"`
}
func init() {
var (
err error
dbType, dbName, user, password, host, tablePrefix string
)
sec, err := setting.Cfg.GetSection("database")
if err != nil {
log.Fatal(2, "Fail to get section 'database': %v", err)
}
dbType = sec.Key("TYPE").String()
dbName = sec.Key("NAME").String()
user = sec.Key("USER").String()
password = sec.Key("PASSWORD").String()
host = sec.Key("HOST").String()
tablePrefix = sec.Key("TABLE_PREFIX").String()
db, err = gorm.Open(dbType, fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local",
user,
password,
host,
dbName))
if err != nil {
log.Println(err)
}
gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string {
return tablePrefix + defaultTableName;
}
db.SingularTable(true)
db.LogMode(true)
db.DB().SetMaxIdleConns(10)
db.DB().SetMaxOpenConns(100)
}
func CloseDB() {
defer db.Close()
}
Запись файлов запуска и маршрутизации проекта
Основные приготовления сделаны, приступаем к написанию демо!
Написать демо
существуетgo-gin-example
Под установленнымmain.go
как файл запуска (т.е.main
пакет), мы сначала пишемDemo, чтобы всем было понятно, напишите содержимое файла:
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/EDDYCJY/go-gin-example/pkg/setting"
)
func main() {
router := gin.Default()
router.GET("/test", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "test",
})
})
s := &http.Server{
Addr: fmt.Sprintf(":%d", setting.HTTPPort),
Handler: router,
ReadTimeout: setting.ReadTimeout,
WriteTimeout: setting.WriteTimeout,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
}
воплощать в жизньgo run main.go
, посмотрите, отображается ли в командной строке
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /test --> main.main.func1 (3 handlers)
Выполнить этот блокcurl 127.0.0.1:8000/test
Проверьте, чтобы вернуться{"message":"test"}
.
Точка знаний
Итак, давайте расширим очки знаний, задействованные в Demo!
стандартная библиотека
- fmt: Реализует форматированный ввод-вывод, аналогичный printf и scanf языка C. Действие форматирования («глагол») получено из языка C, но проще
- net/http: Обеспечивает реализацию HTTP-клиента и сервера.
Gin
-
gin.Default(): возвращает Джин
type Engine struct{...}
, который содержитRouterGroup
, что эквивалентно созданию маршрутаHandlers
, вы можете привязывать различные правила и функции маршрутизации, промежуточное ПО и т. д. -
router.GET(...){...}: создание различных HTTP-методов для привязки.
Handlers
Он также поддерживает распространенные методы Restful, такие как POST, PUT, DELETE, PATCH, OPTIONS, HEAD и т. д. -
gin.H{...}: только один
map[string]interface{}
-
gin.Context:
Context
даgin
контекст в , что позволяет нам передавать переменные между промежуточным ПО, управлять потоками, проверять запросы JSON, отвечать на запросы JSON и т. д. вgin
содержит многоContext
методы, такие как наши обычно используемыеDefaultQuery
,Query
,DefaultPostForm
,PostForm
и т.д
&http.Server и ListenAndServe?
1. http.Сервер:
type Server struct {
Addr string
Handler Handler
TLSConfig *tls.Config
ReadTimeout time.Duration
ReadHeaderTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int
ConnState func(net.Conn, ConnState)
ErrorLog *log.Logger
}
- Addr: TCP-адрес прослушивания, формат
:8000
- Обработчик: HTTP-дескриптор, по сути
ServeHTTP
, чтобы обработчик ответил на HTTP-запрос - TLSConfig: конфигурация безопасности транспортного уровня (TLS)
- ReadTimeout: максимальное время, разрешенное для чтения
- ReadHeaderTimeout: максимальное время, разрешенное для чтения заголовков запроса.
- WriteTimeout: максимальное время, разрешенное для записи
- IdleTimeout: максимальное время ожидания
- MaxHeaderBytes: максимальное количество байтов в заголовке запроса.
- ConnState: укажите необязательную функцию обратного вызова, которая будет вызываться при изменении подключения клиента.
- ErrorLog: указывает необязательный регистратор для получения информации о неожиданном поведении программы и базовых системных ошибках;
nil
По умолчанию делается стандартным логгером пакета логов (то есть вывод в консоль)
2. Слушайте и обслуживайте:
func (srv *Server) ListenAndServe() error {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
Запустите службу прослушивания, прослушивая сетевой адрес TCP, адрес и вызывая приложение для обработки запросов при подключении.
Мы видим в исходном кодеAddr
это позвать нас&http.Server
Параметры задаются в , поэтому мы используем&
, мы хотим изменить значение параметра, потому что мыListenAndServe
и некоторые другие методы должны быть использованы&http.Server
параметры, они влияют друг на друга.
3.http.ListenAndServe
иСерийныйизr.Run()
Есть ли разница?
Посмотримr.Run
Реализация:
func (engine *Engine) Run(addr ...string) (err error) {
defer func() { debugPrintError(err) }()
address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address)
err = http.ListenAndServe(address, engine)
return
}
Анализируя исходный код, мы знаемПо сути никакой разницы, а также узнал, что стартапgin
Здесь выводится отладочная информация прослушивания.
4. Почему в ДемоWARNING
?
Сначала мы можем видетьDefault()
реализация
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
Вы можете видеть, что по умолчанию экземпляр движка промежуточного программного обеспечения журнала и восстановления подключен. и вызывается в началеdebugPrintWARNINGDefault()
, и его реализация заключается в выводе строки журнала
func debugPrintWARNINGDefault() {
debugPrint(`[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
`)
}
и другойRunning in "debug" mode. Switch to "release" mode in production.
, является причиной режима работы, понять не сложно, он уже находится под управлением конфигурационного файла :-), и эксплуатационный и обслуживающий персонал может модифицировать его конфигурацию в любой момент.
5. Демоrouter.GET
и другие правила маршрутизации не могут быть записаны вmain
в сумке?
мы обнаруживаемrouter.GET
и другие правила маршрутизации, которые прописаны в демоmain
В упаковке чувствуется очень странно, давайте избавимся от этой части логики!
существуетgo-gin-example
Внизrouters
новый каталогrouter.go
файл, напишите содержимое:
package routers
import (
"github.com/gin-gonic/gin"
"github.com/EDDYCJY/go-gin-example/pkg/setting"
)
func InitRouter() *gin.Engine {
r := gin.New()
r.Use(gin.Logger())
r.Use(gin.Recovery())
gin.SetMode(setting.RunMode)
r.GET("/test", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "test",
})
})
return r
}
Исправлятьmain.go
содержимое файла:
package main
import (
"fmt"
"net/http"
"github.com/EDDYCJY/go-gin-example/routers"
"github.com/EDDYCJY/go-gin-example/pkg/setting"
)
func main() {
router := routers.InitRouter()
s := &http.Server{
Addr: fmt.Sprintf(":%d", setting.HTTPPort),
Handler: router,
ReadTimeout: setting.ReadTimeout,
WriteTimeout: setting.WriteTimeout,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
}
Текущая структура каталогов:
go-gin-example/
├── conf
│ └── app.ini
├── main.go
├── middleware
├── models
│ └── models.go
├── pkg
│ ├── e
│ │ ├── code.go
│ │ └── msg.go
│ ├── setting
│ │ └── setting.go
│ └── util
│ └── pagination.go
├── routers
│ └── router.go
├── runtime
Перезапустите службу, выполнитеcurl 127.0.0.1:8000/test
Проверьте, правильно ли он возвращается.
В следующем разделе мы будем использовать нашу демонстрацию в качестве отправной точки для модификации и начала кодирования!
Ссылаться на
Пример кода для этой серии
?
Если у вас есть какие-либо вопросы или ошибки, добро пожаловать вissuesЗадавайте вопросы или вносите исправления, если вам нравится или вам помогают, добро пожаловатьStar, является своеобразным поощрением и продвижением автора.
мой блог
Изучите го с жареной рыбой:GitHub.com/Vicious Genetics/Нет...