到目前为止,我们的后端运行良好。我们可以创建用户和登录名,但是一旦我们重新启动服务器,该用户数据就会丢失,因为它仅在应用程序运行时保存在内存中。当然这是不现实的情况,即使服务器重新启动,我们也希望保存所有用户数据。为此,所有数据都将保存到数据库中。我们将使用的数据库是介绍中提到的 PostgreSQL。我们不会介绍 PostgreSQL 的安装,因为官方页面上有关于它的文档。对于接下来的步骤,我们将假设 PostgreSQL 已安装和配置。在所有示例中,我们将使用默认用户postgres和密码postgres

让我们首先连接到 PostgreSQL 以创建新数据库。如果 PostgreSQL 服务没有运行,你必须先启动它,然后连接postgres用户:

sudo service postgresql start
sudo -u postgres psql

进入全屏模式 退出全屏模式

连接后,创建新数据库:

CREATE DATABASE rgb;

进入全屏模式 退出全屏模式

对于数据库通信,我们将使用go-pg模块。您可以通过运行go get github.com/go-pg/pg/v10来安装它。这将安装go-pg模块的第 10 版,这是编写本指南时的最新版本。现在在其中创建新目录internal/database/和文件database.go

package database

import (
  "github.com/go-pg/pg/v10"
)

func NewDBOptions() *pg.Options {
  return &pg.Options{
    Addr:     "localhost:5432",
    Database: "rgb",
    User:     "postgres",
    Password: "postgres",
  }
}

进入全屏模式 退出全屏模式

这只会创建用于连接数据库的新选项。创建新文件internal/store/store.go,我们将在其中设置数据库连接以供我们的store包使用,这将是唯一与数据库通信的包。

package store

import (
  "log"

  "github.com/go-pg/pg/v10"
)

// Database connector
var db *pg.DB

func SetDBConnection(dbOpts *pg.Options) {
  if dbOpts == nil {
    log.Panicln("DB options can't be nil")
  } else {
    db = pg.Connect(dbOpts)
  }
}

func GetDBConnection() *pg.DB { return db }

进入全屏模式 退出全屏模式

这里我们有一个变量db,它将作为 store 包的数据库连接器。我们还有两个函数,一个是设置数据库连接器,一个是获取它。函数SetDBConnection()将立即使用,因为我们需要设置我们的数据库连接器。如前所述,只有 store 包将连接到数据库,并且db变量在整个包中可用。那么,我们需要GetDBConnection()做什么?好吧,我们实际上还不需要它,但我们将在下一节中需要它来运行迁移,因为这将在单独的小应用程序中发生,正如您很快就会看到的那样。现在,我们只通过添加第store.SetDBConnection(database.NewDBOptions())行在internal/server/server.go文件中设置数据库连接器:

package server

import (
  "rgb/internal/database"
  "rgb/internal/store"
)

func Start() {
  store.SetDBConnection(database.NewDBOptions())

  router := setRouter()

  // Start listening and serving requests
  router.Run(":8080")
}

进入全屏模式 退出全屏模式

有了这些,我们准备在internal/store/users.go文件中实现创建和验证用户的方法:

package store

import "errors"

type User struct {
  ID       int
  Username string `binding:"required,min=5,max=30"`
  Password string `binding:"required,min=7,max=32"`
}

func AddUser(user *User) error {
  _, err := db.Model(user).Returning("*").Insert()
  if err != nil {
    return err
  }
  return nil
}

func Authenticate(username, password string) (*User, error) {
  user := new(User)
  if err := db.Model(user).Where(
    "username = ?", username).Select(); err != nil {
    return nil, err
  }
  if password != user.Password {
    return nil, errors.New("Password not valid.")
  }
  return user, nil
}

进入全屏模式 退出全屏模式

上述函数将在internal/server/user.go中使用:

package server

import (
  "net/http"
  "rgb/internal/store"

  "github.com/gin-gonic/gin"
)

func signUp(ctx *gin.Context) {
  user := new(store.User)
  if err := ctx.Bind(user); err != nil {
    ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    return
  }
  if err := store.AddUser(user); err != nil {
    ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    return
  }
  ctx.JSON(http.StatusOK, gin.H{
    "msg": "Signed up successfully.",
    "jwt": "123456789",
  })
}

func signIn(ctx *gin.Context) {
  user := new(store.User)
  if err := ctx.Bind(user); err != nil {
    ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    return
  }
  user, err := store.Authenticate(user.Username, user.Password)
  if err != nil {
    ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Sign in failed."})
    return
  }

  ctx.JSON(http.StatusOK, gin.H{
    "msg": "Signed in successfully.",
    "jwt": "123456789",
  })
}

进入全屏模式 退出全屏模式

如果我们现在尝试创建新帐户,我们将收到错误消息。这是意料之中的,因为我们正在尝试将我们的数据添加到尚不存在的用户表中。我们将在下一节中通过使用迁移来解决这个问题。

Logo

PostgreSQL社区为您提供最前沿的新闻资讯和知识内容

更多推荐