用户注册、登录和注销是任何一个网站都必然会有的功能,可以说,这是重新造轮子做多的领域,每个做网站的人应该都做过很多遍。见微知著,从这么一个小功能其实就可以看到所使用的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

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

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

revel new myapp

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

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

revel run myapp

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

内部的细节暂时不多说,来吧,先让用户可以注册。注意,在整个开发过程中大部分时候不需要重新启动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 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已经提供了配置系统,打开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 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\routes,添加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\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。

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

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

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写的,这是一个经得住考验的框架。

Go语言实战 - revel框架教程之用户注册的更多相关文章

  1. Go语言实战 - revel框架教程之权限控制

    一个站点上面最基本都会有三种用户角色,未登录用户.已登录用户和管理员.这一次我们就来看看在revel框架下如何进行权限控制. 因为revel是MVC结构的,每一个url其实都会映射到一个具体的Cont ...

  2. Go语言实战 - revel框架教程之缓存和Job

    所有的网站应该都会有一个非常简单的需求,首页一秒之内打开. 满足的方式主要有两种: 页面静态化,效果最好,对服务器基本没负担,只要带宽足够就好了.我知道一个PV过亿的站点就是全站静态(以前新浪也是), ...

  3. Go语言实战 - revel框架教程之MongDB的最佳搭档revmgo

    由于revel框架本身对于model层的编写没有提供任何指导,所以在设计这部分的时候就有些犹豫,反复斟酌到底怎样才算是最佳实践. 我在做山坡网的时候刚开始也纠结了一下,拿不准mongodb的sessi ...

  4. Go语言实战 - revel框架教程之CSRF(跨站请求伪造)保护

    CSRF是什么?请看这篇博文“浅谈CSRF攻击方式”,说的非常清楚. 现在做网站敢不防CSRF的我猜只有两种情况,一是没什么人访问,二是局域网应用.山坡网之前属于第一种情况,哈哈,所以至今没什么问题. ...

  5. revel框架教程之缓存和Job

    Go语言实战 - revel框架教程之缓存和Job   所有的网站应该都会有一个非常简单的需求,首页一秒之内打开. 满足的方式主要有两种: 页面静态化,效果最好,对服务器基本没负担,只要带宽足够就好了 ...

  6. revel框架教程之权限控制

    Go语言实战 - revel框架教程之权限控制 一个站点上面最基本都会有三种用户角色,未登录用户.已登录用户和管理员.这一次我们就来看看在revel框架下如何进行权限控制. 因为revel是MVC结构 ...

  7. go语言实战教程:实战项目资源导入和项目框架搭建

    从本节内容开始,我们将利用我们所学习的Iris框架的相关知识,进行实战项目开发. 实战项目框架搭建 我们的实战项目是使用Iris框架开发一个关于本地服务平台的后台管理平台.平台中可以管理用户.商品.商 ...

  8. Windows7下安装golang语言开发环境和revel框架

    1.下载先去下载32位或64 golang window 安装包 并安装下载地址:https://www.golangtc.com/download 本人更改了安装地址为 D:\GO\Go 2. go ...

  9. Go语言笔记[实现一个Web框架实战]——EzWeb框架(一)

    Go语言笔记[实现一个Web框架实战]--EzWeb框架(一) 一.Golang中的net/http标准库如何处理一个请求 func main() { http.HandleFunc("/& ...

随机推荐

  1. mysql基本信息收集

    1.下载安装 percona-toolkit 工具包http://www.percona.com/downloads/percona-toolkit/LATEST/tarball/2.运行下面两个工具 ...

  2. opencv中的SIFT,SURF,ORB,FAST 特征描叙算子比较

    opencv中的SIFT,SURF,ORB,FAST 特征描叙算子比较 参考: http://wenku.baidu.com/link?url=1aDYAJBCrrK-uk2w3sSNai7h52x_ ...

  3. HDFS操作

    HDFS操作 1.shell 1.1 创建目录 hadoop fs -mkdir 目录名(其中/为根目录) 1.2 遍历目录 hadoop fs -ls 目录名 1.3 删除目录 hadoop fs ...

  4. Maven的pom.xml 配置详解

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  5. VS2012 还原默认设置

    恢复默认设置的2种方法 如果VS出现问题或设置变乱,可以通过恢复默认设置使之回到安装成功时的状态,从而解决出现的问题.VS恢复默认设置的方法有2种,分别是:通过"导入和导出设置"实 ...

  6. Codeforces CF#628 Education 8 E. Zbazi in Zeydabad

    E. Zbazi in Zeydabad time limit per test 5 seconds memory limit per test 512 megabytes input standar ...

  7. 关于H5填写信息类页面横向布局总结

    接触h5已经有快一年了,因为一直偏向页面重构所以在页面布局方面也算是经历过风风雨雨.所以总结一下自己用过的方法来比较归纳 首先来说,H5的页面一般分为两种,一种是用来做市场营销的,主要特征是图多,页面 ...

  8. Php:学习笔记(一):版本选择

    (注:本文来自网络) 超过75%的网站使用了PHP作为开发语言,wordpress,phpmyadmin和其他一些开源项目的盛行,带来了一大批的长尾用户.然而,他们一般安装之后却很少升级.下图是目前P ...

  9. C# winform安装部署(转载)

    c# winform 程序打包部署 核心总结: 1.建议在完成的要打包的项目外,另建解决方案建立安装部署项目(而不是在同一个解决方案内新建),在解决方案上右击-〉添加-〉现有项目-〉选择你要打包的项目 ...

  10. Android带边框表格的实现

    最近做项目需要用到表格,数据是动态指定的,本来用GridView是很方便的,可是老大不同意用这么重量级的控件,想办法吧. 做表格很容易想到用TableLayout,那就自定义一个来搞. 一.表格最蛋疼 ...