:golang

Go语言Web框架Tango中的中间件应用级别

Tango在创建之初就支持了全局中间件的支持,经过最近的改进,同时支持了Group级别和Route级别的中间件。下面我们就一起来看下这三种级别的中间件:

比如我们自定义了一个新的中间件:

func MyMiddleware() tango.HandlerFunc {
    return func(ctx *tango.Context) {
        ctx.Info("I'm a middleware")
        ctx.Next()
    }
}

全局中间件

全局中间件在任何一个请求调用时均会进行调用。用法如下:

t := tango.Classic()
t.Use(MyMiddleware())
t.Get("/", func() string {return "global"})

Group中间件

Group中间件在Group下匹配的路由被调用时会被调用。用法如下:

t := tango.Classic()
t.Group("/group", func(g *tango.Group) {
    g.Use(MyMiddleware())
    g.Get("/", func() string {return "group"})
})

Route中间件

Route中间件在该Route被调用时会被调用。如果有多个,会按照先后顺序调用。用法如下:

t := tango.Classic()
t.Get("/route", func() string {return "route"}, MyMiddleware())

中间件优先级

  • 全局中间件被先调用,Group中间件次之,最后是Route中间件
  • 相同级别的中间件,先加入的被先调用

Go语言Web框架Tango在Session中间件中的设计

Tango在创建之初的目标就是既有Beego的效率,又有martini的灵活中间件。因此Session也是在核心库 tango 之外作为官方中间件存在,地址是 tango-session

Session因为有着灵活的设计,因此可以完成很多我们希望的功能。首先看下Session中间件的选项:

type Options struct {
    MaxAge           time.Duration
    SessionIdName    string
    Store            Store
    Generator        IdGenerator
    Tracker          Tracker
    OnSessionNew     func(*Session)
    OnSessionRelease func(*Session)
}

其中我将要着重讲的是Store,Generator和Tracker。

Store

Store是一个接口,主要作用是存储Session中的内容,其定义如下:

type Store interface {
    Add(id Id) bool
    Exist(id Id) bool
    Clear(id Id) bool

    Get(id Id, key string) interface{}
    Set(id Id, key string, value interface{}) error
    Del(id Id, key string) bool

    SetMaxAge(maxAge time.Duration)
    SetIdMaxAge(id Id, maxAge time.Duration)

    Run() error
}

默认的内核自带了MemoryStore,这将会把所有Session内容保存在内存中。同时官方中间件中也提供了

这几种方式进行Session内容的存储。当然如果你愿意,也可以自己来实现一个Store。

Generator

Generator是一个接口,主要封装了SessionID的生成算法,其定义如下:

type IdGenerator interface {
    Gen(req *http.Request) Id
    IsValid(id Id) bool
}

默认的Generator是Sha1Generator,他是通过req.RemoteAddr,当前时间和随机字符串生成。 当然你也可以自定义更好的算法来生成SessionID。

Tracker

Tracker是一个接口,主要封装了Session的跟踪方式,其定义如下:

type Tracker interface {
    SetMaxAge(maxAge time.Duration)
    Get(req *http.Request) (Id, error)
    Set(req *http.Request, rw http.ResponseWriter, id Id)
    Clear(rw http.ResponseWriter)
}

默认的Tracker实现是CookieTracker,就是我们最常见的,将SessionID保存在cookie中,通过cookie来进行跟踪。Session中间件中也同时提供了HeaderTracker,支持将SessionID保存在自定义的Http Header中。当然你也可以自定义Tracker,比如通过URL参数来进行跟踪等等方式。

最后

看起来似乎很复杂,但是一般情况下都不需要去改变,你只需要

t := tango.Classic()
t.Use(session.New())
t.Run()

Go语言库net/smtp发送邮件故障一则

在通过net/smtp包发送邮件时遇到一个奇怪的错误,在mac和windows上运行都能够成功发送邮件,但是将程序跨平台编译后上传到debian linux执行报:

x509: failed to load system roots and no roots provided

的错误,看样子是在认证邮件服务器证书的根证书时没有找到本地的根证书库。最后通过运行

#apt-get install ca-certificates

之后,邮件成功发送。

在Xorm中使用Join和Extends标记

本文主要针对对Xorm已经有了一定了解的读者,如果您是第一次了解Xorm,请先阅读xorm操作手册

Xorm的基本操作都是比较简单的,可能大家也都比较熟悉了。今天主要讲解extends标记和join的使用。

一般我们会针对数据库中的每一个表,建立一个对应的结构体。比如:

