美文网首页
Go实战项目【二】搭建项目的基础

Go实战项目【二】搭建项目的基础

作者: Leo丶Dicaprio | 来源:发表于2019-11-16 16:08 被阅读0次

    项目配置

    conf文件夹下创建app.ini文件,这个是整个项目的配置文件
    conf/app.ini

    #app相关
    [app]
    PageSize = 20                           #分页每页返回的size
    JwtSecret = 1111$1111                #JWT密钥
    JwtExpireTime = 100                     #jwt失效时间,单位是小时
    
    RuntimeRootPath = runtime/              #保存文件的跟路径
    
    ImagePrefixUrl = http://127.0.0.1:9999  #图片url
    ImageSavePath = upload/images/          #图片路径
    ImageMaxSize = 1                        #图片最大尺寸,单位(M)
    ImageAllowExts = .jpg,.jpeg,.png        #图片允许的格式
    
    ApkSavePath = upload/apks/              #apk文件路径
    ApkAllowExt = .apk                      #apk文件格式
    #iOS应用在App Store中的地址,用于版本更新
    AppStoreUrl = null
    
    LogSavePath = logs/                     #日志文件路径
    LogSaveName = log                       #日志文件名称
    LogFileExt = log                        #日志文件后缀
    TimeFormat = 20060102                   #时间相关
    
    WechatAppID = null       #微信的appID
    WechatSecret = null #微信的secret
    QQAppID =  null                           #QQ的appID
    QQAppKey =  null   #QQ的appkey
    
    #服务器相关
    [server]
    RunMode = debug                         #debug or release
    HttpPort = 9999                         #端口号
    ReadTimeout = 60                        #读取超时时间
    WriteTimeout = 60                       #写入超时时间
    
    #数据库mysql相关
    [database]
    Type = mysql                            #数据库类型
    User = root                             #数据库账号
    Password = 123456                     #数据库密码
    Host = 127.0.0.1:3306                   #数据库地址+端口号
    Name = api                            #数据库名称
    TablePrefix = api_                    #数据库表名称前缀
    

    每个配置字段的含义都有注释,解释下几个字段,
    JwtSecret:json web token加密的密钥,需要客户端和服务端保持一致
    ImagePrefixUrl:项目中返还给客户端图片的域名,
    AppStoreUrl:项目在AppStore中的地址,用做更新使用

    其它例如数据库信息,根据自己的情况自行设置

    安装ini配置包
    终端执行

    go get -u github.com/go-ini/ini
    

    配置文件已经有了,如何使用到配置文件设置的值呢?这里使用了映射结构体的方式,来获取配置文件中设置的值。

    首先创建pkg/setting/setting.go文件,编写结构体,结构体与配置文件的值一一对应
    pkg/setting/setting.go

    package setting
    
    import "time"
    
    //app配置
    type App struct {
        JwtSecret string                //jwt密钥
        JwtExpireTime time.Duration     //jwt失效时间,单位是小时
        PageSize int                    //分页返回的数据个数
        RuntimeRootPath string          //保存文件的跟路径
    
        ImagePrefixUrl string           //图片url
        ImageSavePath string            //图片要保存的路径
        ImageMaxSize int                //图片最大尺寸
        ImageAllowExts []string         //图片允许的格式   jpg jpeg png
    
        ApkSavePath string              //apk文件路径
        ApkAllowExt string              //apk文件格式
        AppStoreUrl string              //iOS应用在App Store中的地址,用于版本更新
    
        LogSavePath string              //日志文件保存的路径
        LogSaveName string              //日志文件名称
        LogFileExt string               //日志文件后缀
        TimeFormat string               //文件的日期名称
    
        WechatAppID string              //微信的appID
        WechatSecret string             //微信的secret
        QQAppID string                  //QQ的appID
        QQAppKey string                 //QQ的appkey
    }
    var AppSetting = &App{}
    
    //服务配置
    type Server struct {
        RunMode string              //运行模式
        HttpPort int                //端口号
        ReadTimeout time.Duration   //读取超时时间
        WriteTimeout time.Duration  //写入超时时间
    }
    var ServerSetting = &Server{}
    
    //数据库配置
    type Database struct {
        Type string                 //数据库类型
        User string                 //数据库用户
        Password string             //数据库密码
        Host string                 //数据库地址+端口号
        Name string                 //数据库名称
        TablePrefix string          //数据库数据表前缀
    }
    var DatabaseSetting = &Database{}
    

    然后创建一个初始化的方法,方法的作用是把配置文件映射到上面的结构体当中,注意这个方法不能用init方法,因为要保证程序启动后首先要加载配置文件。避免多个包下init方法的执行顺序这种情况。
    pkg/setting/setting.go

    ...  //为了节省文章篇幅,以后将会用这种方式来标示已写过的代码
    //将配置选项映射到结构体上
    func SetUp()  {
        Cfg,err := ini.Load("conf/app.ini") //加载配置文件ini
        if err != nil {
            log.Fatal("获取.ini配置失败")
        }
    
        //映射配置
        err = Cfg.Section("app").MapTo(AppSetting)
        if err != nil {
            log.Fatalf("Cfg配置文件映射 AppSetting 错误: %v", err)
        }
    
        AppSetting.ImageMaxSize = AppSetting.ImageMaxSize * 1024 * 1024     //设置允许上传图片的最大尺寸
    
        err = Cfg.Section("server").MapTo(ServerSetting)
        if err != nil {
            log.Fatalf("Cfg配置文件映射 ServerSetting 错误: %v", err)
        }
    
        ServerSetting.ReadTimeout = ServerSetting.ReadTimeout * time.Second
        ServerSetting.WriteTimeout = ServerSetting.ReadTimeout * time.Second
    
        err = Cfg.Section("database").MapTo(DatabaseSetting)
        if err != nil {
            log.Fatalf("Cfg配置文件映射 DatabaseSetting 错误: %v", err)
        }
    }
    

    接下来就是如何使用,在项目根目录下创建main.go文件,作为项目的入口,在main方法中调用SetUp方法
    (可以把上篇文章创建的test.go文件删除了)
    main.go

    package main
    
    import (
        "api/pkg/setting"
        "log"
    )
    
    func main() {
        log.Println("Hello, api 正在启动中...")
        setting.SetUp() //初始化配置文件
        log.Println(setting.ServerSetting.HttpPort) //测试能否打印出ini配置文件设置的信息
    }
    

    然后终端执行go run main.go,看下能否正常打印出信息。
    以后有需要再添加的配置信息,就按照这种方法新增就可以了。

    编写自定义api接口的返回码和信息

    每个api接口返回的是json数据,定义的json格式如下:

    {
        "Code":200,
        "Msg":"Message",
        "Data":Object
    }
    

    Code:api接口返回的状态,200代表正常,此外还有其它状态,我们要编写的就是这个
    Msg:状态码所对应的消息
    Data:返回的数据就在这里,这个值的类型不是固定的。

    pkg/e/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         //token无效
        ERROR_AUTH_CHECK_TOKEN_TIMEOUT = 20002      //token超时
        ERROR_AUTH_TOKEN = 20003                    //taoken错误
        ERROR_AUTH = 20004                          //无效的用户
        ERROR_EXIST_AUTH = 20005                    //手机号已存在
        ERROR_AUTH_PASSWORD = 20006                 //密码错误
    
        ERROR_UPLOAD_SAVE_IMAGE_FAIL = 30001        // 保存图片失败
        ERROR_UPLOAD_CHECK_IMAGE_FAIL = 30002       // 检查图片失败
        ERROR_UPLOAD_CHECK_IMAGE_FORMAT = 30003     // 校验图片错误,图片格式或大小有问题
    )
    

    同样,每个状态吗所对应的信息也告诉客户端,返回的值在json的Msg值中
    pkg/e/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 : "用户不存在",
        ERROR_EXIST_AUTH : "用户已存在",
        ERROR_AUTH_PASSWORD : "密码错误",
        ERROR_UPLOAD_SAVE_IMAGE_FAIL:"保存图片失败",
        ERROR_UPLOAD_CHECK_IMAGE_FAIL:"检查图片失败",
        ERROR_UPLOAD_CHECK_IMAGE_FORMAT :"校验图片错误,图片格式或大小有问题",
    }
    
    /*
       根据传入的编码。获取对应的编码消息
    */
    func GetMsg(code int)string  {
        msg,ok := MsgFlags[code]
        if ok {
            return msg
        }
        return MsgFlags[ERROR]
    }
    

    同样的,以后有需要新增状态,修改以上文件新增就可以了。

    启动服务和应用热更新

    准备工作完成一部分了,可以先把程序服务启动来。也是在main方法中进行
    main.go

    ...
    func main() {
        log.Println("Hello, api 正在启动中...")
        setting.SetUp() //初始化配置文件
    
        router := gin.Default()
    
        router.GET("/test", func(context *gin.Context) {
            context.JSON(e.SUCCESS,gin.H{
                "Code":e.SUCCESS,
                "Msg":e.GetMsg(e.SUCCESS),
                "Data":"返回数据成功",
            })
        })
    
        s := &http.Server{
            Addr:fmt.Sprintf(":%d", setting.ServerSetting.HttpPort),        //设置端口号
            Handler:router,                                         //http句柄,实质为ServeHTTP,用于处理程序响应HTTP请求
            ReadTimeout:setting.ServerSetting.ReadTimeout,          //允许读取的最大时间
            WriteTimeout:setting.ServerSetting.WriteTimeout,        //允许写入的最大时间
            MaxHeaderBytes: 1 << 20,                                //请求头的最大字节数
        }
    
        /*
           使用 http.Server - Shutdown() 优雅的关闭http服务
        */
        go func() {
            if err := s.ListenAndServe(); err != nil{
                log.Printf("Listen: %s\n", err)
            }
        }()
    
        quit := make(chan os.Signal)
        signal.Notify(quit,os.Interrupt)
        <- quit
    
        log.Println("Shutdown Server ...")
        ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
        defer cancel()
        if err := s.Shutdown(ctx); err != nil {
            log.Fatal("Server Shutdown:", err)
        }
    
        log.Println("程序服务关闭退出")
    }
    

    可以在浏览器输入http://127.0.0.1:9999/test查看数据是否正确

    image.png

    封装返回数据方法

    由于json的第一层格式是固定的,所以封装一下返回数据的方法,能更简单的调用。
    pkg/util/response.go

    package util
    
    import (
        "api/pkg/e"
        "github.com/gin-gonic/gin"
    )
    
    /*
       数据返回信息的model,格式如下
    */
    type Response struct {
        Code int            //自定义编码
        Msg string          //自定义消息
        Data interface{}    //返回的数据
    }
    
    func ResponseWithJson(code int, data interface{}, c *gin.Context) {
        c.JSON(200,&Response{
            Code:code,
            Msg:e.GetMsg(code),
            Data:data,
        })
    }
    

    以后会经常使用到这个方法,直接调用util.ResponseWithJson()方法即可。

    点关注,不迷路

    相关文章

      网友评论

          本文标题:Go实战项目【二】搭建项目的基础

          本文链接:https://www.haomeiwen.com/subject/tdpsictx.html