Merge pull request #103 from OlingCat/master
移除了cvs用的所有footer,重新格式化了所有的代码。
This commit is contained in:
3
1.1.md
3
1.1.md
@@ -130,8 +130,5 @@ homebrew是Mac系统下面目前使用最多的管理软件的工具,目前已
|
|||||||
* 上一节: [Go环境配置](<1.md>)
|
* 上一节: [Go环境配置](<1.md>)
|
||||||
* 下一节: [GOPATH 与工作空间](<1.2.md>)
|
* 下一节: [GOPATH 与工作空间](<1.2.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|
||||||
[downlink]: http://code.google.com/p/go/downloads/list "Go安装包下载"
|
[downlink]: http://code.google.com/p/go/downloads/list "Go安装包下载"
|
||||||
[hg]: http://mercurial.selenic.com/downloads/ "Mercurial下载"
|
[hg]: http://mercurial.selenic.com/downloads/ "Mercurial下载"
|
||||||
|
|||||||
125
1.2.md
125
1.2.md
@@ -7,11 +7,11 @@
|
|||||||
|
|
||||||
在类似 Unix 环境大概这样设置:
|
在类似 Unix 环境大概这样设置:
|
||||||
|
|
||||||
export GOPATH=/home/apple/mygo
|
export GOPATH=/home/apple/mygo
|
||||||
|
|
||||||
Windows 设置如下,新建一个环境变量名称叫做GOPATH:
|
Windows 设置如下,新建一个环境变量名称叫做GOPATH:
|
||||||
|
|
||||||
GOPATH=c:\mygo
|
GOPATH=c:\mygo
|
||||||
|
|
||||||
GOPATH允许多个目录,当有多个目录时,请注意分隔符,多个GOPATH的时候Windows是分号,Linux系统是冒号,当有多个GOPATH时,默认会将go get的内容放在第一个目录下
|
GOPATH允许多个目录,当有多个目录时,请注意分隔符,多个GOPATH的时候Windows是分号,Linux系统是冒号,当有多个GOPATH时,默认会将go get的内容放在第一个目录下
|
||||||
|
|
||||||
@@ -31,21 +31,21 @@ GOPATH允许多个目录,当有多个目录时,请注意分隔符,多个GO
|
|||||||
|
|
||||||
执行如下代码
|
执行如下代码
|
||||||
|
|
||||||
cd $GOPATH/src
|
cd $GOPATH/src
|
||||||
mkdir mymath
|
mkdir mymath
|
||||||
|
|
||||||
新建文件sqrt.go,内容如下
|
新建文件sqrt.go,内容如下
|
||||||
|
|
||||||
// $GOPATH/src/mymath/sqrt.go源码如下:
|
// $GOPATH/src/mymath/sqrt.go源码如下:
|
||||||
package mymath
|
package mymath
|
||||||
|
|
||||||
func Sqrt(x float64) float64 {
|
func Sqrt(x float64) float64 {
|
||||||
z := 0.0
|
z := 0.0
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
z -= (z*z - x) / (2 * x)
|
z -= (z*z - x) / (2 * x)
|
||||||
}
|
}
|
||||||
return z
|
return z
|
||||||
}
|
}
|
||||||
|
|
||||||
这样我的应用包目录和代码已经新建完毕,注意:package的名称必须和目录名保持一致
|
这样我的应用包目录和代码已经新建完毕,注意:package的名称必须和目录名保持一致
|
||||||
|
|
||||||
@@ -58,9 +58,9 @@ GOPATH允许多个目录,当有多个目录时,请注意分隔符,多个GO
|
|||||||
|
|
||||||
安装完之后,我们可以进入如下目录
|
安装完之后,我们可以进入如下目录
|
||||||
|
|
||||||
cd $GOPATH/pkg/${GOOS}_${GOARCH}
|
cd $GOPATH/pkg/${GOOS}_${GOARCH}
|
||||||
//可以看到如下文件
|
//可以看到如下文件
|
||||||
mymath.a
|
mymath.a
|
||||||
|
|
||||||
这个.a文件是应用包,相当于一个函数库一样,那么我们如何进行调用呢?
|
这个.a文件是应用包,相当于一个函数库一样,那么我们如何进行调用呢?
|
||||||
|
|
||||||
@@ -68,86 +68,86 @@ GOPATH允许多个目录,当有多个目录时,请注意分隔符,多个GO
|
|||||||
|
|
||||||
新建应用包mathapp
|
新建应用包mathapp
|
||||||
|
|
||||||
cd $GOPATH/src
|
cd $GOPATH/src
|
||||||
mkdir mathapp
|
mkdir mathapp
|
||||||
vim main.go
|
vim main.go
|
||||||
|
|
||||||
// `$GOPATH/src/mathapp/main.go`源码:
|
// `$GOPATH/src/mathapp/main.go`源码:
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"mymath"
|
"mymath"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Printf("Hello, world. Sqrt(2) = %v\n", mymath.Sqrt(2))
|
fmt.Printf("Hello, world. Sqrt(2) = %v\n", mymath.Sqrt(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
如何编译程序呢?进入该应用目录,然后执行`go build`,那么在该目录下面会生成一个mathapp的可执行文件
|
如何编译程序呢?进入该应用目录,然后执行`go build`,那么在该目录下面会生成一个mathapp的可执行文件
|
||||||
|
|
||||||
./mathapp
|
./mathapp
|
||||||
|
|
||||||
输出如下内容
|
输出如下内容
|
||||||
|
|
||||||
Hello, world. Sqrt(2) = 1.414213562373095
|
Hello, world. Sqrt(2) = 1.414213562373095
|
||||||
|
|
||||||
如何安装该应用,进入该目录执行`go install`,那么在$GOPATH/bin/下增加了一个可执行文件mathapp,这样可以在命令行输入如下命令就可以执行
|
如何安装该应用,进入该目录执行`go install`,那么在$GOPATH/bin/下增加了一个可执行文件mathapp,这样可以在命令行输入如下命令就可以执行
|
||||||
|
|
||||||
mathapp
|
mathapp
|
||||||
|
|
||||||
也是输出如下内容
|
也是输出如下内容
|
||||||
|
|
||||||
Hello, world. Sqrt(2) = 1.414213562373095
|
Hello, world. Sqrt(2) = 1.414213562373095
|
||||||
|
|
||||||
## 获取远程包
|
## 获取远程包
|
||||||
go语言有一个获取远程包的工具就是`go get`,目前go get支持多数开源社区(例如:github、googlecode、bitbucket、Launchpad)
|
go语言有一个获取远程包的工具就是`go get`,目前go get支持多数开源社区(例如:github、googlecode、bitbucket、Launchpad)
|
||||||
|
|
||||||
go get github.com/astaxie/beedb
|
go get github.com/astaxie/beedb
|
||||||
|
|
||||||
通过这个命令可以获取相应的源码,对应的开源平台采用不同的源码控制工具,例如github采用git、googlecode采用hg,所以要想获取这些源码,必须先安装相应的源码控制工具
|
通过这个命令可以获取相应的源码,对应的开源平台采用不同的源码控制工具,例如github采用git、googlecode采用hg,所以要想获取这些源码,必须先安装相应的源码控制工具
|
||||||
|
|
||||||
通过上面获取的代码在我们本地的源码相应的代码结构如下
|
通过上面获取的代码在我们本地的源码相应的代码结构如下
|
||||||
|
|
||||||
$GOPATH
|
$GOPATH
|
||||||
src
|
src
|
||||||
|--github.com
|
|--github.com
|
||||||
|-astaxie
|
|-astaxie
|
||||||
|-beedb
|
|-beedb
|
||||||
pkg
|
pkg
|
||||||
|--相应平台
|
|--相应平台
|
||||||
|-github.com
|
|-github.com
|
||||||
|--astaxie
|
|--astaxie
|
||||||
|beedb.a
|
|beedb.a
|
||||||
|
|
||||||
go get本质上可以理解为首先第一步是通过源码工具clone代码到src下面,然后执行`go install`
|
go get本质上可以理解为首先第一步是通过源码工具clone代码到src下面,然后执行`go install`
|
||||||
|
|
||||||
在代码中如何使用远程包,很简单的就是和使用本地包一样,只要在开头import相应的路径就可以
|
在代码中如何使用远程包,很简单的就是和使用本地包一样,只要在开头import相应的路径就可以
|
||||||
|
|
||||||
import "github.com/astaxie/beedb"
|
import "github.com/astaxie/beedb"
|
||||||
|
|
||||||
## 程序的整体结构
|
## 程序的整体结构
|
||||||
通过上面建立的我本地的mygo的目录结构如下所示
|
通过上面建立的我本地的mygo的目录结构如下所示
|
||||||
|
|
||||||
bin/
|
bin/
|
||||||
mathapp
|
mathapp
|
||||||
pkg/
|
pkg/
|
||||||
平台名/ 如:darwin_amd64、linux_amd64
|
平台名/ 如:darwin_amd64、linux_amd64
|
||||||
mymath.a
|
mymath.a
|
||||||
github.com/
|
github.com/
|
||||||
astaxie/
|
astaxie/
|
||||||
beedb.a
|
beedb.a
|
||||||
src/
|
src/
|
||||||
mathapp
|
mathapp
|
||||||
main.go
|
main.go
|
||||||
mymath/
|
mymath/
|
||||||
sqrt.go
|
sqrt.go
|
||||||
github.com/
|
github.com/
|
||||||
astaxie/
|
astaxie/
|
||||||
beedb/
|
beedb/
|
||||||
beedb.go
|
beedb.go
|
||||||
util.go
|
util.go
|
||||||
|
|
||||||
从上面的结构我们可以很清晰的看到,bin目录下面存的是编译之后可执行的文件,pkg下面存放的是函数包,src下面保存的是应用源代码
|
从上面的结构我们可以很清晰的看到,bin目录下面存的是编译之后可执行的文件,pkg下面存放的是函数包,src下面保存的是应用源代码
|
||||||
|
|
||||||
@@ -157,6 +157,3 @@ go get本质上可以理解为首先第一步是通过源码工具clone代码到
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [GO安装](<1.1.md>)
|
* 上一节: [GO安装](<1.1.md>)
|
||||||
* 下一节: [GO 命令](<1.3.md>)
|
* 下一节: [GO 命令](<1.3.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
21
1.3.md
21
1.3.md
@@ -26,10 +26,10 @@
|
|||||||
|
|
||||||
- 如果你的源代码针对不同的操作系统需要不同的处理,那么你可以根据不同的操作系统后缀来命名文件。例如有一个读取数组的程序,它对于不同的操作系统可能有如下几个源文件:
|
- 如果你的源代码针对不同的操作系统需要不同的处理,那么你可以根据不同的操作系统后缀来命名文件。例如有一个读取数组的程序,它对于不同的操作系统可能有如下几个源文件:
|
||||||
|
|
||||||
array_linux.go
|
array_linux.go
|
||||||
array_darwin.go
|
array_darwin.go
|
||||||
array_windows.go
|
array_windows.go
|
||||||
array_freebsd.go
|
array_freebsd.go
|
||||||
|
|
||||||
`go build`的时候会选择性地编译以系统名结尾的文件(linux、darwin、windows、freebsd)。例如Linux系统下面编译只会选择array_linux.go文件,其它系统命名后缀文件全部忽略。
|
`go build`的时候会选择性地编译以系统名结尾的文件(linux、darwin、windows、freebsd)。例如Linux系统下面编译只会选择array_linux.go文件,其它系统命名后缀文件全部忽略。
|
||||||
|
|
||||||
@@ -84,11 +84,11 @@
|
|||||||
|
|
||||||
很多人说go不需要任何的第三方文档,例如chm手册之类的(其实我已经做了一个了,[chm手册](https://github.com/astaxie/godoc)),因为它内部就有一个很强大的文档工具。
|
很多人说go不需要任何的第三方文档,例如chm手册之类的(其实我已经做了一个了,[chm手册](https://github.com/astaxie/godoc)),因为它内部就有一个很强大的文档工具。
|
||||||
|
|
||||||
如何查看相应package的文档呢?
|
如何查看相应package的文档呢?
|
||||||
例如builtin包,那么执行`go doc builtin`
|
例如builtin包,那么执行`go doc builtin`
|
||||||
如果是http包,那么执行`go doc net/http`
|
如果是http包,那么执行`go doc net/http`
|
||||||
查看某一个包里面的函数,那么执行`godoc fmt Printf`
|
查看某一个包里面的函数,那么执行`godoc fmt Printf`
|
||||||
也可以查看相应的代码,执行`godoc -src fmt Printf`
|
也可以查看相应的代码,执行`godoc -src fmt Printf`
|
||||||
|
|
||||||
通过命令在命令行执行 godoc -http=:端口号 比如`godoc -http=:8080`。然后在浏览器中打开`127.0.0.1:8080`,你将会看到一个golang.org的本地copy版本,通过它你可以查询pkg文档等其它内容。如果你设置了GOPATH,在pkg分类下,不但会列出标准包的文档,还会列出你本地`GOPATH`中所有项目的相关文档,这对于经常被墙的用户来说是一个不错的选择。
|
通过命令在命令行执行 godoc -http=:端口号 比如`godoc -http=:8080`。然后在浏览器中打开`127.0.0.1:8080`,你将会看到一个golang.org的本地copy版本,通过它你可以查询pkg文档等其它内容。如果你设置了GOPATH,在pkg分类下,不但会列出标准包的文档,还会列出你本地`GOPATH`中所有项目的相关文档,这对于经常被墙的用户来说是一个不错的选择。
|
||||||
|
|
||||||
@@ -106,6 +106,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [GOPATH与工作空间](<1.2.md>)
|
* 上一节: [GOPATH与工作空间](<1.2.md>)
|
||||||
* 下一节: [Go开发工具](<1.4.md>)
|
* 下一节: [Go开发工具](<1.4.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
261
1.4.md
261
1.4.md
@@ -20,11 +20,11 @@
|
|||||||
这里将介绍Sublime Text 2(以下简称Sublime)+GoSublime+gocode+MarGo的组合,那么为什么选择这个组合呢?
|
这里将介绍Sublime Text 2(以下简称Sublime)+GoSublime+gocode+MarGo的组合,那么为什么选择这个组合呢?
|
||||||
|
|
||||||
- 自动化提示代码,如下图所示
|
- 自动化提示代码,如下图所示
|
||||||

|

|
||||||
|
|
||||||
- 保存的时候自动格式化代码,让您编写的代码更加美观,符合Go的标准。
|
- 保存的时候自动格式化代码,让您编写的代码更加美观,符合Go的标准。
|
||||||
- 支持项目管理
|
- 支持项目管理
|
||||||

|

|
||||||
- 支持语法高亮
|
- 支持语法高亮
|
||||||
- Sublime Text 2可免费使用,只是保存次数达到一定数量之后就会提示是否购买,点击取消继续用,和正式注册版本没有任何区别。
|
- Sublime Text 2可免费使用,只是保存次数达到一定数量之后就会提示是否购买,点击取消继续用,和正式注册版本没有任何区别。
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
1. 打开之后安装 Package Control:Ctrl+` 打开命令行,执行如下代码:
|
1. 打开之后安装 Package Control:Ctrl+` 打开命令行,执行如下代码:
|
||||||
|
|
||||||
import urllib2,os; pf='Package Control.sublime-package'; ipp=sublime.installed_packages_path(); os.makedirs(ipp) if not os.path.exists(ipp) else None; urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler())); open(os.path.join(ipp,pf),'wb').write(urllib2.urlopen('http://sublime.wbond.net/'+pf.replace(' ','%20')).read()); print 'Please restart Sublime Text to finish installation'
|
import urllib2,os; pf='Package Control.sublime-package'; ipp=sublime.installed_packages_path(); os.makedirs(ipp) if not os.path.exists(ipp) else None; urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler())); open(os.path.join(ipp,pf),'wb').write(urllib2.urlopen('http://sublime.wbond.net/'+pf.replace(' ','%20')).read()); print 'Please restart Sublime Text to finish installation'
|
||||||
|
|
||||||
这个时候重启一下Sublime,可以发现在在菜单栏多了一个如下的栏目,说明Package Control已经安装成功了。
|
这个时候重启一下Sublime,可以发现在在菜单栏多了一个如下的栏目,说明Package Control已经安装成功了。
|
||||||
|
|
||||||
@@ -43,8 +43,8 @@
|
|||||||
2. 接下来安装gocode和MarGo。
|
2. 接下来安装gocode和MarGo。
|
||||||
打开终端运行如下代码(需要git)
|
打开终端运行如下代码(需要git)
|
||||||
|
|
||||||
go get -u github.com/nsf/gocode
|
go get -u github.com/nsf/gocode
|
||||||
go get -u github.com/DisposaBoy/MarGo
|
go get -u github.com/DisposaBoy/MarGo
|
||||||
|
|
||||||
这个时候我们会发现在`$GOPATH/bin`下面多了两个可执行文件,gocode和MarGo,这两个文件会在GoSublime加载时自动启动。
|
这个时候我们会发现在`$GOPATH/bin`下面多了两个可执行文件,gocode和MarGo,这两个文件会在GoSublime加载时自动启动。
|
||||||
|
|
||||||
@@ -70,36 +70,36 @@ Vim是从vi发展出来的一个文本编辑器, 代码补全、编译及错误
|
|||||||
|
|
||||||
1. 配置vim高亮显示
|
1. 配置vim高亮显示
|
||||||
|
|
||||||
cp -r $GOROOT/misc/vim/* ~/.vim/
|
cp -r $GOROOT/misc/vim/* ~/.vim/
|
||||||
|
|
||||||
2. 在~/.vimrc文件中增加语法高亮显示
|
2. 在~/.vimrc文件中增加语法高亮显示
|
||||||
|
|
||||||
filetype plugin indent on
|
filetype plugin indent on
|
||||||
syntax on
|
syntax on
|
||||||
|
|
||||||
3. 安装[Gocode](https://github.com/nsf/gocode/)
|
3. 安装[Gocode](https://github.com/nsf/gocode/)
|
||||||
|
|
||||||
go get -u github.com/nsf/gocode
|
go get -u github.com/nsf/gocode
|
||||||
|
|
||||||
gocode默认安装到`$GOPATH/bin`下面,需要把`$GOPATH/bin`路径设置到系统`$PATH`里面。
|
gocode默认安装到`$GOPATH/bin`下面,需要把`$GOPATH/bin`路径设置到系统`$PATH`里面。
|
||||||
|
|
||||||
4. 配置[Gocode](https://github.com/nsf/gocode/)
|
4. 配置[Gocode](https://github.com/nsf/gocode/)
|
||||||
|
|
||||||
~ cd $GOPATH/src/github.com/nsf/gocode/vim
|
~ cd $GOPATH/src/github.com/nsf/gocode/vim
|
||||||
~ ./update.bash
|
~ ./update.bash
|
||||||
~ gocode set propose-builtins true
|
~ gocode set propose-builtins true
|
||||||
propose-builtins true
|
propose-builtins true
|
||||||
~ gocode set lib-path "/home/border/gocode/pkg/linux_amd64"
|
~ gocode set lib-path "/home/border/gocode/pkg/linux_amd64"
|
||||||
lib-path "/home/border/gocode/pkg/linux_amd64"
|
lib-path "/home/border/gocode/pkg/linux_amd64"
|
||||||
~ gocode set
|
~ gocode set
|
||||||
propose-builtins true
|
propose-builtins true
|
||||||
lib-path "/home/border/gocode/pkg/linux_amd64"
|
lib-path "/home/border/gocode/pkg/linux_amd64"
|
||||||
|
|
||||||
>gocode set里面的两个参数的含意说明:
|
>gocode set里面的两个参数的含意说明:
|
||||||
>
|
>
|
||||||
>propose-builtins:是否自动提示Go的内置函数、类型和常量,默认为false,不提示。
|
>propose-builtins:是否自动提示Go的内置函数、类型和常量,默认为false,不提示。
|
||||||
>
|
>
|
||||||
>lib-path:默认情况下,gocode只会搜索**$GOPATH/pkg/$GOOS_$GOARCH **和 **$GOROOT/pkg/$GOOS_$GOARCH**目录下的包,当然这个设置就是可以设置我们额外的lib能访问的路径
|
>lib-path:默认情况下,gocode只会搜索**$GOPATH/pkg/$GOOS_$GOARCH** 和 **$GOROOT/pkg/$GOOS_$GOARCH**目录下的包,当然这个设置就是可以设置我们额外的lib能访问的路径
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -111,133 +111,133 @@ Emacs传说中的神器,她不仅仅是一个编辑器,它是一个整合环
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
1. 配置Emacs高亮显示
|
1. 配置Emacs高亮显示
|
||||||
|
|
||||||
cp $GOROOT/misc/emacs/* ~/.emacs.d/
|
cp $GOROOT/misc/emacs/* ~/.emacs.d/
|
||||||
|
|
||||||
2. 安装[Gocode](https://github.com/nsf/gocode/)
|
2. 安装[Gocode](https://github.com/nsf/gocode/)
|
||||||
|
|
||||||
go get -u github.com/nsf/gocode
|
go get -u github.com/nsf/gocode
|
||||||
|
|
||||||
gocode默认安装到`$GOPATH/bin`里面下面,需要把`$GOPATH/bin`路径设置到系统`$PATH`里面。
|
gocode默认安装到`$GOPATH/bin`里面下面,需要把`$GOPATH/bin`路径设置到系统`$PATH`里面。
|
||||||
|
|
||||||
3. 配置[Gocode](https://github.com/nsf/gocode/)
|
3. 配置[Gocode](https://github.com/nsf/gocode/)
|
||||||
|
|
||||||
|
|
||||||
~ cd $GOPATH/src/github.com/nsf/gocode/emacs
|
~ cd $GOPATH/src/github.com/nsf/gocode/emacs
|
||||||
~ cp go-autocomplete.el ~/.emacs.d/
|
~ cp go-autocomplete.el ~/.emacs.d/
|
||||||
~ gocode set propose-builtins true
|
~ gocode set propose-builtins true
|
||||||
propose-builtins true
|
propose-builtins true
|
||||||
~ gocode set lib-path "/home/border/gocode/pkg/linux_amd64" // 换为你自己的路径
|
~ gocode set lib-path "/home/border/gocode/pkg/linux_amd64" // 换为你自己的路径
|
||||||
lib-path "/home/border/gocode/pkg/linux_amd64"
|
lib-path "/home/border/gocode/pkg/linux_amd64"
|
||||||
~ gocode set
|
~ gocode set
|
||||||
propose-builtins true
|
propose-builtins true
|
||||||
lib-path "/home/border/gocode/pkg/linux_amd64"
|
lib-path "/home/border/gocode/pkg/linux_amd64"
|
||||||
|
|
||||||
4. 需要安装 [Auto Completion](http://www.emacswiki.org/emacs/AutoComplete)
|
4. 需要安装 [Auto Completion](http://www.emacswiki.org/emacs/AutoComplete)
|
||||||
|
|
||||||
下载AutoComplete并解压
|
下载AutoComplete并解压
|
||||||
|
|
||||||
~ make install DIR=$HOME/.emacs.d/auto-complete
|
~ make install DIR=$HOME/.emacs.d/auto-complete
|
||||||
|
|
||||||
配置~/.emacs文件
|
配置~/.emacs文件
|
||||||
|
|
||||||
;;auto-complete
|
;;auto-complete
|
||||||
(require 'auto-complete-config)
|
(require 'auto-complete-config)
|
||||||
(add-to-list 'ac-dictionary-directories "~/.emacs.d/auto-complete/ac-dict")
|
(add-to-list 'ac-dictionary-directories "~/.emacs.d/auto-complete/ac-dict")
|
||||||
(ac-config-default)
|
(ac-config-default)
|
||||||
(local-set-key (kbd "M-/") 'semantic-complete-analyze-inline)
|
(local-set-key (kbd "M-/") 'semantic-complete-analyze-inline)
|
||||||
(local-set-key "." 'semantic-complete-self-insert)
|
(local-set-key "." 'semantic-complete-self-insert)
|
||||||
(local-set-key ">" 'semantic-complete-self-insert)
|
(local-set-key ">" 'semantic-complete-self-insert)
|
||||||
|
|
||||||
详细信息参考: http://www.emacswiki.org/emacs/AutoComplete
|
详细信息参考: http://www.emacswiki.org/emacs/AutoComplete
|
||||||
|
|
||||||
5. 配置.emacs
|
5. 配置.emacs
|
||||||
|
|
||||||
;; golang mode
|
;; golang mode
|
||||||
(require 'go-mode-load)
|
(require 'go-mode-load)
|
||||||
(require 'go-autocomplete)
|
(require 'go-autocomplete)
|
||||||
;; speedbar
|
;; speedbar
|
||||||
;; (speedbar 1)
|
;; (speedbar 1)
|
||||||
(speedbar-add-supported-extension ".go")
|
(speedbar-add-supported-extension ".go")
|
||||||
(add-hook
|
(add-hook
|
||||||
'go-mode-hook
|
'go-mode-hook
|
||||||
'(lambda ()
|
'(lambda ()
|
||||||
;; gocode
|
;; gocode
|
||||||
(auto-complete-mode 1)
|
(auto-complete-mode 1)
|
||||||
(setq ac-sources '(ac-source-go))
|
(setq ac-sources '(ac-source-go))
|
||||||
;; Imenu & Speedbar
|
;; Imenu & Speedbar
|
||||||
(setq imenu-generic-expression
|
(setq imenu-generic-expression
|
||||||
'(("type" "^type *\\([^ \t\n\r\f]*\\)" 1)
|
'(("type" "^type *\\([^ \t\n\r\f]*\\)" 1)
|
||||||
("func" "^func *\\(.*\\) {" 1)))
|
("func" "^func *\\(.*\\) {" 1)))
|
||||||
(imenu-add-to-menubar "Index")
|
(imenu-add-to-menubar "Index")
|
||||||
;; Outline mode
|
;; Outline mode
|
||||||
(make-local-variable 'outline-regexp)
|
(make-local-variable 'outline-regexp)
|
||||||
(setq outline-regexp "//\\.\\|//[^\r\n\f][^\r\n\f]\\|pack\\|func\\|impo\\|cons\\|var.\\|type\\|\t\t*....")
|
(setq outline-regexp "//\\.\\|//[^\r\n\f][^\r\n\f]\\|pack\\|func\\|impo\\|cons\\|var.\\|type\\|\t\t*....")
|
||||||
(outline-minor-mode 1)
|
(outline-minor-mode 1)
|
||||||
(local-set-key "\M-a" 'outline-previous-visible-heading)
|
(local-set-key "\M-a" 'outline-previous-visible-heading)
|
||||||
(local-set-key "\M-e" 'outline-next-visible-heading)
|
(local-set-key "\M-e" 'outline-next-visible-heading)
|
||||||
;; Menu bar
|
;; Menu bar
|
||||||
(require 'easymenu)
|
(require 'easymenu)
|
||||||
(defconst go-hooked-menu
|
(defconst go-hooked-menu
|
||||||
'("Go tools"
|
'("Go tools"
|
||||||
["Go run buffer" go t]
|
["Go run buffer" go t]
|
||||||
["Go reformat buffer" go-fmt-buffer t]
|
["Go reformat buffer" go-fmt-buffer t]
|
||||||
["Go check buffer" go-fix-buffer t]))
|
["Go check buffer" go-fix-buffer t]))
|
||||||
(easy-menu-define
|
(easy-menu-define
|
||||||
go-added-menu
|
go-added-menu
|
||||||
(current-local-map)
|
(current-local-map)
|
||||||
"Go tools"
|
"Go tools"
|
||||||
go-hooked-menu)
|
go-hooked-menu)
|
||||||
|
|
||||||
;; Other
|
;; Other
|
||||||
(setq show-trailing-whitespace t)
|
(setq show-trailing-whitespace t)
|
||||||
))
|
))
|
||||||
;; helper function
|
;; helper function
|
||||||
(defun go ()
|
(defun go ()
|
||||||
"run current buffer"
|
"run current buffer"
|
||||||
(interactive)
|
(interactive)
|
||||||
(compile (concat "go run " (buffer-file-name))))
|
(compile (concat "go run " (buffer-file-name))))
|
||||||
|
|
||||||
;; helper function
|
;; helper function
|
||||||
(defun go-fmt-buffer ()
|
(defun go-fmt-buffer ()
|
||||||
"run gofmt on current buffer"
|
"run gofmt on current buffer"
|
||||||
(interactive)
|
(interactive)
|
||||||
(if buffer-read-only
|
(if buffer-read-only
|
||||||
(progn
|
(progn
|
||||||
(ding)
|
(ding)
|
||||||
(message "Buffer is read only"))
|
(message "Buffer is read only"))
|
||||||
(let ((p (line-number-at-pos))
|
(let ((p (line-number-at-pos))
|
||||||
(filename (buffer-file-name))
|
(filename (buffer-file-name))
|
||||||
(old-max-mini-window-height max-mini-window-height))
|
(old-max-mini-window-height max-mini-window-height))
|
||||||
(show-all)
|
(show-all)
|
||||||
(if (get-buffer "*Go Reformat Errors*")
|
(if (get-buffer "*Go Reformat Errors*")
|
||||||
(progn
|
(progn
|
||||||
(delete-windows-on "*Go Reformat Errors*")
|
(delete-windows-on "*Go Reformat Errors*")
|
||||||
(kill-buffer "*Go Reformat Errors*")))
|
(kill-buffer "*Go Reformat Errors*")))
|
||||||
(setq max-mini-window-height 1)
|
(setq max-mini-window-height 1)
|
||||||
(if (= 0 (shell-command-on-region (point-min) (point-max) "gofmt" "*Go Reformat Output*" nil "*Go Reformat Errors*" t))
|
(if (= 0 (shell-command-on-region (point-min) (point-max) "gofmt" "*Go Reformat Output*" nil "*Go Reformat Errors*" t))
|
||||||
(progn
|
(progn
|
||||||
(erase-buffer)
|
(erase-buffer)
|
||||||
(insert-buffer-substring "*Go Reformat Output*")
|
(insert-buffer-substring "*Go Reformat Output*")
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
(forward-line (1- p)))
|
(forward-line (1- p)))
|
||||||
(with-current-buffer "*Go Reformat Errors*"
|
(with-current-buffer "*Go Reformat Errors*"
|
||||||
(progn
|
(progn
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
(while (re-search-forward "<standard input>" nil t)
|
(while (re-search-forward "<standard input>" nil t)
|
||||||
(replace-match filename))
|
(replace-match filename))
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
(compilation-mode))))
|
(compilation-mode))))
|
||||||
(setq max-mini-window-height old-max-mini-window-height)
|
(setq max-mini-window-height old-max-mini-window-height)
|
||||||
(delete-windows-on "*Go Reformat Output*")
|
(delete-windows-on "*Go Reformat Output*")
|
||||||
(kill-buffer "*Go Reformat Output*"))))
|
(kill-buffer "*Go Reformat Output*"))))
|
||||||
;; helper function
|
;; helper function
|
||||||
(defun go-fix-buffer ()
|
(defun go-fix-buffer ()
|
||||||
"run gofix on current buffer"
|
"run gofix on current buffer"
|
||||||
(interactive)
|
(interactive)
|
||||||
(show-all)
|
(show-all)
|
||||||
(shell-command-on-region (point-min) (point-max) "go tool fix -diff"))
|
(shell-command-on-region (point-min) (point-max) "go tool fix -diff"))
|
||||||
|
|
||||||
6. 恭喜你,你现在可以体验在神器中开发Go的乐趣。默认speedbar是关闭的,如果打开需要把 ;; (speedbar 1) 前面的注释去掉,或者也可以通过 *M-x speedbar* 手动开启。
|
6. 恭喜你,你现在可以体验在神器中开发Go的乐趣。默认speedbar是关闭的,如果打开需要把 ;; (speedbar 1) 前面的注释去掉,或者也可以通过 *M-x speedbar* 手动开启。
|
||||||
|
|
||||||
@@ -299,6 +299,3 @@ gocode的github地址:
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [Go 命令](<1.3.md>)
|
* 上一节: [Go 命令](<1.3.md>)
|
||||||
* 下一节: [总结](<1.5.md>)
|
* 下一节: [总结](<1.5.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
1.5.md
3
1.5.md
@@ -6,6 +6,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [Go开发工具](<1.4.md>)
|
* 上一节: [Go开发工具](<1.4.md>)
|
||||||
* 下一章: [go语言基础](<2.md>)
|
* 下一章: [go语言基础](<2.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
5
1.md
5
1.md
@@ -13,7 +13,7 @@ Go是一种新的语言,一种并发的、带垃圾回收的、快速编译的
|
|||||||
|
|
||||||
- 它可以在一台计算机上用几秒钟的时间编译一个大型的Go程序。
|
- 它可以在一台计算机上用几秒钟的时间编译一个大型的Go程序。
|
||||||
- Go为软件构造提供了一种模型,它使依赖分析更加容易,且避免了大部分C风格include文件与库的开头。
|
- Go为软件构造提供了一种模型,它使依赖分析更加容易,且避免了大部分C风格include文件与库的开头。
|
||||||
- Go的类型系统没有层级,因此不需要在类型之间的关系定义上花费时间。此外,尽管Go是静态类型的,该语言也试图使类型感觉起来比典型的面向对象语言更轻量级。
|
- Go是静态类型的语言,它的类型系统没有层级。因此用户不需要在定义类型之间的关系上花费时间,这样感觉起来比典型的面向对象语言更轻量级。
|
||||||
- Go完全是垃圾回收型的语言,并为并发执行与通信提供了基本的支持。
|
- Go完全是垃圾回收型的语言,并为并发执行与通信提供了基本的支持。
|
||||||
- 按照其设计,Go打算为多核机器上系统软件的构造提供一种方法。
|
- 按照其设计,Go打算为多核机器上系统软件的构造提供一种方法。
|
||||||
|
|
||||||
@@ -24,6 +24,3 @@ Go试图成为结合解释型编程的轻松、动态类型语言的高效以及
|
|||||||
## links
|
## links
|
||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 下一节: [Go安装](<1.1.md>)
|
* 下一节: [Go安装](<1.1.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
24
10.1.md
24
10.1.md
@@ -56,17 +56,18 @@ GO语言默认采用"UTF-8"编码集,所以我们实现i18n时不考虑第三
|
|||||||
|
|
||||||
- Accept-Language
|
- Accept-Language
|
||||||
|
|
||||||
客户端请求的时候在HTTP头信息里面有`Accept-Language`,一般的客户端都会设置该信息,下面是Go语言实现的一个简单的根据`Accept-Language`实现设置地区的代码:
|
客户端请求的时候在HTTP头信息里面有`Accept-Language`,一般的客户端都会设置该信息,下面是Go语言实现的一个简单的根据`Accept-Language`实现设置地区的代码:
|
||||||
|
|
||||||
AL := r.Header.Get("Accept-Language")
|
AL := r.Header.Get("Accept-Language")
|
||||||
if AL == "en" {
|
if AL == "en" {
|
||||||
i18n.SetLocale("en")
|
i18n.SetLocale("en")
|
||||||
} else if AL == "zh-CN" {
|
} else if AL == "zh-CN" {
|
||||||
i18n.SetLocale("zh-CN")
|
i18n.SetLocale("zh-CN")
|
||||||
} else if AL == "zh-TW" {
|
} else if AL == "zh-TW" {
|
||||||
i18n.SetLocale("zh-TW")
|
i18n.SetLocale("zh-TW")
|
||||||
}
|
}
|
||||||
当然在实际应用中,可能需要更加严格的判断来进行设置地区
|
|
||||||
|
当然在实际应用中,可能需要更加严格的判断来进行设置地区
|
||||||
- IP地址
|
- IP地址
|
||||||
|
|
||||||
另一种根据客户端来设定地区就是用户访问的IP,我们根据相应的IP库,对应访问的IP到地区,目前全球比较常用的就是GeoIP Lite Country这个库。这种设置地区的机制非常简单,我们只需要根据IP数据库查询用户的IP然后返回国家地区,根据返回的结果设置对应的地区。
|
另一种根据客户端来设定地区就是用户访问的IP,我们根据相应的IP库,对应访问的IP到地区,目前全球比较常用的就是GeoIP Lite Country这个库。这种设置地区的机制非常简单,我们只需要根据IP数据库查询用户的IP然后返回国家地区,根据返回的结果设置对应的地区。
|
||||||
@@ -82,6 +83,3 @@ GO语言默认采用"UTF-8"编码集,所以我们实现i18n时不考虑第三
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [国际化和本地化](<10.md>)
|
* 上一节: [国际化和本地化](<10.md>)
|
||||||
* 下一节: [本地化资源](<10.2.md>)
|
* 下一节: [本地化资源](<10.2.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
39
10.2.md
39
10.2.md
@@ -94,18 +94,18 @@ $GOROOT/lib/time包中的timeinfo.zip含有locale对应的时区的定义,为
|
|||||||
我们可能会根据Locale的不同来展示视图,这些视图包含不同的图片、css、js等各种静态资源。那么应如何来处理这些信息呢?首先我们应按locale来组织文件信息,请看下面的文件目录安排:
|
我们可能会根据Locale的不同来展示视图,这些视图包含不同的图片、css、js等各种静态资源。那么应如何来处理这些信息呢?首先我们应按locale来组织文件信息,请看下面的文件目录安排:
|
||||||
|
|
||||||
views
|
views
|
||||||
|--en //英文模板
|
|--en //英文模板
|
||||||
|--images //存储图片信息
|
|--images //存储图片信息
|
||||||
|--js //存储JS文件
|
|--js //存储JS文件
|
||||||
|--css //存储css文件
|
|--css //存储css文件
|
||||||
index.tpl //用户首页
|
index.tpl //用户首页
|
||||||
login.tpl //登陆首页
|
login.tpl //登陆首页
|
||||||
|--zh-CN //中文模板
|
|--zh-CN //中文模板
|
||||||
|--images
|
|--images
|
||||||
|--js
|
|--js
|
||||||
|--css
|
|--css
|
||||||
index.tpl
|
index.tpl
|
||||||
login.tpl
|
login.tpl
|
||||||
|
|
||||||
有了这个目录结构后我们就可以在渲染的地方这样来实现代码:
|
有了这个目录结构后我们就可以在渲染的地方这样来实现代码:
|
||||||
|
|
||||||
@@ -116,12 +116,12 @@ $GOROOT/lib/time包中的timeinfo.zip含有locale对应的时区的定义,为
|
|||||||
|
|
||||||
而对于里面的index.tpl里面的资源设置如下:
|
而对于里面的index.tpl里面的资源设置如下:
|
||||||
|
|
||||||
//js文件
|
// js文件
|
||||||
<script type="text/javascript" src="views/{{.VV.Lang}}/js/jquery/jquery-1.8.0.min.js"></script>
|
<script type="text/javascript" src="views/{{.VV.Lang}}/js/jquery/jquery-1.8.0.min.js"></script>
|
||||||
//css文件
|
// css文件
|
||||||
<link href="views/{{.VV.Lang}}/css/bootstrap-responsive.min.css" rel="stylesheet">
|
<link href="views/{{.VV.Lang}}/css/bootstrap-responsive.min.css" rel="stylesheet">
|
||||||
//图片文件
|
// 图片文件
|
||||||
<img src="views/{{.VV.Lang}}/images/btn.png">
|
<img src="views/{{.VV.Lang}}/images/btn.png">
|
||||||
|
|
||||||
采用这种方式来本地化视图以及资源时,我们就可以很容易的进行扩展了。
|
采用这种方式来本地化视图以及资源时,我们就可以很容易的进行扩展了。
|
||||||
|
|
||||||
@@ -132,6 +132,3 @@ $GOROOT/lib/time包中的timeinfo.zip含有locale对应的时区的定义,为
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [设置默认地区](<10.1.md>)
|
* 上一节: [设置默认地区](<10.1.md>)
|
||||||
* 下一节: [国际化站点](<10.3.md>)
|
* 下一节: [国际化站点](<10.3.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
110
10.3.md
110
10.3.md
@@ -6,19 +6,19 @@
|
|||||||
# zh.json
|
# zh.json
|
||||||
|
|
||||||
{
|
{
|
||||||
"zh": {
|
"zh": {
|
||||||
"submit": "提交",
|
"submit": "提交",
|
||||||
"create": "创建"
|
"create": "创建"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#en.json
|
#en.json
|
||||||
|
|
||||||
{
|
{
|
||||||
"en": {
|
"en": {
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"create": "Create"
|
"create": "Create"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
为了支持国际化,在此我们使用了一个国际化相关的包——go-i18n(https://github.com/astaxie/go-i18n),首先我们向go-i18n包注册config/locales这个目录,以加载所有的locale文件
|
为了支持国际化,在此我们使用了一个国际化相关的包——go-i18n(https://github.com/astaxie/go-i18n),首先我们向go-i18n包注册config/locales这个目录,以加载所有的locale文件
|
||||||
@@ -100,75 +100,76 @@
|
|||||||
|
|
||||||
1. 文本信息
|
1. 文本信息
|
||||||
|
|
||||||
文本信息调用`Tr.Translate`来实现相应的信息转换,mapFunc的实现如下:
|
文本信息调用`Tr.Translate`来实现相应的信息转换,mapFunc的实现如下:
|
||||||
|
|
||||||
func I18nT(args ...interface{}) string {
|
func I18nT(args ...interface{}) string {
|
||||||
ok := false
|
ok := false
|
||||||
var s string
|
var s string
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
s, ok = args[0].(string)
|
s, ok = args[0].(string)
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
s = fmt.Sprint(args...)
|
|
||||||
}
|
|
||||||
return Tr.Translate(s)
|
|
||||||
}
|
}
|
||||||
|
if !ok {
|
||||||
|
s = fmt.Sprint(args...)
|
||||||
|
}
|
||||||
|
return Tr.Translate(s)
|
||||||
|
}
|
||||||
|
|
||||||
注册函数如下:
|
注册函数如下:
|
||||||
|
|
||||||
t.Funcs(template.FuncMap{"T": I18nT})
|
t.Funcs(template.FuncMap{"T": I18nT})
|
||||||
|
|
||||||
模板中使用如下:
|
模板中使用如下:
|
||||||
|
|
||||||
{{.V.Submit | T}}
|
{{.V.Submit | T}}
|
||||||
|
|
||||||
|
|
||||||
2. 时间日期
|
2. 时间日期
|
||||||
|
|
||||||
时间日期调用`Tr.Time`函数来实现相应的时间转换,mapFunc的实现如下:
|
时间日期调用`Tr.Time`函数来实现相应的时间转换,mapFunc的实现如下:
|
||||||
|
|
||||||
func I18nTimeDate(args ...interface{}) string {
|
func I18nTimeDate(args ...interface{}) string {
|
||||||
ok := false
|
ok := false
|
||||||
var s string
|
var s string
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
s, ok = args[0].(string)
|
s, ok = args[0].(string)
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
s = fmt.Sprint(args...)
|
|
||||||
}
|
|
||||||
return Tr.Time(s)
|
|
||||||
}
|
}
|
||||||
|
if !ok {
|
||||||
|
s = fmt.Sprint(args...)
|
||||||
|
}
|
||||||
|
return Tr.Time(s)
|
||||||
|
}
|
||||||
|
|
||||||
注册函数如下:
|
注册函数如下:
|
||||||
|
|
||||||
t.Funcs(template.FuncMap{"TD": I18nTimeDate})
|
t.Funcs(template.FuncMap{"TD": I18nTimeDate})
|
||||||
|
|
||||||
模板中使用如下:
|
模板中使用如下:
|
||||||
|
|
||||||
|
{{.V.Now | TD}}
|
||||||
|
|
||||||
{{.V.Now | TD}}
|
|
||||||
3. 货币信息
|
3. 货币信息
|
||||||
|
|
||||||
货币调用`Tr.Money`函数来实现相应的时间转换,mapFunc的实现如下:
|
货币调用`Tr.Money`函数来实现相应的时间转换,mapFunc的实现如下:
|
||||||
|
|
||||||
func I18nMoney(args ...interface{}) string {
|
func I18nMoney(args ...interface{}) string {
|
||||||
ok := false
|
ok := false
|
||||||
var s string
|
var s string
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
s, ok = args[0].(string)
|
s, ok = args[0].(string)
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
s = fmt.Sprint(args...)
|
|
||||||
}
|
|
||||||
return Tr.Money(s)
|
|
||||||
}
|
}
|
||||||
|
if !ok {
|
||||||
|
s = fmt.Sprint(args...)
|
||||||
|
}
|
||||||
|
return Tr.Money(s)
|
||||||
|
}
|
||||||
|
|
||||||
注册函数如下:
|
注册函数如下:
|
||||||
|
|
||||||
t.Funcs(template.FuncMap{"M": I18nMoney})
|
t.Funcs(template.FuncMap{"M": I18nMoney})
|
||||||
|
|
||||||
模板中使用如下:
|
模板中使用如下:
|
||||||
|
|
||||||
{{.V.Money | M}}
|
{{.V.Money | M}}
|
||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
通过这小节我们知道了如何实现一个多语言包的Web应用,通过自定义语言包我们可以方便的实现多语言,而且通过配置文件能够非常方便的扩充多语言,默认情况下,go-i18n会自定加载一些公共的配置信息,例如时间、货币等,我们就可以非常方便的使用,同时为了支持在模板中使用这些函数,也实现了相应的模板函数,这样就允许我们在开发Web应用的时候直接在模板中通过pipeline的方式来操作多语言包。
|
通过这小节我们知道了如何实现一个多语言包的Web应用,通过自定义语言包我们可以方便的实现多语言,而且通过配置文件能够非常方便的扩充多语言,默认情况下,go-i18n会自定加载一些公共的配置信息,例如时间、货币等,我们就可以非常方便的使用,同时为了支持在模板中使用这些函数,也实现了相应的模板函数,这样就允许我们在开发Web应用的时候直接在模板中通过pipeline的方式来操作多语言包。
|
||||||
@@ -177,6 +178,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [本地化资源](<10.2.md>)
|
* 上一节: [本地化资源](<10.2.md>)
|
||||||
* 下一节: [小结](<10.4.md>)
|
* 下一节: [小结](<10.4.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
10.4.md
3
10.4.md
@@ -4,6 +4,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [国际化站点](<10.3.md>)
|
* 上一节: [国际化站点](<10.3.md>)
|
||||||
* 下一节: [错误处理,故障排除和测试](<11.md>)
|
* 下一节: [错误处理,故障排除和测试](<11.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
10.md
3
10.md
@@ -25,6 +25,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [第九章总结](<9.7.md>)
|
* 上一章: [第九章总结](<9.7.md>)
|
||||||
* 下一节: [设置默认地区](<10.1.md>)
|
* 下一节: [设置默认地区](<10.1.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
6
11.1.md
6
11.1.md
@@ -6,9 +6,9 @@ Go语言设计的时候主要的特点是:简洁、明白,简洁是指语法
|
|||||||
下面这个例子通过`os.Open`打开一个文件,如果出错那么会执行`log.Fatal`打印出来错误信息:
|
下面这个例子通过`os.Open`打开一个文件,如果出错那么会执行`log.Fatal`打印出来错误信息:
|
||||||
|
|
||||||
f, err := os.Open("filename.ext")
|
f, err := os.Open("filename.ext")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
其实这样的error返回在Go语言的很多内置包里面有很多,我们这个小节将详细的介绍这些error是怎么设计的,以及在我们设计的Web应用如何更好的处理error。
|
其实这样的error返回在Go语言的很多内置包里面有很多,我们这个小节将详细的介绍这些error是怎么设计的,以及在我们设计的Web应用如何更好的处理error。
|
||||||
## Error类型
|
## Error类型
|
||||||
|
|||||||
3
2.1.md
3
2.1.md
@@ -50,6 +50,3 @@ Go使用`package`(和Python的模块类似)来组织代码。`main.main()`
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [Go语言基础](<2.md>)
|
* 上一节: [Go语言基础](<2.md>)
|
||||||
* 下一节: [Go基础](<2.2.md>)
|
* 下一节: [Go基础](<2.2.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
37
2.2.md
37
2.2.md
@@ -265,11 +265,11 @@ Go之所以会那么简洁,是因为它有一些默认的行为:
|
|||||||
|
|
||||||
也许你会说,我想数组里面的值还是数组,能实现吗?当然咯,Go支持嵌套数组,即多维数组。比如下面的代码就声明了一个二维数组:
|
也许你会说,我想数组里面的值还是数组,能实现吗?当然咯,Go支持嵌套数组,即多维数组。比如下面的代码就声明了一个二维数组:
|
||||||
|
|
||||||
// 声明了一个二维数组,该数组以两个数组作为元素,其中每个数组中又有4个int类型的元素
|
// 声明了一个二维数组,该数组以两个数组作为元素,其中每个数组中又有4个int类型的元素
|
||||||
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
|
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
|
||||||
|
|
||||||
// 如果内部的元素和外部的一样,那么上面的声明可以简化,直接忽略内部的类型
|
// 如果内部的元素和外部的一样,那么上面的声明可以简化,直接忽略内部的类型
|
||||||
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
|
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
|
||||||
|
|
||||||
数组的分配如下所示:
|
数组的分配如下所示:
|
||||||
|
|
||||||
@@ -343,8 +343,8 @@ slice有一些简便的操作
|
|||||||
- 长度,即`slice`的长度
|
- 长度,即`slice`的长度
|
||||||
- 最大长度,也就是`slice`开始位置到数组的最后位置的长度
|
- 最大长度,也就是`slice`开始位置到数组的最后位置的长度
|
||||||
|
|
||||||
Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
|
Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
|
||||||
Slice_a := Array_a[2:5]
|
Slice_a := Array_a[2:5]
|
||||||
|
|
||||||
上面代码的真正存储结构如下图所示
|
上面代码的真正存储结构如下图所示
|
||||||
|
|
||||||
@@ -429,17 +429,17 @@ slice有一些简便的操作
|
|||||||
关于“零值”,所指并非是空值,而是一种“变量未填充前”的默认值,通常为0。
|
关于“零值”,所指并非是空值,而是一种“变量未填充前”的默认值,通常为0。
|
||||||
此处罗列 部分类型 的 “零值”
|
此处罗列 部分类型 的 “零值”
|
||||||
|
|
||||||
int 0
|
int 0
|
||||||
int8 0
|
int8 0
|
||||||
int32 0
|
int32 0
|
||||||
int64 0
|
int64 0
|
||||||
uint 0x0
|
uint 0x0
|
||||||
rune 0 //rune的实际类型是 int32
|
rune 0 //rune的实际类型是 int32
|
||||||
byte 0x0 // byte的实际类型是 uint8
|
byte 0x0 // byte的实际类型是 uint8
|
||||||
float32 0 //长度为 4 byte
|
float32 0 //长度为 4 byte
|
||||||
float64 0 //长度为 8 byte
|
float64 0 //长度为 8 byte
|
||||||
bool false
|
bool false
|
||||||
string ""
|
string ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -448,6 +448,3 @@ slice有一些简便的操作
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [你好,Go](<2.1.md>)
|
* 上一章: [你好,Go](<2.1.md>)
|
||||||
* 下一节: [流程和函数](<2.3.md>)
|
* 下一节: [流程和函数](<2.3.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
11
2.3.md
11
2.3.md
@@ -112,9 +112,9 @@ Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读
|
|||||||
由于 Go 支持 “多值返回”, 而对于“声明而未被调用”的变量, 编译器会报错, 在这种情况下, 可以使用`_`来丢弃不需要的返回值
|
由于 Go 支持 “多值返回”, 而对于“声明而未被调用”的变量, 编译器会报错, 在这种情况下, 可以使用`_`来丢弃不需要的返回值
|
||||||
例如
|
例如
|
||||||
|
|
||||||
for _, v := range map{
|
for _, v := range map{
|
||||||
fmt.Println("map's val:", v)
|
fmt.Println("map's val:", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
### switch
|
### switch
|
||||||
@@ -360,7 +360,7 @@ Go里面有一个不错的设计,就是回调函数,有点类似面向对象
|
|||||||
如果有很多调用`defer`,那么`defer`是采用后进先出模式,所以如下代码会输出`4 3 2 1 0`
|
如果有很多调用`defer`,那么`defer`是采用后进先出模式,所以如下代码会输出`4 3 2 1 0`
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
defer fmt.Printf("%d ", i)
|
defer fmt.Printf("%d ", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
### 函数作为值、类型
|
### 函数作为值、类型
|
||||||
@@ -460,6 +460,3 @@ Go程序会自动调用`init()`和`main()`,所以你不需要在任何地方
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [Go基础](<2.2.md>)
|
* 上一章: [Go基础](<2.2.md>)
|
||||||
* 下一节: [struct类型](<2.4.md>)
|
* 下一节: [struct类型](<2.4.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
11
2.4.md
11
2.4.md
@@ -30,7 +30,7 @@ Go语言中,也和C或者其他语言一样,我们可以声明新的类型
|
|||||||
|
|
||||||
- 2.通过`field:value`的方式初始化,这样可以任意顺序
|
- 2.通过`field:value`的方式初始化,这样可以任意顺序
|
||||||
|
|
||||||
P := person{age:24, name:"Tom"}
|
P := person{age:24, name:"Tom"}
|
||||||
|
|
||||||
下面我们看一个完整的使用struct的例子
|
下面我们看一个完整的使用struct的例子
|
||||||
|
|
||||||
@@ -69,13 +69,13 @@ Go语言中,也和C或者其他语言一样,我们可以声明新的类型
|
|||||||
bp_Older, bp_diff := Older(bob, paul)
|
bp_Older, bp_diff := Older(bob, paul)
|
||||||
|
|
||||||
fmt.Printf("Of %s and %s, %s is older by %d years\n",
|
fmt.Printf("Of %s and %s, %s is older by %d years\n",
|
||||||
tom.name, bob.name, tb_Older.name, tb_diff)
|
tom.name, bob.name, tb_Older.name, tb_diff)
|
||||||
|
|
||||||
fmt.Printf("Of %s and %s, %s is older by %d years\n",
|
fmt.Printf("Of %s and %s, %s is older by %d years\n",
|
||||||
tom.name, paul.name, tp_Older.name, tp_diff)
|
tom.name, paul.name, tp_Older.name, tp_diff)
|
||||||
|
|
||||||
fmt.Printf("Of %s and %s, %s is older by %d years\n",
|
fmt.Printf("Of %s and %s, %s is older by %d years\n",
|
||||||
bob.name, paul.name, bp_Older.name, bp_diff)
|
bob.name, paul.name, bp_Older.name, bp_diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
### struct的匿名字段
|
### struct的匿名字段
|
||||||
@@ -205,6 +205,3 @@ Go里面很简单的解决了这个问题,最外层的优先访问,也就是
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [流程和函数](<2.3.md>)
|
* 上一章: [流程和函数](<2.3.md>)
|
||||||
* 下一节: [面向对象](<2.5.md>)
|
* 下一节: [面向对象](<2.5.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
2.5.md
3
2.5.md
@@ -319,6 +319,3 @@ method的语法如下:
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [struct类型](<2.4.md>)
|
* 上一章: [struct类型](<2.4.md>)
|
||||||
* 下一节: [interface](<2.6.md>)
|
* 下一节: [interface](<2.6.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
331
2.6.md
331
2.6.md
@@ -16,71 +16,71 @@ Go语言里面设计最精妙的应该算interface,它让面向对象,内容
|
|||||||
interface类型定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了此接口。详细的语法参考下面这个例子
|
interface类型定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了此接口。详细的语法参考下面这个例子
|
||||||
|
|
||||||
type Human struct {
|
type Human struct {
|
||||||
name string
|
name string
|
||||||
age int
|
age int
|
||||||
phone string
|
phone string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Student struct {
|
type Student struct {
|
||||||
Human //匿名字段Human
|
Human //匿名字段Human
|
||||||
school string
|
school string
|
||||||
loan float32
|
loan float32
|
||||||
}
|
}
|
||||||
|
|
||||||
type Employee struct {
|
type Employee struct {
|
||||||
Human //匿名字段Human
|
Human //匿名字段Human
|
||||||
company string
|
company string
|
||||||
money float32
|
money float32
|
||||||
}
|
}
|
||||||
|
|
||||||
//Human对象实现Sayhi方法
|
//Human对象实现Sayhi方法
|
||||||
func (h *Human) SayHi() {
|
func (h *Human) SayHi() {
|
||||||
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
|
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Human对象实现Sing方法
|
// Human对象实现Sing方法
|
||||||
func (h *Human) Sing(lyrics string) {
|
func (h *Human) Sing(lyrics string) {
|
||||||
fmt.Println("La la, la la la, la la la la la...", lyrics)
|
fmt.Println("La la, la la la, la la la la la...", lyrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Human对象实现Guzzle方法
|
//Human对象实现Guzzle方法
|
||||||
func (h *Human) Guzzle(beerStein string) {
|
func (h *Human) Guzzle(beerStein string) {
|
||||||
fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
|
fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Employee重载Human的Sayhi方法
|
// Employee重载Human的Sayhi方法
|
||||||
func (e *Employee) SayHi() {
|
func (e *Employee) SayHi() {
|
||||||
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
|
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
|
||||||
e.company, e.phone) //Yes you can split into 2 lines here.
|
e.company, e.phone) //Yes you can split into 2 lines here.
|
||||||
}
|
}
|
||||||
|
|
||||||
//Student实现BorrowMoney方法
|
//Student实现BorrowMoney方法
|
||||||
func (s *Student) BorrowMoney(amount float32) {
|
func (s *Student) BorrowMoney(amount float32) {
|
||||||
s.loan += amount // (again and again and...)
|
s.loan += amount // (again and again and...)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Employee实现SpendSalary方法
|
//Employee实现SpendSalary方法
|
||||||
func (e *Employee) SpendSalary(amount float32) {
|
func (e *Employee) SpendSalary(amount float32) {
|
||||||
e.money -= amount // More vodka please!!! Get me through the day!
|
e.money -= amount // More vodka please!!! Get me through the day!
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义interface
|
// 定义interface
|
||||||
type Men interface {
|
type Men interface {
|
||||||
SayHi()
|
SayHi()
|
||||||
Sing(lyrics string)
|
Sing(lyrics string)
|
||||||
Guzzle(beerStein string)
|
Guzzle(beerStein string)
|
||||||
}
|
}
|
||||||
|
|
||||||
type YoungChap interface {
|
type YoungChap interface {
|
||||||
SayHi()
|
SayHi()
|
||||||
Sing(song string)
|
Sing(song string)
|
||||||
BorrowMoney(amount float32)
|
BorrowMoney(amount float32)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ElderlyGent interface {
|
type ElderlyGent interface {
|
||||||
SayHi()
|
SayHi()
|
||||||
Sing(song string)
|
Sing(song string)
|
||||||
SpendSalary(amount float32)
|
SpendSalary(amount float32)
|
||||||
}
|
}
|
||||||
|
|
||||||
通过上面的代码我们可以知道,interface可以被任意的对象实现。我们看到上面的Men interface被Human、Student和Employee实现。同理,一个对象可以实现任意多个interface,例如上面的Student实现了Men和YonggChap两个interface。
|
通过上面的代码我们可以知道,interface可以被任意的对象实现。我们看到上面的Men interface被Human、Student和Employee实现。同理,一个对象可以实现任意多个interface,例如上面的Student实现了Men和YonggChap两个interface。
|
||||||
@@ -98,76 +98,76 @@ interface类型定义了一组方法,如果某个对象实现了某个接口
|
|||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
type Human struct {
|
type Human struct {
|
||||||
name string
|
name string
|
||||||
age int
|
age int
|
||||||
phone string
|
phone string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Student struct {
|
type Student struct {
|
||||||
Human //匿名字段
|
Human //匿名字段
|
||||||
school string
|
school string
|
||||||
loan float32
|
loan float32
|
||||||
}
|
}
|
||||||
|
|
||||||
type Employee struct {
|
type Employee struct {
|
||||||
Human //匿名字段
|
Human //匿名字段
|
||||||
company string
|
company string
|
||||||
money float32
|
money float32
|
||||||
}
|
}
|
||||||
|
|
||||||
//Human实现Sayhi方法
|
//Human实现Sayhi方法
|
||||||
func (h Human) SayHi() {
|
func (h Human) SayHi() {
|
||||||
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
|
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Human实现Sing方法
|
//Human实现Sing方法
|
||||||
func (h Human) Sing(lyrics string) {
|
func (h Human) Sing(lyrics string) {
|
||||||
fmt.Println("La la la la...", lyrics)
|
fmt.Println("La la la la...", lyrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Employee重载Human的SayHi方法
|
//Employee重载Human的SayHi方法
|
||||||
func (e Employee) SayHi() {
|
func (e Employee) SayHi() {
|
||||||
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
|
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
|
||||||
e.company, e.phone) //Yes you can split into 2 lines here.
|
e.company, e.phone) //Yes you can split into 2 lines here.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interface Men被Human,Student和Employee实现
|
// Interface Men被Human,Student和Employee实现
|
||||||
// 因为这三个类型都实现了这两个方法
|
// 因为这三个类型都实现了这两个方法
|
||||||
type Men interface {
|
type Men interface {
|
||||||
SayHi()
|
SayHi()
|
||||||
Sing(lyrics string)
|
Sing(lyrics string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
|
mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
|
||||||
paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
|
paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
|
||||||
sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
|
sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
|
||||||
Tom := Employee{Human{"Sam", 36, "444-222-XXX"}, "Things Ltd.", 5000}
|
Tom := Employee{Human{"Sam", 36, "444-222-XXX"}, "Things Ltd.", 5000}
|
||||||
|
|
||||||
//定义Men类型的变量i
|
//定义Men类型的变量i
|
||||||
var i Men
|
var i Men
|
||||||
|
|
||||||
//i能存储Student
|
//i能存储Student
|
||||||
i = mike
|
i = mike
|
||||||
fmt.Println("This is Mike, a Student:")
|
fmt.Println("This is Mike, a Student:")
|
||||||
i.SayHi()
|
i.SayHi()
|
||||||
i.Sing("November rain")
|
i.Sing("November rain")
|
||||||
|
|
||||||
//i也能存储Employee
|
//i也能存储Employee
|
||||||
i = Tom
|
i = Tom
|
||||||
fmt.Println("This is Tom, an Employee:")
|
fmt.Println("This is Tom, an Employee:")
|
||||||
i.SayHi()
|
i.SayHi()
|
||||||
i.Sing("Born to be wild")
|
i.Sing("Born to be wild")
|
||||||
|
|
||||||
//定义了slice Men
|
//定义了slice Men
|
||||||
fmt.Println("Let's use a slice of Men and see what happens")
|
fmt.Println("Let's use a slice of Men and see what happens")
|
||||||
x := make([]Men, 3)
|
x := make([]Men, 3)
|
||||||
//T这三个都是不同类型的元素,但是他们实现了interface同一个接口
|
//T这三个都是不同类型的元素,但是他们实现了interface同一个接口
|
||||||
x[0], x[1], x[2] = paul, sam, mike
|
x[0], x[1], x[2] = paul, sam, mike
|
||||||
|
|
||||||
for _, value := range x{
|
for _, value := range x{
|
||||||
value.SayHi()
|
value.SayHi()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
通过上面的代码,你会发现interface就是一组抽象方法的集合,它必须由其他非interface类型实现,而不能自我实现, go 通过interface实现了duck-typing:即"当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子"。
|
通过上面的代码,你会发现interface就是一组抽象方法的集合,它必须由其他非interface类型实现,而不能自我实现, go 通过interface实现了duck-typing:即"当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子"。
|
||||||
@@ -190,30 +190,30 @@ interface的变量可以持有任意实现该interface类型的对象,这给
|
|||||||
举个例子:我们已经知道fmt.Println是我们常用的一个函数,但是你是否注意到它可以接受任意类型的数据。打开fmt的源码文件,你会看到这样一个定义:
|
举个例子:我们已经知道fmt.Println是我们常用的一个函数,但是你是否注意到它可以接受任意类型的数据。打开fmt的源码文件,你会看到这样一个定义:
|
||||||
|
|
||||||
type Stringer interface {
|
type Stringer interface {
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
任何实现了String方法的类型都能作为参数去调用fmt.Println,让我们来试一试
|
任何实现了String方法的类型都能作为参数去调用fmt.Println,让我们来试一试
|
||||||
|
|
||||||
package main
|
package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Human struct {
|
type Human struct {
|
||||||
name string
|
name string
|
||||||
age int
|
age int
|
||||||
phone string
|
phone string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过这个方法 Human 实现了 fmt.Stringer
|
// 通过这个方法 Human 实现了 fmt.Stringer
|
||||||
func (h Human) String() string {
|
func (h Human) String() string {
|
||||||
return "❰"+h.name+" - "+strconv.Itoa(h.age)+" years - ✆ " +h.phone+"❱"
|
return "❰"+h.name+" - "+strconv.Itoa(h.age)+" years - ✆ " +h.phone+"❱"
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
Bob := Human{"Bob", 39, "000-7777-XXX"}
|
Bob := Human{"Bob", 39, "000-7777-XXX"}
|
||||||
fmt.Println("This Human is : ", Bob)
|
fmt.Println("This Human is : ", Bob)
|
||||||
}
|
}
|
||||||
现在我们再回顾一下前面的Box示例,你会发现Color结构也定义了一个method:String。其实这也是实现了fmt.Stringer这个interface,即如果需要某个类型能被fmt包以特殊的格式输出,你就必须实现Stringer这个接口。如果没有实现这个接口,fmt将以默认的方式输出。
|
现在我们再回顾一下前面的Box示例,你会发现Color结构也定义了一个method:String。其实这也是实现了fmt.Stringer这个interface,即如果需要某个类型能被fmt包以特殊的格式输出,你就必须实现Stringer这个接口。如果没有实现这个接口,fmt将以默认的方式输出。
|
||||||
|
|
||||||
@@ -234,91 +234,91 @@ interface的变量可以持有任意实现该interface类型的对象,这给
|
|||||||
|
|
||||||
让我们通过一个例子来更加深入的理解。
|
让我们通过一个例子来更加深入的理解。
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Element interface{}
|
type Element interface{}
|
||||||
type List [] Element
|
type List [] Element
|
||||||
|
|
||||||
type Person struct {
|
type Person struct {
|
||||||
name string
|
name string
|
||||||
age int
|
age int
|
||||||
|
}
|
||||||
|
|
||||||
|
//定义了String方法,实现了fmt.Stringer
|
||||||
|
func (p Person) String() string {
|
||||||
|
return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
list := make(List, 3)
|
||||||
|
list[0] = 1 // an int
|
||||||
|
list[1] = "Hello" // a string
|
||||||
|
list[2] = Person{"Dennis", 70}
|
||||||
|
|
||||||
|
for index, element := range list {
|
||||||
|
if value, ok := element.(int); ok {
|
||||||
|
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
|
||||||
|
} else if value, ok := element.(string); ok {
|
||||||
|
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
|
||||||
|
} else if value, ok := element.(Person); ok {
|
||||||
|
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
|
||||||
|
} else {
|
||||||
|
fmt.Println("list[%d] is of a different type", index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//定义了String方法,实现了fmt.Stringer
|
是不是很简单啊,同时你是否注意到了多个ifs里面,还记得我前面介绍流程里面讲过,if里面允许初始化变量。
|
||||||
func (p Person) String() string {
|
|
||||||
return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
也许你注意到了,我们断言的类型越多,那么ifelse也就越多,所以才引出了下面要介绍的switch。
|
||||||
list := make(List, 3)
|
|
||||||
list[0] = 1 // an int
|
|
||||||
list[1] = "Hello" // a string
|
|
||||||
list[2] = Person{"Dennis", 70}
|
|
||||||
|
|
||||||
for index, element := range list {
|
|
||||||
if value, ok := element.(int); ok {
|
|
||||||
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
|
|
||||||
} else if value, ok := element.(string); ok {
|
|
||||||
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
|
|
||||||
} else if value, ok := element.(Person); ok {
|
|
||||||
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
|
|
||||||
} else {
|
|
||||||
fmt.Println("list[%d] is of a different type", index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
是不是很简单啊,同时你是否注意到了多个ifs里面,还记得我前面介绍流程里面讲过,if里面允许初始化变量。
|
|
||||||
|
|
||||||
也许你注意到了,我们断言的类型越多,那么ifelse也就越多,所以才引出了下面要介绍的switch。
|
|
||||||
- switch测试
|
- switch测试
|
||||||
|
|
||||||
最好的讲解就是代码例子,现在让我们重写上面的这个实现
|
最好的讲解就是代码例子,现在让我们重写上面的这个实现
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Element interface{}
|
type Element interface{}
|
||||||
type List [] Element
|
type List [] Element
|
||||||
|
|
||||||
type Person struct {
|
type Person struct {
|
||||||
name string
|
name string
|
||||||
age int
|
age int
|
||||||
}
|
}
|
||||||
|
|
||||||
//打印
|
//打印
|
||||||
func (p Person) String() string {
|
func (p Person) String() string {
|
||||||
return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
|
return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
list := make(List, 3)
|
list := make(List, 3)
|
||||||
list[0] = 1 //an int
|
list[0] = 1 //an int
|
||||||
list[1] = "Hello" //a string
|
list[1] = "Hello" //a string
|
||||||
list[2] = Person{"Dennis", 70}
|
list[2] = Person{"Dennis", 70}
|
||||||
|
|
||||||
for index, element := range list{
|
for index, element := range list{
|
||||||
switch value := element.(type) {
|
switch value := element.(type) {
|
||||||
case int:
|
case int:
|
||||||
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
|
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
|
||||||
case string:
|
case string:
|
||||||
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
|
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
|
||||||
case Person:
|
case Person:
|
||||||
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
|
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
|
||||||
default:
|
default:
|
||||||
fmt.Println("list[%d] is of a different type", index)
|
fmt.Println("list[%d] is of a different type", index)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
这里有一点需要强调的是:`element.(type)`语法不能在switch外的任何逻辑里面使用,如果你要在switch外面判断一个类型就使用`comma-ok`。
|
这里有一点需要强调的是:`element.(type)`语法不能在switch外的任何逻辑里面使用,如果你要在switch外面判断一个类型就使用`comma-ok`。
|
||||||
|
|
||||||
@@ -328,21 +328,21 @@ Go里面真正吸引人的是他内置的逻辑语法,就像我们在学习Str
|
|||||||
我们可以看到源码包container/heap里面有这样的一个定义
|
我们可以看到源码包container/heap里面有这样的一个定义
|
||||||
|
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
sort.Interface //嵌入字段sort.Interface
|
sort.Interface //嵌入字段sort.Interface
|
||||||
Push(x interface{}) //a Push method to push elements into the heap
|
Push(x interface{}) //a Push method to push elements into the heap
|
||||||
Pop() interface{} //a Pop elements that pops elements from the heap
|
Pop() interface{} //a Pop elements that pops elements from the heap
|
||||||
}
|
}
|
||||||
|
|
||||||
我们看到sort.Interface其实就是嵌入字段,把sort.Interface的所有method给隐式的包含进来了。也就是下面三个方法
|
我们看到sort.Interface其实就是嵌入字段,把sort.Interface的所有method给隐式的包含进来了。也就是下面三个方法
|
||||||
|
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
// Len is the number of elements in the collection.
|
// Len is the number of elements in the collection.
|
||||||
Len() int
|
Len() int
|
||||||
// Less returns whether the element with index i should sort
|
// Less returns whether the element with index i should sort
|
||||||
// before the element with index j.
|
// before the element with index j.
|
||||||
Less(i, j int) bool
|
Less(i, j int) bool
|
||||||
// Swap swaps the elements with indexes i and j.
|
// Swap swaps the elements with indexes i and j.
|
||||||
Swap(i, j int)
|
Swap(i, j int)
|
||||||
}
|
}
|
||||||
|
|
||||||
另一个例子就是io包下面的 io.ReadWriter ,他包含了io包下面的Reader和Writer两个interface。
|
另一个例子就是io包下面的 io.ReadWriter ,他包含了io包下面的Reader和Writer两个interface。
|
||||||
@@ -350,7 +350,7 @@ Go里面真正吸引人的是他内置的逻辑语法,就像我们在学习Str
|
|||||||
// io.ReadWriter
|
// io.ReadWriter
|
||||||
type ReadWriter interface {
|
type ReadWriter interface {
|
||||||
Reader
|
Reader
|
||||||
Writer
|
Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
### 反射
|
### 反射
|
||||||
@@ -369,23 +369,23 @@ Go语言实现了反射,所谓反射就是动态运行时的状态。我们一
|
|||||||
获取反射值能返回相应的类型和数值
|
获取反射值能返回相应的类型和数值
|
||||||
|
|
||||||
var x float64 = 3.4
|
var x float64 = 3.4
|
||||||
v := reflect.ValueOf(x)
|
v := reflect.ValueOf(x)
|
||||||
fmt.Println("type:", v.Type())
|
fmt.Println("type:", v.Type())
|
||||||
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
|
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
|
||||||
fmt.Println("value:", v.Float())
|
fmt.Println("value:", v.Float())
|
||||||
|
|
||||||
最后,反射的话,那么反射的字段必须是可修改的,我们前面学习过传值和传引用,这个里面也是一样的道理,反射的字段必须是可读写的意思是,如果下面这样写,那么会发生错误
|
最后,反射的话,那么反射的字段必须是可修改的,我们前面学习过传值和传引用,这个里面也是一样的道理,反射的字段必须是可读写的意思是,如果下面这样写,那么会发生错误
|
||||||
|
|
||||||
var x float64 = 3.4
|
var x float64 = 3.4
|
||||||
v := reflect.ValueOf(x)
|
v := reflect.ValueOf(x)
|
||||||
v.SetFloat(7.1)
|
v.SetFloat(7.1)
|
||||||
|
|
||||||
如果要修改相应的值,必须这样写
|
如果要修改相应的值,必须这样写
|
||||||
|
|
||||||
var x float64 = 3.4
|
var x float64 = 3.4
|
||||||
p := reflect.ValueOf(&x)
|
p := reflect.ValueOf(&x)
|
||||||
v := p.Elem()
|
v := p.Elem()
|
||||||
v.SetFloat(7.1)
|
v.SetFloat(7.1)
|
||||||
|
|
||||||
使用反射需要自己在编程中不断的深入去了解,我这边只能大概的介绍一些。
|
使用反射需要自己在编程中不断的深入去了解,我这边只能大概的介绍一些。
|
||||||
|
|
||||||
@@ -393,6 +393,3 @@ Go语言实现了反射,所谓反射就是动态运行时的状态。我们一
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [面向对象](<2.5.md>)
|
* 上一章: [面向对象](<2.5.md>)
|
||||||
* 下一节: [并发](<2.7.md>)
|
* 下一节: [并发](<2.7.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
29
2.7.md
29
2.7.md
@@ -115,19 +115,19 @@ channel通过操作符`<-`来接收和发送数据
|
|||||||
)
|
)
|
||||||
|
|
||||||
func fibonacci(n int, c chan int) {
|
func fibonacci(n int, c chan int) {
|
||||||
x, y := 1, 1
|
x, y := 1, 1
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
c <- x
|
c <- x
|
||||||
x, y = y, x + y
|
x, y = y, x + y
|
||||||
}
|
}
|
||||||
close(c)
|
close(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := make(chan int, 10)
|
c := make(chan int, 10)
|
||||||
go fibonacci(cap(c), c)
|
go fibonacci(cap(c), c)
|
||||||
for i := range c {
|
for i := range c {
|
||||||
fmt.Println(i)
|
fmt.Println(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,13 +149,13 @@ channel通过操作符`<-`来接收和发送数据
|
|||||||
func fibonacci(c, quit chan int) {
|
func fibonacci(c, quit chan int) {
|
||||||
x, y := 1, 1
|
x, y := 1, 1
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case c <- x:
|
case c <- x:
|
||||||
x, y = y, x + y
|
x, y = y, x + y
|
||||||
case <-quit:
|
case <-quit:
|
||||||
fmt.Println("quit")
|
fmt.Println("quit")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,6 +187,3 @@ channel通过操作符`<-`来接收和发送数据
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [interface](<2.6.md>)
|
* 上一章: [interface](<2.6.md>)
|
||||||
* 下一节: [总结](<2.8.md>)
|
* 下一节: [总结](<2.8.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
2.8.md
3
2.8.md
@@ -29,6 +29,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [并发](<2.7.md>)
|
* 上一节: [并发](<2.7.md>)
|
||||||
* 下一章: [Web基础](<3.md>)
|
* 下一章: [Web基础](<3.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
2.md
3
2.md
@@ -25,6 +25,3 @@ Go是一门类似C的编译型语言,但是它的编译速度非常快。这
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [第一章总结](<1.5.md>)
|
* 上一章: [第一章总结](<1.5.md>)
|
||||||
* 下一节: [你好,Go](<2.1.md>)
|
* 下一节: [你好,Go](<2.1.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
7
3.1.md
7
3.1.md
@@ -76,8 +76,8 @@ HTTP协议是无状态的,同一个客户端的这次请求和上次请求是
|
|||||||
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 //客户端能接收的mine
|
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 //客户端能接收的mine
|
||||||
Accept-Encoding:gzip,deflate,sdch //是否支持流压缩
|
Accept-Encoding:gzip,deflate,sdch //是否支持流压缩
|
||||||
Accept-Charset:UTF-8,*;q=0.5 //客户端字符编码集
|
Accept-Charset:UTF-8,*;q=0.5 //客户端字符编码集
|
||||||
//空行,用于分割请求头和消息体
|
//空行,用于分割请求头和消息体
|
||||||
//消息体,请求资源参数,例如POST传递的参数
|
//消息体,请求资源参数,例如POST传递的参数
|
||||||
|
|
||||||
我们通过fiddler抓包可以看到如下请求信息
|
我们通过fiddler抓包可以看到如下请求信息
|
||||||
|
|
||||||
@@ -144,6 +144,3 @@ Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [Web基础](<3.md>)
|
* 上一节: [Web基础](<3.md>)
|
||||||
* 下一节: [GO搭建一个web服务器](<3.2.md>)
|
* 下一节: [GO搭建一个web服务器](<3.2.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
3.2.md
3
3.2.md
@@ -62,6 +62,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [Web工作方式](<3.1.md>)
|
* 上一节: [Web工作方式](<3.1.md>)
|
||||||
* 下一节: [Go如何使得web工作](<3.3.md>)
|
* 下一节: [Go如何使得web工作](<3.3.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
3.3.md
3
3.3.md
@@ -48,6 +48,3 @@ Handler:处理请求和生成返回信息的处理逻辑
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [GO搭建一个简单的web服务](<3.2.md>)
|
* 上一节: [GO搭建一个简单的web服务](<3.2.md>)
|
||||||
* 下一节: [Go的http包详解](<3.4.md>)
|
* 下一节: [Go的http包详解](<3.4.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
87
3.4.md
87
3.4.md
@@ -9,10 +9,10 @@ Go的http有两个核心功能:Conn、ServeMux
|
|||||||
Go在等待客户端请求里面是这样写的:
|
Go在等待客户端请求里面是这样写的:
|
||||||
|
|
||||||
c, err := srv.newConn(rw)
|
c, err := srv.newConn(rw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
go c.serve()
|
go c.serve()
|
||||||
|
|
||||||
这里我们可以看到客户端的每次请求都会创建一个Conn,这个Conn里面保存了该次请求的信息,然后再传递到handler的时候可以读取到相应的header信息,这样保证了每个请求的独立性。
|
这里我们可以看到客户端的每次请求都会创建一个Conn,这个Conn里面保存了该次请求的信息,然后再传递到handler的时候可以读取到相应的header信息,这样保证了每个请求的独立性。
|
||||||
|
|
||||||
@@ -22,21 +22,21 @@ Go在等待客户端请求里面是这样写的:
|
|||||||
它的结构如下:
|
它的结构如下:
|
||||||
|
|
||||||
type ServeMux struct {
|
type ServeMux struct {
|
||||||
mu sync.RWMutex //锁,由于请求设计到并发处理,因此这里需要一个锁机制
|
mu sync.RWMutex //锁,由于请求设计到并发处理,因此这里需要一个锁机制
|
||||||
m map[string]muxEntry // 路由规则,一个string对应一个mux实体,这里的string就是注册的路由表达式
|
m map[string]muxEntry // 路由规则,一个string对应一个mux实体,这里的string就是注册的路由表达式
|
||||||
}
|
}
|
||||||
|
|
||||||
下面看一下muxEntry
|
下面看一下muxEntry
|
||||||
|
|
||||||
type muxEntry struct {
|
type muxEntry struct {
|
||||||
explicit bool // 是否精确匹配
|
explicit bool // 是否精确匹配
|
||||||
h Handler // 这个路由表达式对应哪个handler
|
h Handler // 这个路由表达式对应哪个handler
|
||||||
}
|
}
|
||||||
|
|
||||||
下面看一下handler的定义
|
下面看一下handler的定义
|
||||||
|
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
ServeHTTP(ResponseWriter, *Request) // 路由实现器
|
ServeHTTP(ResponseWriter, *Request) // 路由实现器
|
||||||
}
|
}
|
||||||
|
|
||||||
handler是一个接口,但是前一小节中的`sayhelloName`函数并没有实现ServeHTTP这个接口,为什么能添加呢?原来在http包里面还定义了一个类型`HandlerFunc`,我们定义的函数`sayhelloName`就是这个HandlerFunc调用之后的结果,这个类型默认就实现了ServeHTTP这个接口,即我们调用了HandlerFunc(f),类似强制类型转换f成为handlerFunc类型,这样f就拥有了ServHTTP方法。
|
handler是一个接口,但是前一小节中的`sayhelloName`函数并没有实现ServeHTTP这个接口,为什么能添加呢?原来在http包里面还定义了一个类型`HandlerFunc`,我们定义的函数`sayhelloName`就是这个HandlerFunc调用之后的结果,这个类型默认就实现了ServeHTTP这个接口,即我们调用了HandlerFunc(f),类似强制类型转换f成为handlerFunc类型,这样f就拥有了ServHTTP方法。
|
||||||
@@ -45,7 +45,7 @@ handler是一个接口,但是前一小节中的`sayhelloName`函数并没有
|
|||||||
|
|
||||||
// ServeHTTP calls f(w, r).
|
// ServeHTTP calls f(w, r).
|
||||||
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
|
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
|
||||||
f(w, r)
|
f(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
路由器里面存储好了相应的路由规则之后,那么具体的请求又是怎么分发的呢?
|
路由器里面存储好了相应的路由规则之后,那么具体的请求又是怎么分发的呢?
|
||||||
@@ -55,19 +55,19 @@ handler是一个接口,但是前一小节中的`sayhelloName`函数并没有
|
|||||||
也就是调用对应路由的handler的ServerHTTP接口,那么mux.handler(r)怎么处理的呢?
|
也就是调用对应路由的handler的ServerHTTP接口,那么mux.handler(r)怎么处理的呢?
|
||||||
|
|
||||||
func (mux *ServeMux) handler(r *Request) Handler {
|
func (mux *ServeMux) handler(r *Request) Handler {
|
||||||
mux.mu.RLock()
|
mux.mu.RLock()
|
||||||
defer mux.mu.RUnlock()
|
defer mux.mu.RUnlock()
|
||||||
|
|
||||||
// Host-specific pattern takes precedence over generic ones
|
// Host-specific pattern takes precedence over generic ones
|
||||||
h := mux.match(r.Host + r.URL.Path)
|
h := mux.match(r.Host + r.URL.Path)
|
||||||
if h == nil {
|
if h == nil {
|
||||||
h = mux.match(r.URL.Path)
|
h = mux.match(r.URL.Path)
|
||||||
}
|
}
|
||||||
if h == nil {
|
if h == nil {
|
||||||
h = NotFoundHandler()
|
h = NotFoundHandler()
|
||||||
}
|
}
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
原来他是根据用户请求的URL和路由器里面存储的map去匹配的,当匹配到之后返回存储的handler,调用这个handler的ServHTTP接口就可以执行到相应的函数了。
|
原来他是根据用户请求的URL和路由器里面存储的map去匹配的,当匹配到之后返回存储的handler,调用这个handler的ServHTTP接口就可以执行到相应的函数了。
|
||||||
|
|
||||||
@@ -109,52 +109,49 @@ handler是一个接口,但是前一小节中的`sayhelloName`函数并没有
|
|||||||
|
|
||||||
- 首先调用Http.HandleFunc
|
- 首先调用Http.HandleFunc
|
||||||
|
|
||||||
按顺序做了几件事:
|
按顺序做了几件事:
|
||||||
|
|
||||||
1 调用了DefaultServerMux的HandleFunc
|
1. 调用了DefaultServerMux的HandleFunc
|
||||||
|
|
||||||
2 调用了DefaultServerMux的Handle
|
2. 调用了DefaultServerMux的Handle
|
||||||
|
|
||||||
3 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则
|
3. 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则
|
||||||
|
|
||||||
- 其次调用http.ListenAndServe(":9090", nil)
|
- 其次调用http.ListenAndServe(":9090", nil)
|
||||||
|
|
||||||
按顺序做了几件事情:
|
按顺序做了几件事情:
|
||||||
|
|
||||||
1 实例化Server
|
1. 实例化Server
|
||||||
|
|
||||||
2 调用Server的ListenAndServe()
|
2. 调用Server的ListenAndServe()
|
||||||
|
|
||||||
3 调用net.Listen("tcp", addr)监听端口
|
3. 调用net.Listen("tcp", addr)监听端口
|
||||||
|
|
||||||
4 启动一个for循环,在循环体中Accept请求
|
4. 启动一个for循环,在循环体中Accept请求
|
||||||
|
|
||||||
5 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
|
5. 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
|
||||||
|
|
||||||
6 读取每个请求的内容w, err := c.readRequest()
|
6. 读取每个请求的内容w, err := c.readRequest()
|
||||||
|
|
||||||
7 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux
|
7. 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux
|
||||||
|
|
||||||
8 调用handler的ServeHttp
|
8. 调用handler的ServeHttp
|
||||||
|
|
||||||
9 在这个例子中,下面就进入到DefaultServerMux.ServeHttp
|
9. 在这个例子中,下面就进入到DefaultServerMux.ServeHttp
|
||||||
|
|
||||||
10 根据request选择handler,并且进入到这个handler的ServeHTTP
|
10. 根据request选择handler,并且进入到这个handler的ServeHTTP
|
||||||
|
|
||||||
mux.handler(r).ServeHTTP(w, r)
|
mux.handler(r).ServeHTTP(w, r)
|
||||||
|
|
||||||
11 选择handler:
|
11 选择handler:
|
||||||
|
|
||||||
A 判断是否有路由能满足这个request(循环遍历ServerMux的muxEntry)
|
A. 判断是否有路由能满足这个request(循环遍历ServerMux的muxEntry)
|
||||||
|
|
||||||
B 如果有路由满足,调用这个路由handler的ServeHttp
|
B. 如果有路由满足,调用这个路由handler的ServeHttp
|
||||||
|
|
||||||
C 如果没有路由满足,调用NotFoundHandler的ServeHttp
|
C. 如果没有路由满足,调用NotFoundHandler的ServeHttp
|
||||||
|
|
||||||
## links
|
## links
|
||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [Go如何使得web工作](<3.3.md>)
|
* 上一节: [Go如何使得web工作](<3.3.md>)
|
||||||
* 下一节: [小结](<3.5.md>)
|
* 下一节: [小结](<3.5.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
3.5.md
3
3.5.md
@@ -7,6 +7,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [Go的http包详解](<3.4.md>)
|
* 上一节: [Go的http包详解](<3.4.md>)
|
||||||
* 下一章: [表单](<4.md>)
|
* 下一章: [表单](<4.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
3.md
3
3.md
@@ -13,6 +13,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [第二章总结](<2.8.md>)
|
* 上一章: [第二章总结](<2.8.md>)
|
||||||
* 下一节: [web工作方式](<3.1.md>)
|
* 下一节: [web工作方式](<3.1.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
9
4.1.md
9
4.1.md
@@ -8,9 +8,9 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<form action="http://127.0.0.1:9090/login" method="post">
|
<form action="http://127.0.0.1:9090/login" method="post">
|
||||||
用户名:<input type="text" name="username">
|
用户名:<input type="text" name="username">
|
||||||
密码:<input type="password" name="password">
|
密码:<input type="password" name="password">
|
||||||
<input type="submit" value="登陆">
|
<input type="submit" value="登陆">
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -99,6 +99,3 @@ Tips: Request请求也提供了FormValue()函数来获取用户提交的参数
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [表单](<4.md>)
|
* 上一节: [表单](<4.md>)
|
||||||
* 下一节: [验证表单的输入](<4.2.md>)
|
* 下一节: [验证表单的输入](<4.2.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
4.2.md
3
4.2.md
@@ -161,6 +161,3 @@ Go里面提供了一个time的处理包,我们可以把用户的输入年月
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [处理表单的输入](<4.1.md>)
|
* 上一节: [处理表单的输入](<4.1.md>)
|
||||||
* 下一节: [预防跨站脚本](<4.3.md>)
|
* 下一节: [预防跨站脚本](<4.3.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
4.3.md
3
4.3.md
@@ -64,6 +64,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [验证的输入](<4.2.md>)
|
* 上一节: [验证的输入](<4.2.md>)
|
||||||
* 下一节: [防止多次递交表单](<4.4.md>)
|
* 下一节: [防止多次递交表单](<4.4.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
4.4.md
3
4.4.md
@@ -51,6 +51,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [预防跨站脚本](<4.3.md>)
|
* 上一节: [预防跨站脚本](<4.3.md>)
|
||||||
* 下一节: [处理文件上传](<4.5.md>)
|
* 下一节: [处理文件上传](<4.5.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
11
4.5.md
11
4.5.md
@@ -69,9 +69,9 @@
|
|||||||
文件handler是multipart.FileHeader,里面存储了如下结构信息
|
文件handler是multipart.FileHeader,里面存储了如下结构信息
|
||||||
|
|
||||||
type FileHeader struct {
|
type FileHeader struct {
|
||||||
Filename string
|
Filename string
|
||||||
Header textproto.MIMEHeader
|
Header textproto.MIMEHeader
|
||||||
// contains filtered or unexported fields
|
// contains filtered or unexported fields
|
||||||
}
|
}
|
||||||
|
|
||||||
我们通过上面的实例代码打印出来上传文件的信息如下
|
我们通过上面的实例代码打印出来上传文件的信息如下
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
//iocopy
|
//iocopy
|
||||||
_, err = io.Copy(fileWriter, fh)
|
_, err = io.Copy(fileWriter, fh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
contentType := bodyWriter.FormDataContentType()
|
contentType := bodyWriter.FormDataContentType()
|
||||||
@@ -152,6 +152,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [防止多次递交表单](<4.4.md>)
|
* 上一节: [防止多次递交表单](<4.4.md>)
|
||||||
* 下一节: [小结](<4.6.md>)
|
* 下一节: [小结](<4.6.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
4.6.md
3
4.6.md
@@ -7,6 +7,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [处理文件上传](<4.5.md>)
|
* 上一节: [处理文件上传](<4.5.md>)
|
||||||
* 下一章: [访问数据库](<5.md>)
|
* 下一章: [访问数据库](<5.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
5
4.md
5
4.md
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<form>
|
<form>
|
||||||
...
|
...
|
||||||
input 元素
|
input 元素
|
||||||
...
|
...
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@@ -29,6 +29,3 @@ HTTP协议是一种无状态的协议,那么如何才能辨别是否是同一
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [第三章总结](<3.5.md>)
|
* 上一章: [第三章总结](<3.5.md>)
|
||||||
* 下一节: [处理表单的输入](<4.1.md>)
|
* 下一节: [处理表单的输入](<4.1.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
50
5.1.md
50
5.1.md
@@ -1,4 +1,4 @@
|
|||||||
# 5.1 database/sql接口
|
h# 5.1 database/sql接口
|
||||||
Go和PHP不同的地方是,他没有官方提供数据库驱动,而是为开发数据库驱动定义了一些标准接口,第三方用户可以根据定义的接口来开发相应的数据库驱动,这样做有一个好处,我们按照标准接口开发的代码, 在需要迁移数据库时,不需要任何修改。那么Go都定义了那些标准接口呢?让我们来详细的分析一下
|
Go和PHP不同的地方是,他没有官方提供数据库驱动,而是为开发数据库驱动定义了一些标准接口,第三方用户可以根据定义的接口来开发相应的数据库驱动,这样做有一个好处,我们按照标准接口开发的代码, 在需要迁移数据库时,不需要任何修改。那么Go都定义了那些标准接口呢?让我们来详细的分析一下
|
||||||
|
|
||||||
## sql.Register
|
## sql.Register
|
||||||
@@ -28,17 +28,17 @@ Go和PHP不同的地方是,他没有官方提供数据库驱动,而是为开
|
|||||||
因此通过database/sql的注册函数可以同时注册多个数据库驱动,只要不重复。
|
因此通过database/sql的注册函数可以同时注册多个数据库驱动,只要不重复。
|
||||||
|
|
||||||
>在我们使用database/sql接口和第三方库的时候经常看到如下的import
|
>在我们使用database/sql接口和第三方库的时候经常看到如下的import
|
||||||
>
|
|
||||||
> "database/sql"
|
"database/sql"
|
||||||
> _ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
>
|
|
||||||
>新手都会被这个`_`所迷惑,其实这个就是Go设计的巧妙之处,我们在变量赋值的时候经常看到这个,它是用来忽略变量的占位符,那么这个包引入也是,这儿的意思是引入此包而不直接使用这个包中定义的函数,变量等资源,我们在2.3流程和函数里面介绍过init函数的初始化过程,包在引入的时候会去调用包的init函数以完成对包的初始化,因此我们引入上面的数据库驱动包之后会去调用init函数,然后在init函数里面注册了这个数据库驱动,这样我们就可以在接下来的代码中直接使用这个数据库驱动了。
|
>新手都会被这个`_`所迷惑,其实这个就是Go设计的巧妙之处,我们在变量赋值的时候经常看到这个,它是用来忽略变量的占位符,那么这个包引入也是,这儿的意思是引入此包而不直接使用这个包中定义的函数,变量等资源,我们在2.3流程和函数里面介绍过init函数的初始化过程,包在引入的时候会去调用包的init函数以完成对包的初始化,因此我们引入上面的数据库驱动包之后会去调用init函数,然后在init函数里面注册了这个数据库驱动,这样我们就可以在接下来的代码中直接使用这个数据库驱动了。
|
||||||
|
|
||||||
## driver.Driver
|
## driver.Driver
|
||||||
Driver是一个数据库驱动的接口,他定义了一个method: Open(name string),这个方法返回一个数据库的Conn接口。
|
Driver是一个数据库驱动的接口,他定义了一个method: Open(name string),这个方法返回一个数据库的Conn接口。
|
||||||
|
|
||||||
type Driver interface {
|
type Driver interface {
|
||||||
Open(name string) (Conn, error)
|
Open(name string) (Conn, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
返回的Conn只能用来进行一次goroutine的操作,也就是说不能把这个Conn应用于Go的多个goroutine里面。如下代码会出现错误
|
返回的Conn只能用来进行一次goroutine的操作,也就是说不能把这个Conn应用于Go的多个goroutine里面。如下代码会出现错误
|
||||||
@@ -56,9 +56,9 @@ Driver是一个数据库驱动的接口,他定义了一个method: Open(name
|
|||||||
Conn是一个数据库连接的接口定义,他定义了一系列方法,这个Conn只能应用在一个goroutine里面,不能使用在多个goroutine里面,详情请参考上面的说明。
|
Conn是一个数据库连接的接口定义,他定义了一系列方法,这个Conn只能应用在一个goroutine里面,不能使用在多个goroutine里面,详情请参考上面的说明。
|
||||||
|
|
||||||
type Conn interface {
|
type Conn interface {
|
||||||
Prepare(query string) (Stmt, error)
|
Prepare(query string) (Stmt, error)
|
||||||
Close() error
|
Close() error
|
||||||
Begin() (Tx, error)
|
Begin() (Tx, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
Prepare函数返回与当前连接相关的准备好Sql语句的状态,可以进行查询、删除等操作。
|
Prepare函数返回与当前连接相关的准备好Sql语句的状态,可以进行查询、删除等操作。
|
||||||
@@ -71,10 +71,10 @@ Begin函数返回一个代表事务处理的Tx,通过它你可以进行查询,
|
|||||||
Stmt是一种准备好的状态,和Conn相关联,而且是只能应用于一个goroutine中,不能应用在多个goroutine中。
|
Stmt是一种准备好的状态,和Conn相关联,而且是只能应用于一个goroutine中,不能应用在多个goroutine中。
|
||||||
|
|
||||||
type Stmt interface {
|
type Stmt interface {
|
||||||
Close() error
|
Close() error
|
||||||
NumInput() int
|
NumInput() int
|
||||||
Exec(args []Value) (Result, error)
|
Exec(args []Value) (Result, error)
|
||||||
Query(args []Value) (Rows, error)
|
Query(args []Value) (Rows, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
Close函数关闭当前的链接状态,但是如果当前正在执行query,query还是有效返回rows数据。
|
Close函数关闭当前的链接状态,但是如果当前正在执行query,query还是有效返回rows数据。
|
||||||
@@ -91,7 +91,7 @@ Query函数执行Prepare准备好的sql,传入需要的参数执行select操
|
|||||||
|
|
||||||
type Tx interface {
|
type Tx interface {
|
||||||
Commit() error
|
Commit() error
|
||||||
Rollback() error
|
Rollback() error
|
||||||
}
|
}
|
||||||
|
|
||||||
这两个函数一个用来递交一个事务,一个用来回滚事务。
|
这两个函数一个用来递交一个事务,一个用来回滚事务。
|
||||||
@@ -100,7 +100,7 @@ Query函数执行Prepare准备好的sql,传入需要的参数执行select操
|
|||||||
这是一个Conn可选择实现的接口
|
这是一个Conn可选择实现的接口
|
||||||
|
|
||||||
type Execer interface {
|
type Execer interface {
|
||||||
Exec(query string, args []Value) (Result, error)
|
Exec(query string, args []Value) (Result, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
如果这个接口没有定义,那么在调用DB.Exec,就会首先调用Prepare返回Stmt,然后执行Stmt的Exec,然后关闭Stmt。
|
如果这个接口没有定义,那么在调用DB.Exec,就会首先调用Prepare返回Stmt,然后执行Stmt的Exec,然后关闭Stmt。
|
||||||
@@ -109,8 +109,8 @@ Query函数执行Prepare准备好的sql,传入需要的参数执行select操
|
|||||||
这个是执行Update/Insert等操作返回的结果接口定义
|
这个是执行Update/Insert等操作返回的结果接口定义
|
||||||
|
|
||||||
type Result interface {
|
type Result interface {
|
||||||
LastInsertId() (int64, error)
|
LastInsertId() (int64, error)
|
||||||
RowsAffected() (int64, error)
|
RowsAffected() (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
LastInsertId函数返回由数据库执行插入操作得到的自动增长ID号。
|
LastInsertId函数返回由数据库执行插入操作得到的自动增长ID号。
|
||||||
@@ -121,12 +121,9 @@ RowsAffected函数返回query操作影响的数据条目数。
|
|||||||
Rows是执行查询返回的结果集接口定义
|
Rows是执行查询返回的结果集接口定义
|
||||||
|
|
||||||
type Rows interface {
|
type Rows interface {
|
||||||
|
Columns() []string
|
||||||
Columns() []string
|
Close() error
|
||||||
|
Next(dest []Value) error
|
||||||
Close() error
|
|
||||||
|
|
||||||
Next(dest []Value) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Columns函数返回查询数据库表的字段信息,这个返回的slice和你sql查询的字段一一对应,而不是返回整个表的所有字段。
|
Columns函数返回查询数据库表的字段信息,这个返回的slice和你sql查询的字段一一对应,而不是返回整个表的所有字段。
|
||||||
@@ -163,7 +160,7 @@ Value的值必须所有的驱动里面控制的,Value要么是nil,要么是
|
|||||||
ValueConverter接口定义了如何把一个普通的值转化成driver.Value的接口
|
ValueConverter接口定义了如何把一个普通的值转化成driver.Value的接口
|
||||||
|
|
||||||
type ValueConverter interface {
|
type ValueConverter interface {
|
||||||
ConvertValue(v interface{}) (Value, error)
|
ConvertValue(v interface{}) (Value, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
在开发的数据库驱动包里面实现这个接口的函数在很多地方会使用到,这个ValueConverter有很多好处:
|
在开发的数据库驱动包里面实现这个接口的函数在很多地方会使用到,这个ValueConverter有很多好处:
|
||||||
@@ -176,7 +173,7 @@ ValueConverter接口定义了如何把一个普通的值转化成driver.Value的
|
|||||||
Valuer接口定义了返回一个driver.Value的方式
|
Valuer接口定义了返回一个driver.Value的方式
|
||||||
|
|
||||||
type Valuer interface {
|
type Valuer interface {
|
||||||
Value() (Value, error)
|
Value() (Value, error)
|
||||||
}
|
}
|
||||||
很多类型都实现了这个Value方法,用来自身与driver.Value的转化。
|
很多类型都实现了这个Value方法,用来自身与driver.Value的转化。
|
||||||
|
|
||||||
@@ -200,6 +197,3 @@ database/sql在database/sql/driver提供的接口基础上定义了一些更高
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [访问数据库](<5.md>)
|
* 上一节: [访问数据库](<5.md>)
|
||||||
* 下一节: [使用MySQL数据库](<5.2.md>)
|
* 下一节: [使用MySQL数据库](<5.2.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
5.2.md
3
5.2.md
@@ -133,6 +133,3 @@ stmt.Exec()函数用来执行stmt准备好的SQL语句
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [database/sql接口](<5.1.md>)
|
* 上一节: [database/sql接口](<5.1.md>)
|
||||||
* 下一节: [使用SQLite数据库](<5.3.md>)
|
* 下一节: [使用SQLite数据库](<5.3.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
5.3.md
3
5.3.md
@@ -114,6 +114,3 @@ Go支持sqlite的驱动也比较多,但是好多都是不支持database/sql接
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [使用MySQL数据库](<5.2.md>)
|
* 上一节: [使用MySQL数据库](<5.2.md>)
|
||||||
* 下一节: [使用PostgreSQL数据库](<5.4.md>)
|
* 下一节: [使用PostgreSQL数据库](<5.4.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
19
5.4.md
19
5.4.md
@@ -20,19 +20,19 @@ Go实现的支持PostgreSQL的驱动也很多,因为国外很多人在开发
|
|||||||
|
|
||||||
CREATE TABLE userinfo
|
CREATE TABLE userinfo
|
||||||
(
|
(
|
||||||
uid serial NOT NULL,
|
uid serial NOT NULL,
|
||||||
username character varying(100) NOT NULL,
|
username character varying(100) NOT NULL,
|
||||||
departname character varying(500) NOT NULL,
|
departname character varying(500) NOT NULL,
|
||||||
Created date,
|
Created date,
|
||||||
CONSTRAINT userinfo_pkey PRIMARY KEY (uid)
|
CONSTRAINT userinfo_pkey PRIMARY KEY (uid)
|
||||||
)
|
)
|
||||||
WITH (OIDS=FALSE);
|
WITH (OIDS=FALSE);
|
||||||
|
|
||||||
CREATE TABLE userdeatail
|
CREATE TABLE userdeatail
|
||||||
(
|
(
|
||||||
uid integer,
|
uid integer,
|
||||||
intro character varying(100),
|
intro character varying(100),
|
||||||
profile character varying(100)
|
profile character varying(100)
|
||||||
)
|
)
|
||||||
WITH(OIDS=FALSE);
|
WITH(OIDS=FALSE);
|
||||||
|
|
||||||
@@ -120,6 +120,3 @@ package main
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [使用SQLite数据库](<5.3.md>)
|
* 上一节: [使用SQLite数据库](<5.3.md>)
|
||||||
* 下一节: [使用beedb库进行ORM开发](<5.5.md>)
|
* 下一节: [使用beedb库进行ORM开发](<5.5.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
40
5.5.md
40
5.5.md
@@ -28,14 +28,13 @@ beedb支持go get方式安装,是完全按照Go Style的方式来实现的。
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"github.com/astaxie/beedb"
|
"github.com/astaxie/beedb"
|
||||||
_ "github.com/ziutek/mymysql/godrv"
|
_ "github.com/ziutek/mymysql/godrv"
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
导入必须的package之后,我们需要打开到数据库的链接,然后创建一个beedb对象(以MySQL为例),如下所示
|
导入必须的package之后,我们需要打开到数据库的链接,然后创建一个beedb对象(以MySQL为例),如下所示
|
||||||
|
|
||||||
db, err := sql.Open("mymysql", "test/xiemengjun/123456")
|
db, err := sql.Open("mymysql", "test/xiemengjun/123456")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
orm := beedb.New(db)
|
orm := beedb.New(db)
|
||||||
|
|
||||||
@@ -56,10 +55,10 @@ beedb的New函数实际上应该有两个参数,第一个参数标准接口的
|
|||||||
接下来我们的例子采用前面的数据库表Userinfo,现在我们建立相应的struct
|
接下来我们的例子采用前面的数据库表Userinfo,现在我们建立相应的struct
|
||||||
|
|
||||||
type Userinfo struct {
|
type Userinfo struct {
|
||||||
Uid int `PK` //如果表的主键不是id,那么需要加上pk注释,显式的说这个字段是主键
|
Uid int `PK` //如果表的主键不是id,那么需要加上pk注释,显式的说这个字段是主键
|
||||||
Username string
|
Username string
|
||||||
Departname string
|
Departname string
|
||||||
Created time.Time
|
Created time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
>注意一点,beedb针对驼峰命名会自动帮你转化成下划线字段,例如你定义了Struct名字为`UserInfo`,那么转化成底层实现的时候是`user_info`,字段命名也遵循该规则。
|
>注意一点,beedb针对驼峰命名会自动帮你转化成下划线字段,例如你定义了Struct名字为`UserInfo`,那么转化成底层实现的时候是`user_info`,字段命名也遵循该规则。
|
||||||
@@ -228,30 +227,23 @@ Having:用来指定having执行的时候的条件
|
|||||||
- 实现关联数据库设计,支持一对一,一对多,多对多的实现,示例代码如下:
|
- 实现关联数据库设计,支持一对一,一对多,多对多的实现,示例代码如下:
|
||||||
|
|
||||||
|
|
||||||
type Profile struct{
|
type Profile struct{
|
||||||
Nickname string
|
Nickname string
|
||||||
Mobile string
|
Mobile string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Userinfo struct {
|
|
||||||
Uid int `PK`
|
|
||||||
Username string
|
|
||||||
Departname string
|
|
||||||
Created time.Time
|
|
||||||
Profile `HasOne`
|
|
||||||
}
|
|
||||||
|
|
||||||
|
type Userinfo struct {
|
||||||
|
Uid int `PK`
|
||||||
|
Username string
|
||||||
|
Departname string
|
||||||
|
Created time.Time
|
||||||
|
Profile `HasOne`
|
||||||
|
}
|
||||||
|
|
||||||
- 自动建库建表建索引
|
- 自动建库建表建索引
|
||||||
- 实现连接池的实现,采用goroutine
|
- 实现连接池的实现,采用goroutine
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## links
|
## links
|
||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [使用PostgreSQL数据库](<5.4.md>)
|
* 上一节: [使用PostgreSQL数据库](<5.4.md>)
|
||||||
* 下一节: [NOSQL数据库操作](<5.6.md>)
|
* 下一节: [NOSQL数据库操作](<5.6.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
66
5.6.md
66
5.6.md
@@ -41,11 +41,11 @@ https://github.com/astaxie/goredis
|
|||||||
var client goredis.Client
|
var client goredis.Client
|
||||||
vals := []string{"a", "b", "c", "d", "e"}
|
vals := []string{"a", "b", "c", "d", "e"}
|
||||||
for _, v := range vals {
|
for _, v := range vals {
|
||||||
client.Rpush("l", []byte(v))
|
client.Rpush("l", []byte(v))
|
||||||
}
|
}
|
||||||
dbvals,_ := client.Lrange("l", 0, 4)
|
dbvals,_ := client.Lrange("l", 0, 4)
|
||||||
for i, v := range dbvals {
|
for i, v := range dbvals {
|
||||||
println(i,":",string(v))
|
println(i,":",string(v))
|
||||||
}
|
}
|
||||||
client.Del("l")
|
client.Del("l")
|
||||||
}
|
}
|
||||||
@@ -64,44 +64,43 @@ MongoDB是一个高性能,开源,无模式的文档型数据库,是一个
|
|||||||
|
|
||||||
下面我将演示如果通过Go来操作mongoDB:
|
下面我将演示如果通过Go来操作mongoDB:
|
||||||
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"labix.org/v2/mgo"
|
"labix.org/v2/mgo"
|
||||||
"labix.org/v2/mgo/bson"
|
"labix.org/v2/mgo/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Person struct {
|
type Person struct {
|
||||||
Name string
|
Name string
|
||||||
Phone string
|
Phone string
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
session, err := mgo.Dial("server1.example.com,server2.example.com")
|
session, err := mgo.Dial("server1.example.com,server2.example.com")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
defer session.Close()
|
defer session.Close()
|
||||||
|
|
||||||
session.SetMode(mgo.Monotonic, true)
|
session.SetMode(mgo.Monotonic, true)
|
||||||
|
|
||||||
c := session.DB("test").C("people")
|
c := session.DB("test").C("people")
|
||||||
err = c.Insert(&Person{"Ale", "+55 53 8116 9639"},
|
err = c.Insert(&Person{"Ale", "+55 53 8116 9639"},
|
||||||
&Person{"Cla", "+55 53 8402 8510"})
|
&Person{"Cla", "+55 53 8402 8510"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
result := Person{}
|
result := Person{}
|
||||||
err = c.Find(bson.M{"name": "Ale"}).One(&result)
|
err = c.Find(bson.M{"name": "Ale"}).One(&result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Phone:", result.Phone)
|
fmt.Println("Phone:", result.Phone)
|
||||||
}
|
}
|
||||||
|
|
||||||
我们可以看出来mgo的操作方式和beedb的操作方式几乎类似,都是基于strcut的操作方式,这个就是Go Style。
|
我们可以看出来mgo的操作方式和beedb的操作方式几乎类似,都是基于strcut的操作方式,这个就是Go Style。
|
||||||
|
|
||||||
@@ -111,6 +110,3 @@ MongoDB是一个高性能,开源,无模式的文档型数据库,是一个
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [使用beedb库进行ORM开发](<5.5.md>)
|
* 上一节: [使用beedb库进行ORM开发](<5.5.md>)
|
||||||
* 下一节: [小结](<5.7.md>)
|
* 下一节: [小结](<5.7.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
5.7.md
3
5.7.md
@@ -7,6 +7,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [NOSQL数据库操作](<5.6.md>)
|
* 上一节: [NOSQL数据库操作](<5.6.md>)
|
||||||
* 下一章: [session和数据存储](<6.md>)
|
* 下一章: [session和数据存储](<6.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
5.md
3
5.md
@@ -18,6 +18,3 @@ Go没有内置的驱动支持任何的数据库,但是Go定义了database/sql
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [第四章总结](<4.6.md>)
|
* 上一章: [第四章总结](<4.6.md>)
|
||||||
* 下一节: [database/sql接口](<5.1.md>)
|
* 下一节: [database/sql接口](<5.1.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
31
6.1.md
31
6.1.md
@@ -35,21 +35,21 @@ Go语言中通过net/http包中的SetCookie来设置:
|
|||||||
w表示需要写入的response,cookie是一个struct,让我们来看一下cookie对象是怎么样的
|
w表示需要写入的response,cookie是一个struct,让我们来看一下cookie对象是怎么样的
|
||||||
|
|
||||||
type Cookie struct {
|
type Cookie struct {
|
||||||
Name string
|
Name string
|
||||||
Value string
|
Value string
|
||||||
Path string
|
Path string
|
||||||
Domain string
|
Domain string
|
||||||
Expires time.Time
|
Expires time.Time
|
||||||
RawExpires string
|
RawExpires string
|
||||||
|
|
||||||
// MaxAge=0 means no 'Max-Age' attribute specified.
|
// MaxAge=0 means no 'Max-Age' attribute specified.
|
||||||
// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
|
// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
|
||||||
// MaxAge>0 means Max-Age attribute present and given in seconds
|
// MaxAge>0 means Max-Age attribute present and given in seconds
|
||||||
MaxAge int
|
MaxAge int
|
||||||
Secure bool
|
Secure bool
|
||||||
HttpOnly bool
|
HttpOnly bool
|
||||||
Raw string
|
Raw string
|
||||||
Unparsed []string // Raw text of unparsed attribute-value pairs
|
Unparsed []string // Raw text of unparsed attribute-value pairs
|
||||||
}
|
}
|
||||||
|
|
||||||
我们来看一个例子,如何设置cookie
|
我们来看一个例子,如何设置cookie
|
||||||
@@ -97,6 +97,3 @@ session机制本身并不复杂,然而其实现和配置上的灵活性却使
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [session和数据存储](<6.md>)
|
* 上一节: [session和数据存储](<6.md>)
|
||||||
* 下一节: [Go如何使用session](<6.2.md>)
|
* 下一节: [Go如何使用session](<6.2.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
5
6.2.md
5
6.2.md
@@ -194,7 +194,7 @@ SessionStart函数返回的是一个满足Session接口的变量,那么我们
|
|||||||
我们来看一下Session管理器如何来管理销毁,只要我们在Main启动的时候启动:
|
我们来看一下Session管理器如何来管理销毁,只要我们在Main启动的时候启动:
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
go globalSessions.GC()
|
go globalSessions.GC()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (manager *Manager) GC() {
|
func (manager *Manager) GC() {
|
||||||
@@ -213,6 +213,3 @@ SessionStart函数返回的是一个满足Session接口的变量,那么我们
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [session和cookie](<6.1.md>)
|
* 上一节: [session和cookie](<6.1.md>)
|
||||||
* 下一节: [session存储](<6.3.md>)
|
* 下一节: [session存储](<6.3.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
6.3.md
3
6.3.md
@@ -135,6 +135,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [Go如何使用session](<6.2.md>)
|
* 上一节: [Go如何使用session](<6.2.md>)
|
||||||
* 下一节: [预防session劫持](<6.4.md>)
|
* 下一节: [预防session劫持](<6.4.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
6.4.md
3
6.4.md
@@ -79,6 +79,3 @@ session启动后,我们设置了一个值,用于记录生成sessionID的时
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [session存储](<6.3.md>)
|
* 上一节: [session存储](<6.3.md>)
|
||||||
* 下一节: [小结](<6.5.md>)
|
* 下一节: [小结](<6.5.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
6.5.md
3
6.5.md
@@ -4,6 +4,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [session存储](<6.4.md>)
|
* 上一节: [session存储](<6.4.md>)
|
||||||
* 下一章: [文本处理](<7.md>)
|
* 下一章: [文本处理](<7.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
6.md
3
6.md
@@ -14,6 +14,3 @@ Web开发中一个很重要的议题就是如何做好用户的整个浏览过
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [第五章总结](<5.7.md>)
|
* 上一章: [第五章总结](<5.7.md>)
|
||||||
* 下一节: [session和cookie](<6.1.md>)
|
* 下一节: [session和cookie](<6.1.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
47
7.1.md
47
7.1.md
@@ -99,14 +99,14 @@ Go语言的反射机制,可以利用这些tag信息来将来自XML文件中的
|
|||||||
|
|
||||||
- 如果struct的一个字段是string或者[]byte类型且它的tag含有`",innerxml"`,Unmarshal将会将此字段所对应的元素内所有内嵌的原始xml累加到此字段上,如上面例子Description定义。最后的输出是
|
- 如果struct的一个字段是string或者[]byte类型且它的tag含有`",innerxml"`,Unmarshal将会将此字段所对应的元素内所有内嵌的原始xml累加到此字段上,如上面例子Description定义。最后的输出是
|
||||||
|
|
||||||
<server>
|
<server>
|
||||||
<serverName>Shanghai_VPN</serverName>
|
<serverName>Shanghai_VPN</serverName>
|
||||||
<serverIP>127.0.0.1</serverIP>
|
<serverIP>127.0.0.1</serverIP>
|
||||||
</server>
|
</server>
|
||||||
<server>
|
<server>
|
||||||
<serverName>Beijing_VPN</serverName>
|
<serverName>Beijing_VPN</serverName>
|
||||||
<serverIP>127.0.0.2</serverIP>
|
<serverIP>127.0.0.2</serverIP>
|
||||||
</server>
|
</server>
|
||||||
|
|
||||||
- 如果struct中有一个叫做XMLName,且类型为xml.Name字段,那么在解析的时候就会保存这个element的名字到该字段,如上面例子中的servers。
|
- 如果struct中有一个叫做XMLName,且类型为xml.Name字段,那么在解析的时候就会保存这个element的名字到该字段,如上面例子中的servers。
|
||||||
- 如果某个struct字段的tag定义中含有XML结构中element的名称,那么解析的时候就会把相应的element值赋值给该字段,如上servername和serverip定义。
|
- 如果某个struct字段的tag定义中含有XML结构中element的名称,那么解析的时候就会把相应的element值赋值给该字段,如上servername和serverip定义。
|
||||||
@@ -165,14 +165,14 @@ Go语言的反射机制,可以利用这些tag信息来将来自XML文件中的
|
|||||||
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<servers version="1">
|
<servers version="1">
|
||||||
<server>
|
<server>
|
||||||
<serverName>Shanghai_VPN</serverName>
|
<serverName>Shanghai_VPN</serverName>
|
||||||
<serverIP>127.0.0.1</serverIP>
|
<serverIP>127.0.0.1</serverIP>
|
||||||
</server>
|
</server>
|
||||||
<server>
|
<server>
|
||||||
<serverName>Beijing_VPN</serverName>
|
<serverName>Beijing_VPN</serverName>
|
||||||
<serverIP>127.0.0.2</serverIP>
|
<serverIP>127.0.0.2</serverIP>
|
||||||
</server>
|
</server>
|
||||||
</servers>
|
</servers>
|
||||||
|
|
||||||
和我们之前定义的文件的格式一模一样,之所以会有`os.Stdout.Write([]byte(xml.Header))` 这句代码的出现,是因为`xml.MarshalIndent`或者`xml.Marshal`输出的信息都是不带XML头的,为了生成正确的xml文件,我们使用了xml包预定义的Header变量。
|
和我们之前定义的文件的格式一模一样,之所以会有`os.Stdout.Write([]byte(xml.Header))` 这句代码的出现,是因为`xml.MarshalIndent`或者`xml.Marshal`输出的信息都是不带XML头的,为了生成正确的xml文件,我们使用了xml包预定义的Header变量。
|
||||||
@@ -204,13 +204,13 @@ Go语言的反射机制,可以利用这些tag信息来将来自XML文件中的
|
|||||||
- tag中含有`"omitempty"`,如果该字段的值为空值那么该字段就不会被输出到XML,空值包括:false、0、nil指针或nil接口,任何长度为0的array, slice, map或者string
|
- tag中含有`"omitempty"`,如果该字段的值为空值那么该字段就不会被输出到XML,空值包括:false、0、nil指针或nil接口,任何长度为0的array, slice, map或者string
|
||||||
- tag中含有`"a>b>c"`,那么就会循环输出三个元素a包含b,b包含c,例如如下代码就会输出
|
- tag中含有`"a>b>c"`,那么就会循环输出三个元素a包含b,b包含c,例如如下代码就会输出
|
||||||
|
|
||||||
FirstName string `xml:"name>first"`
|
FirstName string `xml:"name>first"`
|
||||||
LastName string `xml:"name>last"`
|
LastName string `xml:"name>last"`
|
||||||
|
|
||||||
<name>
|
<name>
|
||||||
<first>Asta</first>
|
<first>Asta</first>
|
||||||
<last>Xie</last>
|
<last>Xie</last>
|
||||||
</name>
|
</name>
|
||||||
|
|
||||||
|
|
||||||
上面我们介绍了如何使用Go语言的xml包来编/解码XML文件,重要的一点是对XML的所有操作都是通过struct tag来实现的,所以学会对struct tag的运用变得非常重要,在文章中我们简要的列举了如何定义tag。更多内容或tag定义请参看相应的官方资料。
|
上面我们介绍了如何使用Go语言的xml包来编/解码XML文件,重要的一点是对XML的所有操作都是通过struct tag来实现的,所以学会对struct tag的运用变得非常重要,在文章中我们简要的列举了如何定义tag。更多内容或tag定义请参看相应的官方资料。
|
||||||
@@ -219,6 +219,3 @@ Go语言的反射机制,可以利用这些tag信息来将来自XML文件中的
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [文本处理](<7.md>)
|
* 上一节: [文本处理](<7.md>)
|
||||||
* 下一节: [Json处理](<7.2.md>)
|
* 下一节: [Json处理](<7.2.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
53
7.2.md
53
7.2.md
@@ -58,23 +58,23 @@ JSON(Javascript Object Notation)是一种轻量级的数据交换语言,
|
|||||||
|
|
||||||
现在我们假设有如下的JSON数据
|
现在我们假设有如下的JSON数据
|
||||||
|
|
||||||
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
|
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
|
||||||
|
|
||||||
如果在我们不知道他的结构的情况下,我们把他解析到interface{}里面
|
如果在我们不知道他的结构的情况下,我们把他解析到interface{}里面
|
||||||
|
|
||||||
var f interface{}
|
var f interface{}
|
||||||
err := json.Unmarshal(b, &f)
|
err := json.Unmarshal(b, &f)
|
||||||
|
|
||||||
这个时候f里面存储了一个map类似,他们的key是string,值存储在空的interface{}里
|
这个时候f里面存储了一个map类似,他们的key是string,值存储在空的interface{}里
|
||||||
|
|
||||||
f = map[string]interface{}{
|
f = map[string]interface{}{
|
||||||
"Name": "Wednesday",
|
"Name": "Wednesday",
|
||||||
"Age": 6,
|
"Age": 6,
|
||||||
"Parents": []interface{}{
|
"Parents": []interface{}{
|
||||||
"Gomez",
|
"Gomez",
|
||||||
"Morticia",
|
"Morticia",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
那么如何来访问这些数据呢?通过断言的方式:
|
那么如何来访问这些数据呢?通过断言的方式:
|
||||||
|
|
||||||
@@ -82,21 +82,21 @@ JSON(Javascript Object Notation)是一种轻量级的数据交换语言,
|
|||||||
|
|
||||||
通过断言之后,你就可以通过如下方式来访问里面的数据了
|
通过断言之后,你就可以通过如下方式来访问里面的数据了
|
||||||
|
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
switch vv := v.(type) {
|
switch vv := v.(type) {
|
||||||
case string:
|
case string:
|
||||||
fmt.Println(k, "is string", vv)
|
fmt.Println(k, "is string", vv)
|
||||||
case int:
|
case int:
|
||||||
fmt.Println(k, "is int", vv)
|
fmt.Println(k, "is int", vv)
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
fmt.Println(k, "is an array:")
|
fmt.Println(k, "is an array:")
|
||||||
for i, u := range vv {
|
for i, u := range vv {
|
||||||
fmt.Println(i, u)
|
fmt.Println(i, u)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
fmt.Println(k, "is of a type I don't know how to handle")
|
fmt.Println(k, "is of a type I don't know how to handle")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
通过上面的示例可以看到,通过interface{}与type assert的配合,我们就可以解析未知结构的JSON数了。
|
通过上面的示例可以看到,通过interface{}与type assert的配合,我们就可以解析未知结构的JSON数了。
|
||||||
|
|
||||||
上面这个是官方提供的解决方案,其实很多时候我们通过类型断言,操作起来不是很方便,目前bitly公司开源了一个叫做`simplejson`的包,在处理未知结构体的JSON时相当方便,详细例子如下所示:
|
上面这个是官方提供的解决方案,其实很多时候我们通过类型断言,操作起来不是很方便,目前bitly公司开源了一个叫做`simplejson`的包,在处理未知结构体的JSON时相当方便,详细例子如下所示:
|
||||||
@@ -108,7 +108,7 @@ JSON(Javascript Object Notation)是一种轻量级的数据交换语言,
|
|||||||
"float": 5.150,
|
"float": 5.150,
|
||||||
"bignum": 9223372036854775807,
|
"bignum": 9223372036854775807,
|
||||||
"string": "simplejson",
|
"string": "simplejson",
|
||||||
"bool": true
|
"bool": true
|
||||||
}
|
}
|
||||||
}`))
|
}`))
|
||||||
|
|
||||||
@@ -220,6 +220,3 @@ Marshal函数只有在转换成功的时候才会返回数据,在转换的过
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [XML处理](<7.1.md>)
|
* 上一节: [XML处理](<7.1.md>)
|
||||||
* 下一节: [正则处理](<7.3.md>)
|
* 下一节: [正则处理](<7.3.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
77
7.3.md
77
7.3.md
@@ -28,14 +28,14 @@ Go语言通过`regexp`标准包为正则表达式提供了官方支持,如果
|
|||||||
可以看到,`regexp`的pattern和我们平常使用的正则一模一样。再来看一个例子:当用户输入一个字符串,我们想知道是不是一次合法的输入:
|
可以看到,`regexp`的pattern和我们平常使用的正则一模一样。再来看一个例子:当用户输入一个字符串,我们想知道是不是一次合法的输入:
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) == 1 {
|
if len(os.Args) == 1 {
|
||||||
fmt.Println("Usage: regexp [string]")
|
fmt.Println("Usage: regexp [string]")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
} else if m, _ := regexp.MatchString("^[0-9]+$", os.Args[1]); m {
|
} else if m, _ := regexp.MatchString("^[0-9]+$", os.Args[1]); m {
|
||||||
fmt.Println("数字")
|
fmt.Println("数字")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("不是数字")
|
fmt.Println("不是数字")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
在上面的两个小例子中,我们采用了Match(Reader|String)来判断一些字符串是否符合我们的描述需求,它们使用起来非常方便。
|
在上面的两个小例子中,我们采用了Match(Reader|String)来判断一些字符串是否符合我们的描述需求,它们使用起来非常方便。
|
||||||
@@ -106,34 +106,34 @@ CompilePOSIX和Compile的不同点在于POSIX必须使用POSIX语法,它使用
|
|||||||
在了解了如何新建一个Regexp之后,我们再来看一下这个struct提供了哪些方法来辅助我们操作字符串,首先我们来看下面这写用来搜索的函数:
|
在了解了如何新建一个Regexp之后,我们再来看一下这个struct提供了哪些方法来辅助我们操作字符串,首先我们来看下面这写用来搜索的函数:
|
||||||
|
|
||||||
func (re *Regexp) Find(b []byte) []byte
|
func (re *Regexp) Find(b []byte) []byte
|
||||||
func (re *Regexp) FindAll(b []byte, n int) [][]byte
|
func (re *Regexp) FindAll(b []byte, n int) [][]byte
|
||||||
func (re *Regexp) FindAllIndex(b []byte, n int) [][]int
|
func (re *Regexp) FindAllIndex(b []byte, n int) [][]int
|
||||||
func (re *Regexp) FindAllString(s string, n int) []string
|
func (re *Regexp) FindAllString(s string, n int) []string
|
||||||
func (re *Regexp) FindAllStringIndex(s string, n int) [][]int
|
func (re *Regexp) FindAllStringIndex(s string, n int) [][]int
|
||||||
func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string
|
func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string
|
||||||
func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int
|
func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int
|
||||||
func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte
|
func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte
|
||||||
func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int
|
func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int
|
||||||
func (re *Regexp) FindIndex(b []byte) (loc []int)
|
func (re *Regexp) FindIndex(b []byte) (loc []int)
|
||||||
func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int)
|
func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int)
|
||||||
func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int
|
func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int
|
||||||
func (re *Regexp) FindString(s string) string
|
func (re *Regexp) FindString(s string) string
|
||||||
func (re *Regexp) FindStringIndex(s string) (loc []int)
|
func (re *Regexp) FindStringIndex(s string) (loc []int)
|
||||||
func (re *Regexp) FindStringSubmatch(s string) []string
|
func (re *Regexp) FindStringSubmatch(s string) []string
|
||||||
func (re *Regexp) FindStringSubmatchIndex(s string) []int
|
func (re *Regexp) FindStringSubmatchIndex(s string) []int
|
||||||
func (re *Regexp) FindSubmatch(b []byte) [][]byte
|
func (re *Regexp) FindSubmatch(b []byte) [][]byte
|
||||||
func (re *Regexp) FindSubmatchIndex(b []byte) []int
|
func (re *Regexp) FindSubmatchIndex(b []byte) []int
|
||||||
|
|
||||||
上面这18个函数我们根据输入源(byte slice、string和io.RuneReader)不同还可以继续简化成如下几个,其他的只是输入源不一样,其他功能基本是一样的:
|
上面这18个函数我们根据输入源(byte slice、string和io.RuneReader)不同还可以继续简化成如下几个,其他的只是输入源不一样,其他功能基本是一样的:
|
||||||
|
|
||||||
func (re *Regexp) Find(b []byte) []byte
|
func (re *Regexp) Find(b []byte) []byte
|
||||||
func (re *Regexp) FindAll(b []byte, n int) [][]byte
|
func (re *Regexp) FindAll(b []byte, n int) [][]byte
|
||||||
func (re *Regexp) FindAllIndex(b []byte, n int) [][]int
|
func (re *Regexp) FindAllIndex(b []byte, n int) [][]int
|
||||||
func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte
|
func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte
|
||||||
func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int
|
func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int
|
||||||
func (re *Regexp) FindIndex(b []byte) (loc []int)
|
func (re *Regexp) FindIndex(b []byte) (loc []int)
|
||||||
func (re *Regexp) FindSubmatch(b []byte) [][]byte
|
func (re *Regexp) FindSubmatch(b []byte) [][]byte
|
||||||
func (re *Regexp) FindSubmatchIndex(b []byte) []int
|
func (re *Regexp) FindSubmatchIndex(b []byte) []int
|
||||||
|
|
||||||
对于这些函数的使用我们来看下面这个例子
|
对于这些函数的使用我们来看下面这个例子
|
||||||
|
|
||||||
@@ -193,24 +193,24 @@ CompilePOSIX和Compile的不同点在于POSIX必须使用POSIX语法,它使用
|
|||||||
前面介绍过匹配函数,Regexp也定义了三个函数,它们和同名的外部函数功能一模一样,其实外部函数就是调用了这Regexp的三个函数来实现的:
|
前面介绍过匹配函数,Regexp也定义了三个函数,它们和同名的外部函数功能一模一样,其实外部函数就是调用了这Regexp的三个函数来实现的:
|
||||||
|
|
||||||
func (re *Regexp) Match(b []byte) bool
|
func (re *Regexp) Match(b []byte) bool
|
||||||
func (re *Regexp) MatchReader(r io.RuneReader) bool
|
func (re *Regexp) MatchReader(r io.RuneReader) bool
|
||||||
func (re *Regexp) MatchString(s string) bool
|
func (re *Regexp) MatchString(s string) bool
|
||||||
|
|
||||||
接下里让我们来了解替换函数是怎么操作的?
|
接下里让我们来了解替换函数是怎么操作的?
|
||||||
|
|
||||||
func (re *Regexp) ReplaceAll(src, repl []byte) []byte
|
func (re *Regexp) ReplaceAll(src, repl []byte) []byte
|
||||||
func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte
|
func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte
|
||||||
func (re *Regexp) ReplaceAllLiteral(src, repl []byte) []byte
|
func (re *Regexp) ReplaceAllLiteral(src, repl []byte) []byte
|
||||||
func (re *Regexp) ReplaceAllLiteralString(src, repl string) string
|
func (re *Regexp) ReplaceAllLiteralString(src, repl string) string
|
||||||
func (re *Regexp) ReplaceAllString(src, repl string) string
|
func (re *Regexp) ReplaceAllString(src, repl string) string
|
||||||
func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string
|
func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string
|
||||||
|
|
||||||
这些替换函数我们在上面的抓网页的例子有详细应用示例,
|
这些替换函数我们在上面的抓网页的例子有详细应用示例,
|
||||||
|
|
||||||
接下来我们看一下Expand的解释:
|
接下来我们看一下Expand的解释:
|
||||||
|
|
||||||
func (re *Regexp) Expand(dst []byte, template []byte, src []byte, match []int) []byte
|
func (re *Regexp) Expand(dst []byte, template []byte, src []byte, match []int) []byte
|
||||||
func (re *Regexp) ExpandString(dst []byte, template string, src string, match []int) []byte
|
func (re *Regexp) ExpandString(dst []byte, template string, src string, match []int) []byte
|
||||||
|
|
||||||
那么这个Expand到底用来干嘛的呢?请看下面的例子:
|
那么这个Expand到底用来干嘛的呢?请看下面的例子:
|
||||||
|
|
||||||
@@ -235,6 +235,3 @@ CompilePOSIX和Compile的不同点在于POSIX必须使用POSIX语法,它使用
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [Json处理](<7.2.md>)
|
* 上一节: [Json处理](<7.2.md>)
|
||||||
* 下一节: [模板处理](<7.4.md>)
|
* 下一节: [模板处理](<7.4.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
7.4.md
3
7.4.md
@@ -339,6 +339,3 @@ Unix用户已经很熟悉什么是`pipe`了,`ls | grep "name"`类似这样的
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [正则处理](<7.3.md>)
|
* 上一节: [正则处理](<7.3.md>)
|
||||||
* 下一节: [小结](<7.5.md>)
|
* 下一节: [小结](<7.5.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
7.5.md
3
7.5.md
@@ -5,6 +5,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [模板处理](<7.4.md>)
|
* 上一节: [模板处理](<7.4.md>)
|
||||||
* 下一节: [Web服务](<8.md>)
|
* 下一节: [Web服务](<8.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
7.md
3
7.md
@@ -14,6 +14,3 @@ XML是目前很多标准接口的交互语言,很多时候和一些Java编写
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [第六章总结](<6.5.md>)
|
* 上一章: [第六章总结](<6.5.md>)
|
||||||
* 下一节: [XML处理](<7.1.md>)
|
* 下一节: [XML处理](<7.1.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
8.1.md
3
8.1.md
@@ -324,6 +324,3 @@ Go语言包中处理UDP Socket和TCP Socket不同的地方就是在服务器端
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [Web服务](<8.md>)
|
* 上一节: [Web服务](<8.md>)
|
||||||
* 下一节: [WebSocket](<8.2.md>)
|
* 下一节: [WebSocket](<8.2.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
59
8.2.md
59
8.2.md
@@ -44,41 +44,41 @@ WebSocket分为客户端和服务端,接下来我们将实现一个简单的
|
|||||||
<html>
|
<html>
|
||||||
<head></head>
|
<head></head>
|
||||||
<body>
|
<body>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var sock = null;
|
var sock = null;
|
||||||
var wsuri = "ws://127.0.0.1:1234";
|
var wsuri = "ws://127.0.0.1:1234";
|
||||||
|
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
|
|
||||||
console.log("onload");
|
console.log("onload");
|
||||||
|
|
||||||
sock = new WebSocket(wsuri);
|
sock = new WebSocket(wsuri);
|
||||||
|
|
||||||
sock.onopen = function() {
|
sock.onopen = function() {
|
||||||
console.log("connected to " + wsuri);
|
console.log("connected to " + wsuri);
|
||||||
}
|
}
|
||||||
|
|
||||||
sock.onclose = function(e) {
|
sock.onclose = function(e) {
|
||||||
console.log("connection closed (" + e.code + ")");
|
console.log("connection closed (" + e.code + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
sock.onmessage = function(e) {
|
sock.onmessage = function(e) {
|
||||||
console.log("message received: " + e.data);
|
console.log("message received: " + e.data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function send() {
|
function send() {
|
||||||
var msg = document.getElementById('message').value;
|
var msg = document.getElementById('message').value;
|
||||||
sock.send(msg);
|
sock.send(msg);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<h1>WebSocket Echo Test</h1>
|
<h1>WebSocket Echo Test</h1>
|
||||||
<form>
|
<form>
|
||||||
<p>
|
<p>
|
||||||
Message: <input id="message" type="text" value="Hello, world!">
|
Message: <input id="message" type="text" value="Hello, world!">
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
<button onclick="send();">Send Message</button>
|
<button onclick="send();">Send Message</button>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
@@ -143,6 +143,3 @@ WebSocket分为客户端和服务端,接下来我们将实现一个简单的
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [Socket编程](<8.1.md>)
|
* 上一节: [Socket编程](<8.1.md>)
|
||||||
* 下一节: [REST](<8.3.md>)
|
* 下一节: [REST](<8.3.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
15
8.3.md
15
8.3.md
@@ -6,21 +6,21 @@ REST(REpresentational State Transfer)这个概念,首次出现是在 2000年Ro
|
|||||||
要理解什么是REST,我们需要理解下面几个概念:
|
要理解什么是REST,我们需要理解下面几个概念:
|
||||||
|
|
||||||
- 资源(Resources)
|
- 资源(Resources)
|
||||||
|
REST是"表现层状态转化",其实它省略了主语。"表现层"其实指的是"资源"的"表现层"。
|
||||||
|
|
||||||
REST是"表现层状态转化",其实它省略了主语。"表现层"其实指的是"资源"的"表现层"。
|
那么什么是资源呢?就是我们平常上网访问的一张图片、一个文档、一个视频等。这些资源我们通过URI来定位,也就是一个URI表示一个资源。
|
||||||
|
|
||||||
那么什么是资源呢?就是我们平常上网访问的一张图片、一个文档、一个视频等。这些资源我们通过URI来定位,也就是一个URI表示一个资源。
|
|
||||||
- 表现层(Representation)
|
- 表现层(Representation)
|
||||||
|
|
||||||
资源是做一个具体的实体信息,他可以有多种的展现方式。而把实体展现出来就是表现层,例如一个txt文本信息,他可以输出成html、json、xml等格式,一个图片他可以jpg、png等方式展现,这个就是表现层的意思。
|
资源是做一个具体的实体信息,他可以有多种的展现方式。而把实体展现出来就是表现层,例如一个txt文本信息,他可以输出成html、json、xml等格式,一个图片他可以jpg、png等方式展现,这个就是表现层的意思。
|
||||||
|
|
||||||
URI确定一个资源,但是如何确定它的具体表现形式呢?应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。
|
URI确定一个资源,但是如何确定它的具体表现形式呢?应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。
|
||||||
|
|
||||||
- 状态转化(State Transfer)
|
- 状态转化(State Transfer)
|
||||||
|
|
||||||
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,肯定涉及到数据和状态的变化。而HTTP协议是无状态的,那么这些状态肯定保存在服务器端,所以如果客户端想要通知服务器端改变数据和状态的变化,肯定要通过某种方式来通知它。
|
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,肯定涉及到数据和状态的变化。而HTTP协议是无状态的,那么这些状态肯定保存在服务器端,所以如果客户端想要通知服务器端改变数据和状态的变化,肯定要通过某种方式来通知它。
|
||||||
|
|
||||||
客户端能通知服务器端的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
|
客户端能通知服务器端的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
|
||||||
|
|
||||||
综合上面的解释,我们总结一下什么是RESTful架构:
|
综合上面的解释,我们总结一下什么是RESTful架构:
|
||||||
|
|
||||||
@@ -107,6 +107,3 @@ REST是一种架构风格,汲取了WWW的成功经验:无状态,以资源
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [WebSocket](<8.2.md>)
|
* 上一节: [WebSocket](<8.2.md>)
|
||||||
* 下一节: [RPC](<8.4.md>)
|
* 下一节: [RPC](<8.4.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
8.4.md
3
8.4.md
@@ -388,6 +388,3 @@ Go已经提供了对RPC的良好支持,通过上面HTTP、TCP、JSON RPC的实
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [REST](<8.3.md>)
|
* 上一节: [REST](<8.3.md>)
|
||||||
* 下一节: [小结](<8.5.md>)
|
* 下一节: [小结](<8.5.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
8.5.md
3
8.5.md
@@ -4,6 +4,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [RPC](<8.4.md>)
|
* 上一节: [RPC](<8.4.md>)
|
||||||
* 下一章: [安全与加密](<9.md>)
|
* 下一章: [安全与加密](<9.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
3
8.md
3
8.md
@@ -22,6 +22,3 @@ Go语言是21世纪的C语言,我们追求的是性能、简单,所以我们
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [第七章总结](<7.5.md>)
|
* 上一章: [第七章总结](<7.5.md>)
|
||||||
* 下一节: [Socket编程](<8.1.md>)
|
* 下一节: [Socket编程](<8.1.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
37
9.1.md
37
9.1.md
@@ -56,29 +56,29 @@ CSRF的防御可以从服务端和客户端两方面着手,防御效果是从
|
|||||||
- 每个请求使用验证码,这个方案是完美的,因为要多次输入验证码,所以用户友好性很差,所以不适合实际运用。
|
- 每个请求使用验证码,这个方案是完美的,因为要多次输入验证码,所以用户友好性很差,所以不适合实际运用。
|
||||||
- 不同的表单包含一个不同的伪随机值,我们在4.4小节介绍“如何防止表单多次递交”时介绍过此方案,复用相关代码,实现如下:
|
- 不同的表单包含一个不同的伪随机值,我们在4.4小节介绍“如何防止表单多次递交”时介绍过此方案,复用相关代码,实现如下:
|
||||||
|
|
||||||
生成随机数token
|
生成随机数token
|
||||||
|
|
||||||
h := md5.New()
|
h := md5.New()
|
||||||
io.WriteString(h, strconv.FormatInt(crutime, 10))
|
io.WriteString(h, strconv.FormatInt(crutime, 10))
|
||||||
io.WriteString(h, "ganraomaxxxxxxxxx")
|
io.WriteString(h, "ganraomaxxxxxxxxx")
|
||||||
token := fmt.Sprintf("%x", h.Sum(nil))
|
token := fmt.Sprintf("%x", h.Sum(nil))
|
||||||
|
|
||||||
t, _ := template.ParseFiles("login.gtpl")
|
t, _ := template.ParseFiles("login.gtpl")
|
||||||
t.Execute(w, token)
|
t.Execute(w, token)
|
||||||
|
|
||||||
输出token
|
输出token
|
||||||
|
|
||||||
<input type="hidden" name="token" value="{{.}}">
|
<input type="hidden" name="token" value="{{.}}">
|
||||||
|
|
||||||
验证token
|
验证token
|
||||||
|
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
token := r.Form.Get("token")
|
token := r.Form.Get("token")
|
||||||
if token != "" {
|
if token != "" {
|
||||||
//验证token的合法性
|
//验证token的合法性
|
||||||
} else {
|
} else {
|
||||||
//不存在token报错
|
//不存在token报错
|
||||||
}
|
}
|
||||||
|
|
||||||
这样基本就实现了安全的POST,但是也许你会说如果破解了token的算法呢,按照理论上是,但是实际上破解是基本不可能的,因为有人曾计算过,暴力破解该串大概需要2的11次方时间。
|
这样基本就实现了安全的POST,但是也许你会说如果破解了token的算法呢,按照理论上是,但是实际上破解是基本不可能的,因为有人曾计算过,暴力破解该串大概需要2的11次方时间。
|
||||||
|
|
||||||
@@ -89,6 +89,3 @@ CSRF的防御可以从服务端和客户端两方面着手,防御效果是从
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [安全与加密](<9.md>)
|
* 上一节: [安全与加密](<9.md>)
|
||||||
* 下一节: [确保输入过滤](<9.2.md>)
|
* 下一节: [确保输入过滤](<9.2.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
17
9.2.md
17
9.2.md
@@ -33,13 +33,13 @@
|
|||||||
接下来,让我们通过一个例子来巩固这些概念,请看下面这个表单
|
接下来,让我们通过一个例子来巩固这些概念,请看下面这个表单
|
||||||
|
|
||||||
<form action="/whoami" method="POST">
|
<form action="/whoami" method="POST">
|
||||||
我是谁:
|
我是谁:
|
||||||
<select name="name">
|
<select name="name">
|
||||||
<option value="astaxie">astaxie</option>
|
<option value="astaxie">astaxie</option>
|
||||||
<option value="herry">herry</option>
|
<option value="herry">herry</option>
|
||||||
<option value="marry">marry</option>
|
<option value="marry">marry</option>
|
||||||
</select>
|
</select>
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
在处理这个表单的编程逻辑中,非常容易犯的错误是认为只能提交三个选择中的一个。其实攻击者可以模拟POST操作,递交`name=attack`这样的数据,所以在此时我们需要做类似白名单的处理
|
在处理这个表单的编程逻辑中,非常容易犯的错误是认为只能提交三个选择中的一个。其实攻击者可以模拟POST操作,递交`name=attack`这样的数据,所以在此时我们需要做类似白名单的处理
|
||||||
@@ -70,6 +70,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [预防CSRF攻击](<9.1.md>)
|
* 上一节: [预防CSRF攻击](<9.1.md>)
|
||||||
* 下一节: [避免XSS攻击](<9.3.md>)
|
* 下一节: [避免XSS攻击](<9.3.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
9.3.md
3
9.3.md
@@ -50,6 +50,3 @@ XSS漏洞是相当有危害的,在开发Web应用的时候,一定要记住
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [确保输入过滤](<9.2.md>)
|
* 上一节: [确保输入过滤](<9.2.md>)
|
||||||
* 下一节: [避免SQL注入](<9.4.md>)
|
* 下一节: [避免SQL注入](<9.4.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
9.4.md
3
9.4.md
@@ -67,6 +67,3 @@ SQL注入攻击的危害这么大,那么该如何来防治呢?下面这些建
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [避免XSS攻击](<9.3.md>)
|
* 上一节: [避免XSS攻击](<9.3.md>)
|
||||||
* 下一节: [存储密码](<9.5.md>)
|
* 下一节: [存储密码](<9.5.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
3
9.5.md
3
9.5.md
@@ -87,6 +87,3 @@ Go语言对这三种加密算法的实现如下所示:
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [确保输入过滤](<9.4.md>)
|
* 上一节: [确保输入过滤](<9.4.md>)
|
||||||
* 下一节: [加密和解密数据](<9.6.md>)
|
* 下一节: [加密和解密数据](<9.6.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
19
9.6.md
19
9.6.md
@@ -98,16 +98,16 @@ Go语言的`crypto`里面支持对称加密的高级加解密包有:
|
|||||||
上面通过调用函数`aes.NewCipher`(参数key必须是16、24或者32位的[]byte,分别对应AES-128, AES-192或AES-256算法),返回了一个`cipher.Block`接口,这个接口实现了三个功能:
|
上面通过调用函数`aes.NewCipher`(参数key必须是16、24或者32位的[]byte,分别对应AES-128, AES-192或AES-256算法),返回了一个`cipher.Block`接口,这个接口实现了三个功能:
|
||||||
|
|
||||||
type Block interface {
|
type Block interface {
|
||||||
// BlockSize returns the cipher's block size.
|
// BlockSize returns the cipher's block size.
|
||||||
BlockSize() int
|
BlockSize() int
|
||||||
|
|
||||||
// Encrypt encrypts the first block in src into dst.
|
// Encrypt encrypts the first block in src into dst.
|
||||||
// Dst and src may point at the same memory.
|
// Dst and src may point at the same memory.
|
||||||
Encrypt(dst, src []byte)
|
Encrypt(dst, src []byte)
|
||||||
|
|
||||||
// Decrypt decrypts the first block in src into dst.
|
// Decrypt decrypts the first block in src into dst.
|
||||||
// Dst and src may point at the same memory.
|
// Dst and src may point at the same memory.
|
||||||
Decrypt(dst, src []byte)
|
Decrypt(dst, src []byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
这三个函数实现了加解密操作,详细的操作请看上面的例子。
|
这三个函数实现了加解密操作,详细的操作请看上面的例子。
|
||||||
@@ -120,6 +120,3 @@ Go语言的`crypto`里面支持对称加密的高级加解密包有:
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [存储密码](<9.5.md>)
|
* 上一节: [存储密码](<9.5.md>)
|
||||||
* 下一节: [小结](<9.7.md>)
|
* 下一节: [小结](<9.7.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
7
9.7.md
7
9.7.md
@@ -1,10 +1,9 @@
|
|||||||
# 9.7 小结
|
# 9.7 小结
|
||||||
这一章主要介绍了如:CSRF攻击、XSS攻击、SQL注入攻击等一些Web应用中典型的攻击手法,它们都是由于应用对用户的输入没有很好的过滤引起的,所以除了介绍攻击的方法外,我们也介绍了了如何有效的进行数据过滤,以防止这些攻击的发生的方法。然后针对日异严重的密码泄漏事件,介绍了在设计Web应用中可采用的从基本到专家的加密方案。最后针对敏感数据的加解密简要介绍了,Go语言提供三种对称加密算法:base64、aes和des的实现。
|
这一章主要介绍了如:CSRF攻击、XSS攻击、SQL注入攻击等一些Web应用中典型的攻击手法,它们都是由于应用对用户的输入没有很好的过滤引起的,所以除了介绍攻击的方法外,我们也介绍了了如何有效的进行数据过滤,以防止这些攻击的发生的方法。然后针对日异严重的密码泄漏事件,介绍了在设计Web应用中可采用的从基本到专家的加密方案。最后针对敏感数据的加解密简要介绍了,Go语言提供三种对称加密算法:base64、aes和des的实现。
|
||||||
|
|
||||||
|
编写这一章的目的是希望读者能够在意识里面加强安全概念,在编写Web应用的时候多留心一点,以使我们编写的Web应用能远离黑客们的攻击。Go语言在支持防攻击方面已经提供大量的工具包,我们可以充分的利用这些包来做出一个安全的Web应用。
|
||||||
|
|
||||||
## links
|
## links
|
||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [加密和解密数据](<9.6.md>)
|
* 上一节: [加密和解密数据](<9.6.md>)
|
||||||
* 下一节: [国际化和本地化](<10.md>)
|
* 下一节: [国际化和本地化](<10.md>)
|
||||||
* 上一节: [加密和解密数据](<9.6.md>)
|
|
||||||
* 下一节: [国际化和本地化](<10.md>)
|
|
||||||
|
|
||||||
|
|||||||
3
9.md
3
9.md
@@ -24,6 +24,3 @@
|
|||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [第八章总结](<8.5.md>)
|
* 上一章: [第八章总结](<8.5.md>)
|
||||||
* 下一节: [预防CSRF攻击](<9.1.md>)
|
* 下一节: [预防CSRF攻击](<9.1.md>)
|
||||||
|
|
||||||
## LastModified
|
|
||||||
* $Id$
|
|
||||||
|
|||||||
BIN
images/.DS_Store
vendored
BIN
images/.DS_Store
vendored
Binary file not shown.
Reference in New Issue
Block a user