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

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

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

web框架:revel

数据库:mongodb

数据库driver:mgo

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

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

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

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

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

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

revel new myapp

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

1.jpg

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

revel run myapp

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

2.jpg

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

内部的细节暂时不多说,来吧,先让用户可以注册。注意,在整个开发过程中大部分时候不需要重新启动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(数据访问层),在app\models目录下新建dal.go。dal的写法其实可以用revel的插件机制,这里为了避免一下子引入太多概念,先用这种简单的方式。

package modelsimport (  "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已经提供了配置系统,打开conf\app.conf,添加下面内容

[db]ip = 127.0.0.1

现在实现注册需要用到的方法,在app\models目录下添加文件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,在app\controllers新建一个文件account.go,在里面实现Account控制器,代码如下。

package controllersimport (  "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\routes,添加Register的URL映射。

# Routes# This file defines all application routes (Higher priority routes first)# ~~~~module:testrunnerGET     /                                          App.IndexGET     /register                                Account.RegisterPOST   /register                                Account.PostRegister# Ignore favicon requestsGET     /favicon.ico                           404# Map static resources from the /app/public folder to the /public pathGET     /public/*filepath                     Static.Serve("public")# Catch all*       /:controller/:action                   :controller.:action

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

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

4、准备View

准备view,在app\views下面新建文件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。

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

3.jpg

$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看看吧。是不是这样呢?

4.jpg

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,直接刷新浏览器页面就会发现新的代码已经自动编译并且启用了。

更多go语言相关文章请关注go语言教程栏目。

更多相关文章

  1. golang io读取文件与判断文件是否存在的方法
  2. golang查找文件是否存在的方法
  3. golang中fscanf读取文件时把回车替换成0
  4. golang判断是否存在不存在就创建文件
  5. golang中.a文件是什么
  6. golang判断js文件是否存在
  7. golang判断文件是否存在的方法
  8. golang检查文件是否存在的方法
  9. golang 如何判断文件是否存在

随机推荐

  1. Android(安卓)BroadcastReceiver 简介
  2. Android local manifest
  3. android 设置Button为圆形按钮
  4. 不自动弹出键盘
  5. android中怎么在文字两边划线
  6. android布局实例代码
  7. QR Codes Made Easy In Android
  8. Android Interprocess Communication(一)
  9. Android manifest.xml 中元素含义
  10. Android Tip : think more about Android