分享
  1. 首页
  2. 文章

软件技术-零基础-Golang用MongoDB验证用户信息

zhyuzh3d · · 1400 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

欢迎关注我的专栏( つ•̀ω•́)つ【人工智能通识】


如何避免用户重复注册?如何验证用户登录成功?

上一篇文章软件技术-零基础-Golang存储注册信息

检查重复邮箱

如果用户的邮箱已经存在于MongoDB数据库中了,那么我们应该不要重复写入数据,并且告诉用户您已经注册过了

用下面的代码检测用户邮箱是否已经存在,修改register.goHandleFunc部分:

 //访问数据集
 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 dbc := tools.MongoDBCLient.Database("myweb").Collection("user")
 defer cancel()
 //验证用户邮箱是否存在
 count, err := dbc.CountDocuments(context.TODO(), bson.M{"Email": ds.Email})
 if err != nil {
 resp = RespDS{Err: 1, Msg: "读取数据库失败。"}
 }
 if count >1 {
 resp = RespDS{Err: 1, Msg: "邮箱已经存在。"}
 }
 //写入数据库
 res, err := dbc.InsertOne(ctx, bson.M{"Email": ds.Email, "Pw": ds.Pw})
 if err != nil {
 resp = RespDS{Err: 1, Msg: "写入数据库失败."}
 }
 resp.Data.Token = res.InsertedID.(primitive.ObjectID).Hex()

注意这几个地方:

  • CountDocuments是查找存在几个符合条件的,如果存在超过0个就表示已经至少有一个重名邮箱了。
  • bson.M{"Email": ds.Email}我们是搜索符合这个条件的用户,也就是重名用户。

如果这时候保存并切换到app.go运行代码,从网页上提交注册,仍然会导致重复写入数据库,这是因为我们并没有阻止下面的代码执行。

仔细回顾我们的register.go文件的HandleFunc方法的流程:

三次检查,不管检查是否通过,都不会停止。而我们知道,任何一次检查不通过,都应该直接向用户返回结果,完全没必要再做后面的检查了。

所以我们期望的是这样的流程(注意橙色的捷径):


实用功能util.go

我们来改进整个注册流程。
流程HanleFunc里最后负责返回响应数据的是这两句:

 dt, _ := json.Marshal(resp)
 w.Write([]byte(string(dt)))

虽然只有两句,但是因为以后也会很经常的使用,所以很不方便。我们采用类似tools.go的办法把这些常用的方法提炼到一个新的/src/app/util/util.go(utility实用功能)中去,作为一个单独的WWrite(WebWrite)函数:

package util
import (
 "app/uds"
 "encoding/json"
 "net/http"
)
//WWrite 向用户返回信息,返回resp和一个错误信息
func WWrite(w http.ResponseWriter, code int, msg string, data interface{}) (uds.Respons, error) {
 resp := uds.Respons{Code: code, Msg: msg, Data: data}
 var err error
 dt, err1 := json.Marshal(resp)
 if err1 != nil {
 err = err1
 }
 _, err2 := w.Write([]byte(string(dt)))
 if err2 != nil {
 err = err2
 }
 return resp, err
}

注意这几个地方:

  • 我们调用了统一数据格式app/uds
  • 这个WWrite我们使用了多个参数(w http.ResponseWriter, code int, msg string, data interface{}),也返回了多个(2个)值(uds.Respons, error),这和最后的return resp, err是一致的。
  • 我们用这些参数拼合成了一个标准的返回给用户的数据格式resp := uds.Respons{Code: code, Msg: msg, Data: data}

这个uds.Respons是什么样的?下面是完整的uds.go:

package uds
import "go.mongodb.org/mongo-driver/mongo"
//Tools 定义返回对象
type Tools struct {
 MongoDBCLient *mongo.Client
}
//Respons 定义统一的返回格式
type Respons struct {
 Code int
 Msg string
 Data interface{}
}

util.gotool.go有什么区别?都是提供通用工具啊。但我习惯把简单的、不用初始化的轻量工具放在util.go里面,而把那些复杂的需要初始化InitRun的工具放在tool.go里面,以后工具多了,其实还需要进步一细分处理。这只是对功能的分割规划,并非是必须的。
应该控制每个代码文件不要太多,三五百行还能忍一下看懂,两三千行代码看到就头晕了,以后改动和维护也会很烦。

改进register.go

有了util.go实用功能我们就可以方便的调用它了,不仅可以替换掉register.go中最后两句,而且可以把数据结构RespDS和很多resp = RespDS{Err: 1, Msg: "读取数据库失败。"}类似的语句简化掉,因为WWrite可以自动组装成标准的Respons结构并返回。

