用戶註冊、登入和登出是任何一個網站都必然會有的功能,可以說,這是重新造輪子做多的領域,每個做網站的人應該都做過很多遍。見微知著,從這麼一個小功能其實就可以看到所使用的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 }
內部的細節暫時不多說,來吧,先讓使用者可以註冊。注意,在整個開發過程中大部分時候不需要重新啟動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,直接刷新浏览器页面就会发现新的代码已经自动编译并且启用了。
更多go语言相关文章请关注go语言教程栏目。
以上是go語言使用revel框架實作使用者註冊教學(附程式碼)的詳細內容。更多資訊請關注PHP中文網其他相關文章!