• 创业进行时之用户注册


    Go语言实战 - 创业进行时之用户注册

     

    用户注册、登录和注销是任何一个网站都必然会有的功能,可以说,这是重新造轮子做多的领域,每个做网站的人应该都做过很多遍。见微知著,从这么一个小功能其实就可以看到所使用的web框架中的大部分东西。

    今天就让我们用这个基本模块来看看revel吧。

    先整理一下我们选用的技术框架和组件:

    web框架:revel 

    数据库:mongodb

    数据库driver:mgo

    工欲善其事,必先利其器,这里着重推荐一个mongodb的GUI客户端 - mongovue,可以说,如果没有这个工具,在开发的过程中我们会痛苦许多许多。

    这里假设你已经有了对Go语言最基本的知识,已经配置好GOROOT和GOPATH。

    首先,在GOPATH下面运行下面的命令安装revel,并且把revel的工具编译出来。

    go get github.com/robfig/revel

    go build –o bin/revel.exe github.com/robfig/revel/revel

    完成之后去GOPATHin下面看看是否已经编译出来了revel.exe。为了方便使用,我把GOPATHin添加到了环境变量PATH中。

    到你希望存放工程文件的地方运行

    revel new myapp

    整个工程的框架就建立好了,看下面的文件夹结构就可以看出,revel是一个MVC框架。

    image

    此时整个工程就可以运行了,运行下面的命令行启动站点。

    revel run myapp

    打开浏览器 http://127.0.0.1:9000,就可以看到下面的结果

    image

    内部的细节暂时不多说,来吧,先让用户可以注册。注意,在整个开发过程中大部分时候不需要重新启动revel

    1. 准备Model

     

    按照MVC的开发节奏,我们先准备model。在app目录下新建一个models目录,然后在里面新建entity.go(这个文件的命名大家可自便),打开entity.go加入User的实体定义。

    type User struct { 
      Email    string 
      Nickname string 
      Password []byte 
    }

    type MockUser struct { 
      Email           string 
      Nickname        string 
      Password        string 
      ConfirmPassword string 
    }

    为什么定义MockUser呢?原因后面会提到。

    现在写dal(数据访问层),在appmodels目录下新建dal.go。dal的写法其实可以用revel的插件机制,这里为了避免一下子引入太多概念,先用这种简单的方式。

    package models

    import ( 
      "github.com/robfig/revel" 
      "labix.org/v2/mgo" 
    )

    const ( 
      DbName                         = "myapp" 
      UserCollection                 = "user" 
    )

    type Dal struct { 
      session *mgo.Session 
    }

    func NewDal() (*Dal, error) { 
      revel.Config.SetSection("db") 
      ip, found := revel.Config.String("ip") 
      if !found { 
        revel.ERROR.Fatal("Cannot load database ip from app.conf") 
      }

      session, err := mgo.Dial(ip) 
      if err != nil { 
        return nil, err 
      }

      return &Dal{session}, nil 
    }

    func (d *Dal) Close() { 
      d.session.Close() 

    revel已经提供了配置系统,打开confapp.conf,添加下面内容

    [db] 
    ip = 127.0.0.1

    现在实现注册需要用到的方法,在appmodels目录下添加文件dal_account.go,代码如下。

    func (d *Dal) RegisterUser(mu *MockUser) error { 
      uc := d.session.DB(DbName).C(UserCollection)

      //先检查email和nickname是否已经被使用 
      i, _ := uc.Find(M{"nickname": mu.Nickname}).Count() 
      if i != 0 { 
        return errors.New("用户昵称已经被使用") 
      }

      i, _ = uc.Find(M{"email": mu.Email}).Count() 
      if i != 0 { 
        return errors.New("邮件地址已经被使用") 
      }

      var u User 
      u.Email = mu.Email 
      u.Nickname = mu.Nickname 
      u.Password, _ = bcrypt.GenerateFromPassword([]byte(mu.Password), bcrypt.DefaultCost)

      err := uc.Insert(u)

      return err 
    }

    看出来MockUser存在的意义了吗?用户在页面上填写的是明文的密码,这可不能直接存入数据库,需要先加密,这里用到了"code.google.com/p/go.crypto/bcrypt"这个库。

    2. 准备Controller

    准备controller,在appcontrollers新建一个文件account.go,在里面实现Account控制器,代码如下。

    package controllers

    import ( 
      "github.com/robfig/revel" 
      "myapp/app/models" 
    )

    type Account struct { 
      *revel.Controller 
    }

    func (c *Account) Register() revel.Result { 
      return c.Render() 
    }

    func (c *Account) PostRegister(user *models.MockUser) revel.Result { 
      return c.Render() 
    }

    3. 添加Route

    准备route,打开conf outes,添加Register的URL映射。

    # Routes 
    # This file defines all application routes (Higher priority routes first) 
    # ~~~~

    module:testrunner

    GET     /                                          App.Index

    GET     /register                                Account.Register

    POST   /register                                Account.PostRegister

    # Ignore favicon requests 
    GET     /favicon.ico                           404

    # Map static resources from the /app/public folder to the /public path 
    GET     /public/*filepath                     Static.Serve("public")

    # Catch all 
    *       /:controller/:action                   :controller.:action

    假定大家都知道Restful是啥意思,这里就是把两个url映射到了Controller的两个Action。

    可以看到,这里定义了所有的URL到Controller之间的映射,很方便。这个文件在运行前会被revel转换成app outes outes.go文件参与编译。后面在讲到ReverseRedirect的时候需要用到这个文件里的内容。

    4. 准备View

    准备view,在appviews下面新建文件Register.html,关键内容如下

    <form action="{{url "Account.PostRegister"}}" method="POST"> 
        {{with $field := field "user.Email" .}} 
        <div class="control-group {{$field.ErrorClass}}"> 
          <label class="control-label" for="{{$field.Id}}">电子邮件</label> 
          <div class="controls"> 
            <input type="email" id="{{$field.Id}}" name="{{$field.Name}}" value="{{$field.Flash}}" required> 
            {{if $field.Error}} 
            <span class="help-inline">{{$field.Error}}</span> 
            {{end}} 
          </div> 
        </div> 
        {{end}} 
        …

    一点一点解释一下上面蓝色部分关键字的含义。

    url是revel提供的一个template function,可以很方便的把Controller的Action变成与之相对的url,它的运作原理实际上就是去刚才定义好的routes映射里面查找。

    field是revel提供的一个template function,专门方便生成form,还记得PostRegister方法的签名吗?

    func (c *Account) PostRegister(user *models.MockUser) revel.Result

    它接受一个名为user的*models.User类型的参数,所以,使用{{with $field := field “user.Email”}}就可以通知revel将form的参数封装到user结构中再传递给PostRegister。

    我们都知道用户注册的时候填写的值是需要做有效性检验的,当用户填写的值不符合标准时需要出现错误提示,通常来说会是下面这样

    image

    $field.ErrorClass的作用就是当这个参数出现错误的时候可以方便的通过添加class的方式在页面上显示错误状态。ErrorClass的值可以通过下面的代码修改。

    revel.ERROR_CLASS = "error" 

    $field.Id和$field.Name就不解释了,大家待会儿打开浏览器中看看生成的源代码就明白了。

    $field.Flash这里就需要解释一下Flash的概念了。

    Flash是一个字典,适用于在两个Request中间传递数据,数据存储在cookie中。

    大家都知道,HTTP协议本身是无状态的,所以,考虑一下这个用例,用户在注册的时候输入了一个无效的email地址,点击注册之后页面刷新了一下,“电子邮件”下面出现一行红字“你输入的Email地址无效”,此刻文本框里面需要出现上次用户输入的值。那么,$field.Flash就是在Flash里去找以$field.Name为Key的值。

    $field.Error就是在Flash里找以$field.Name_error为Key的值,也就是上图中红色的“密码必须大于等于6位”这个错误信息。

    好了,现在大家就按照这个节奏在view中添加“昵称”,“密码”和“确认密码”吧。

    添加完成之后就去访问http://127.0.0.1/register看看吧。是不是这样呢?

    image

    revel会通过Controller.Action的名称去查找同名的view文件,例如,Register方法对应的就是Register.html。这里需要注意的一点是,revel是通过反射去查找Controller.Render方法的调用者,而且只向上查找一层。

    例如,下面这段代码是不能工作的。

    func (c *Account) someMethod() revel.Result { 
      ... 
      return c.Render() 
    }

    func (c *Account) Register() revel.Result { 
      return c.someMethod() 
    }

     

    5. 实现Controller

    现在让我们为PostRegister添加处理注册的逻辑。

    首先,验证参数的有效性。

    func (c *Account) PostRegister(user *models.MockUser) revel.Result { 
      c.Validation.Required(user) 
      c.Validation.Email(user.Email) 
      c.Validation.Required(user.Nickname) 
      c.Validation.Required(user.Password) 
      c.Validation.Required(user.ConfirmPassword == user.Password)

      if c.Validation.HasErrors() { 
        c.FlashParams() 
        return c.Redirect((*Account).Register) 
      }

      return c.Render() 
    }

    revel提供了挺好用的Validation机制,上面的代码应该不需要太多解释,只有一行

    c.FlashParams()

    它的作用就是把form提交的参数原样存入Flash中,还记得刚才的$field.Flash吗?

    现在去玩玩注册页面吧,填写一些错误的值看看反应吧,嗯,你应该很快就会发现,错误信息虽然已经显示出来,但可惜却是英文的,修改一下吧。

    func (c *Account) PostRegister(user *models.MockUser) revel.Result { 
      c.Validation.Email(user.Email).Message("电子邮件格式无效") 
      c.Validation.Required(user.Nickname).Message("用户昵称不能为空") 
      c.Validation.Required(user.Password).Message("密码不能为空") 
      c.Validation.Required(user.ConfirmPassword == user.Password).Message("两次输入的密码不一致")

      if c.Validation.HasErrors() { 
        c.FlashParams() 
        return c.Redirect((*Account).Register) 
      }

      return c.Render() 
    }

    Validation提供了好几个常用的验证方法,大家可以自己看看,应该是简单易懂的。

    继续,当所有参数检查都通过之后,就调用dal.RegisterUser方法将用户信息存入数据库。

    func (c *Account) PostRegister(user *models.MockUser) revel.Result { 
      c.Validation.Email(user.Email).Message("电子邮件格式无效") 
      c.Validation.Required(user.Nickname).Message("用户昵称不能为空") 
      c.Validation.Required(user.Password).Message("密码不能为空") 
      c.Validation.Required(user.ConfirmPassword == user.Password).Message("两次输入的密码不一致")

      if c.Validation.HasErrors() { 
        c.FlashParams() 
        return c.Redirect((*Account).Register) 
      }

      dal, err := models.NewDal() 
      if err != nil { 
        c.Response.Status = 500 
        return c.RenderError(err) 
      } 
      defer dal.Close()

      err = dal.RegisterUser(user) 
      if err != nil { 
        c.Flash.Error(err.Error()) 
        return c.Redirect((*Account).Register) 
      }

      return c.Redirect((*Account).RegisterSuccessful) 
    }

    func (c *Account) RegisterSuccessful() revel.Result { 
      return c.Render() 
    }

    我增加了一个方法RegisterSuccessful,用于显示注册成功,大家别忘了在routes和view中添加相应的东西。

    至此,用户注册已经完成。不知道大家注意到没有,就算修改go代码,依然不需要重新启动revel,直接刷新浏览器页面就会发现新的代码已经自动编译并且启用了。

    这就是revel.exe存在的一个意义,它会监视整个工程的文件修改情况,然后自动编译和运行。开发的时候感觉就像在使用python和ruby一样舒服。

    今天已经提到了revel的不少知识点了,希望对大家有用,欢迎任何问题!

    最后提一句,我们的创业产品 山坡网 就是完全用revel写的,这是一个经得住考验的框架。

     
     
  • 相关阅读:
    c# 把对象加入队列,对象为全局变量,对象改变队列值也跟着改变
    C# 一个数组未赋值引发的错误
    c# 2016QQ自动登录程序
    当时钟事件声明为过程变量 让system.threading.timer时钟失效
    if 循环的深入理解 哈希表的一种应用
    VB6对象与地址相互转换
    VB6的函数指针传递
    .net framework 4.0 从 GAC 卸载 程序集
    .net framework 4.0 从 GAC 卸载 程序集
    GAC in CLR 3.0
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3167560.html
Copyright © 2020-2023  润新知