下面是修改后的完整register.go:

package register
import (
 "app/uds"
 "app/util"
 "context"
 "encoding/json"
 "net/http"
 "regexp"
 "time"
 "go.mongodb.org/mongo-driver/bson"
 "go.mongodb.org/mongo-driver/bson/primitive"
)
var tools = uds.Tools{}
//InitRun 初始化tools
func InitRun(t uds.Tools) {
 tools = t
}
//ReqDS 注册接口的请求数据格式
type ReqDS struct {
 Email string `bson:"Email"`
 Pw string
}
//HandleFunc 注册接口处理函数
func HandleFunc(w http.ResponseWriter, r *http.Request) {
 ds := ReqDS{}
 json.NewDecoder(r.Body).Decode(&ds)
 mailRe, _ := regexp.Compile(`^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`)
 pwRe, _ := regexp.Compile(`^[0-9a-zA-Z_@]{6,18}$`)
 if !pwRe.MatchString(ds.Pw) {
 util.WWrite(w, 1, "密码格式错误。", nil)
 return
 }
 if !mailRe.MatchString(ds.Email) {
 util.WWrite(w, 1, "邮箱格式错误。", nil)
 return
 }
 //访问数据集
 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 dbc := tools.MongoDBCLient.Database("myweb").Collection("user")
 defer cancel()
 //验证用户邮箱是否存在
 count, err := dbc.CountDocuments(context.TODO(), bson.M{"Email": ds.Email})
 if err != nil {
 util.WWrite(w, 1, "读取数据库失败。", nil)
 return
 }
 if count > 0 {
 util.WWrite(w, 1, "邮箱已存在。", nil)
 return
 }
 //写入数据库
 res, err := dbc.InsertOne(ctx, bson.M{"Email": ds.Email, "Pw": ds.Pw})
 if err != nil {
 util.WWrite(w, 1, "写入数据库失败。", nil)
 return
 }
 d := res.InsertedID.(primitive.ObjectID).Hex()
 util.WWrite(w, 0, "注册成功。", d)
 return
}

注意这里有很多的return,它表示快速结束当前函数HandleFunc,后面的代码不会被运行。
另外这里也做了很多if err != nil {错误检测,避免在出现异常时候没有反应。
这个代码虽然感觉变多了,但实际运行的顺序却更合理了,如果用户输错了邮箱格式,其实只会运行到util.WWrite(w, 1, "邮箱格式错误。", nil)这一行,后面一大片都不会被运行,所以速度快了很多。

代码多未必运行的更慢,反之亦然

更多扩展内容:

  • 如果我们需要把FindOne得到用户信息提取出来,比如说想知道这个邮箱的密码是什么,可以创建一个var u bson.M对象,然后把读取的信息填充进去dbc.FindOne(context.TODO(), bson.M{"Email": ds.Email}).Decode(&b)然后就可以使用b["Pw"]来读取了:
 ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
 var result bson.M
 err := collection.FindOne(ctx, bson.M{"Name": "a"}).Decode(&result)
 if err != nil {
 log.Fatal(err)
 }
  • 可以使用Find来查找不限数量的符合条件的数据,如果条件也不限制可以使用空的bson.M来做参数:cur, err := collection.Find(ctx, bson.M{}),但如果要使用它读取到的内容就麻烦一些:
 ctx, _ = context.WithTimeout(context.Background(), 30*time.Second)
 cur, err := collection.Find(ctx, bson.M{"Name": "a"})
 if err != nil {
 log.Fatal(err)
 }
 defer cur.Close(ctx)
 for cur.Next(ctx) {
 var result bson.M
 err := cur.Decode(&result)
 if err != nil {
 log.Fatal(err)
 }
 fmt.Println(result, "-->", result["Value"])
 }
  • 如果我们只是想知道有多少条符合要求的信息,那么我们可以使用CountDocuments方法:
 ctx, _ = context.WithTimeout(context.Background(), 30*time.Second)
 c, _ := collection.CountDocuments(ctx, bson.M{})
 fmt.Println("Count", c)

到这里为止,我们还是在做注册功能(虽然我们的网页叫login.html。。。),下一篇我们正式做登录,从数据库来检查当前用户是否输对了邮箱和密码。


欢迎关注我的专栏( つ•̀ω•́)つ【人工智能通识】


每个人的智能新时代

如果您发现文章错误,请不吝留言指正;
如果您觉得有用,请点喜欢;
如果您觉得很有用,欢迎转载~


END


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:zhyuzh3d

查看原文:软件技术-零基础-Golang用MongoDB验证用户信息

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1400 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