Merge pull request #103 from OlingCat/master

移除了cvs用的所有footer,重新格式化了所有的代码。
This commit is contained in:
astaxie
2012-11-01 01:20:32 -07:00
73 changed files with 4568 additions and 4774 deletions

3
1.1.md
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -20,11 +20,11 @@
这里将介绍Sublime Text 2以下简称Sublime+GoSublime+gocode+MarGo的组合那么为什么选择这个组合呢 这里将介绍Sublime Text 2以下简称Sublime+GoSublime+gocode+MarGo的组合那么为什么选择这个组合呢
- 自动化提示代码,如下图所示 - 自动化提示代码,如下图所示
![](images/1.4.sublime1.png?raw=true) ![](images/1.4.sublime1.png?raw=true)
- 保存的时候自动格式化代码让您编写的代码更加美观符合Go的标准。 - 保存的时候自动格式化代码让您编写的代码更加美观符合Go的标准。
- 支持项目管理 - 支持项目管理
![](images/1.4.sublime2.png?raw=true) ![](images/1.4.sublime2.png?raw=true)
- 支持语法高亮 - 支持语法高亮
- Sublime Text 2可免费使用只是保存次数达到一定数量之后就会提示是否购买点击取消继续用和正式注册版本没有任何区别。 - Sublime Text 2可免费使用只是保存次数达到一定数量之后就会提示是否购买点击取消继续用和正式注册版本没有任何区别。
@@ -34,7 +34,7 @@
1. 打开之后安装 Package ControlCtrl+` 打开命令行,执行如下代码: 1. 打开之后安装 Package ControlCtrl+` 打开命令行,执行如下代码:
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传说中的神器她不仅仅是一个编辑器它是一个整合环
![](images/1.4.emacs.png?raw=true) ![](images/1.4.emacs.png?raw=true)
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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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$

View File

@@ -4,6 +4,3 @@
* [目录](<preface.md>) * [目录](<preface.md>)
* 上一节: [国际化站点](<10.3.md>) * 上一节: [国际化站点](<10.3.md>)
* 下一节: [错误处理,故障排除和测试](<11.md>) * 下一节: [错误处理,故障排除和测试](<11.md>)
## LastModified
* $Id$

3
10.md
View File

@@ -25,6 +25,3 @@
* [目录](<preface.md>) * [目录](<preface.md>)
* 上一章: [第九章总结](<9.7.md>) * 上一章: [第九章总结](<9.7.md>)
* 下一节: [设置默认地区](<10.1.md>) * 下一节: [设置默认地区](<10.1.md>)
## LastModified
* $Id$

View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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结构也定义了一个methodString。其实这也是实现了fmt.Stringer这个interface即如果需要某个类型能被fmt包以特殊的格式输出你就必须实现Stringer这个接口。如果没有实现这个接口fmt将以默认的方式输出。 现在我们再回顾一下前面的Box示例你会发现Color结构也定义了一个methodString。其实这也是实现了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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -76,8 +76,8 @@ HTTP协议是无状态的同一个客户端的这次请求和上次请求是
Accepttext/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 //客户端能接收的mine Accepttext/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 //客户端能接收的mine
Accept-Encodinggzip,deflate,sdch //是否支持流压缩 Accept-Encodinggzip,deflate,sdch //是否支持流压缩
Accept-CharsetUTF-8,*;q=0.5 //客户端字符编码集 Accept-CharsetUTF-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
View File

@@ -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
View File

@@ -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
View File

@@ -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这个例子就没有设置handlerhandler就设置为DefaultServeMux 7. 判断handler是否为空如果没有设置handler这个例子就没有设置handlerhandler就设置为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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -7,6 +7,3 @@
* [目录](<preface.md>) * [目录](<preface.md>)
* 上一节: [处理文件上传](<4.5.md>) * 上一节: [处理文件上传](<4.5.md>)
* 下一章: [访问数据库](<5.md>) * 下一章: [访问数据库](<5.md>)
## LastModified
* $Id$

5
4.md
View File

@@ -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
View File

@@ -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函数关闭当前的链接状态但是如果当前正在执行queryquery还是有效返回rows数据。 Close函数关闭当前的链接状态但是如果当前正在执行queryquery还是有效返回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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -35,21 +35,21 @@ Go语言中通过net/http包中的SetCookie来设置
w表示需要写入的responsecookie是一个struct让我们来看一下cookie对象是怎么样的 w表示需要写入的responsecookie是一个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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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包含bb包含c例如如下代码就会输出 - tag中含有`"a>b>c"`那么就会循环输出三个元素a包含bb包含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
View File

@@ -58,23 +58,23 @@ JSONJavascript 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 @@ JSONJavascript 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 @@ JSONJavascript 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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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

Binary file not shown.