type User struct {
    Id int64
    Name string
}

type Account struct {
    Id int64
    UserId int64 `xorm:"index"`
    Amount int64
}

type Car struct {
    Id int64
    UserId int64 `xorm:"index"`
    Type int
}

我们定义了三个结构体,对应数据库的三个表,我们在启动时通过:

engine.Sync2(new(User), new(Account), new(Car))

来进行数据库结构的同步。在这个数据库结构中,我们假设一个用户拥有一个Account,一个用户拥有多个Car。

OK。复杂需求来了。

1)我们需要获得所有的用户的姓名和对应的账户的余额:

type AccountUser struct {
    Account `xorm:"extends"`
    User `xorm:"extends"`
}

var accounts = make([]*AccountUser, 0)
engine.Table("account").Join("INNER", "user", "account.user_id = user.id").Find(&accounts)

OK。这样,我们就取出了user和对应的account,我们通过account.Account可以获取到Account的信息,通过account.User可以获取到User的信息。

这个是两个表Join,那么如果是三个表也是类似的做法。

2)我只需要用户名,不需要其它的内容:

type AccountUser struct {
    Account `xorm:"extends"`
    Name string
}
var accounts = make([]*AccountUser, 0)
engine.Table("account").Join("INNER", "user", "account.user_id = user.id").Find(&accounts)

其实我们代码也是差不多的,但是这里我们实际上在查询数据库的时候是查询了user表的所有内容的。只是在最后赋值到结构体时,按需赋值。

3)更复杂的,我们还想知道每人有几辆车。

type AccountUser struct {
    Account `xorm:"extends"`
    Name string
    NumCars int
}
var accounts = make([]*AccountUser, 0)
engine.Sql("select account.*, user.name, (select count(id) from car where car.user_id = user.id) as num_cars from account, user where account.user_id = user.id").Find(&accounts)

在这样的复杂需求下,我们使用了Sql函数和extends标记结合来完成这个操作。

Go语言的简介及Ubuntu下的安装和编译

简介

GO语言是Google基于BSD发布的开源系统级编程语言,目标是融合Python的开发效率和C的运行时效率于一体。该项目的网址是http://golang.org。目前只支持Linux,freebsd和Mac OS X平台的amd64和386架构。

安装

有一个快速的编译器安装说明,见http://golang.org/doc/install.html。我在虚拟机中的Ubuntu9.10下安装过程如下:

1.设置环境变量

一共需要设置4个变量

export GOROOT=$HOME/go

export GOARCH=386

export GOOS=linux

export GOBIN=$HOME/bin

如果你的平台是AMD64,请将GOARCH替换成amd64;另外GOBIN是可选的,可以

mkdir ~/bin

这里是存放GO语言编译器连接器的目录,需要加入到PATH:

PATH=${PATH}:$HOME/bin

将以上这行和上面4个export拷贝到你的.bashrc中。重新打开终端窗口。

2.获取GO源码

GO使用C写的,需要获取源码后编译,在命令行执行以下命令:

apt-get install python-setuptools python-dev

sudo easy_install mercurial

hg clone -r release https://go.googlecode.com/hg/ $GOROOT

sudo apt-get install bison gcc libc6-dev ed make

cd $GOROOT/src

make all

等到出现:

--- cd ../test

N known bugs; 0 unexpected bugs

(N为某个数字,我这里为4)后编译完成。

编写Hello GO!

针对不同的架构,编译器和链接器都是不一样的:

386对应的编译器是8g,链接器是8l;

amd64对应的编译器是6g,链接器是6l;

OK.我们来编写一个最简单的GO程序,代码如下:

package main

import "fmt"

func main() {

    fmt.Printf("hello, world\n")

}

将上面的代码保存为hello.go并编译之:

8g hello.go

编译后生成hello.8,再链接:

8l hello.8

生成hello.out,执行:

./hello.out

终端上将显示:

Hello, GO!

如果要编写大型程序,Make工具依然有效。

语法及类库文档

语法见这里:

http://golang.org/doc/go_spec.html

类库见这里:

http://golang.org/pkg/

有用的文档:

http://golang.org/doc/effective_go.html

http://golang.org/doc/go_tutorial.html

如果以前学习的C++可以参考这里:

http://golang.org/doc/go_for_cpp_programmers.html

保持最新版本

目前GO语言还在不断完善中,还没有到可以进入生产环境的时候,如果想及时更新最新的版本,如下:

hg pull

hg update release

make all