Conflicts:
	2.2.md
	2.3.md
	genepub.sh
This commit is contained in:
chenwenli
2012-10-29 17:24:07 +08:00
61 changed files with 312 additions and 291 deletions

16
1.1.md
View File

@@ -1,6 +1,6 @@
# 1.1 Go 安装
##Go的三种安装方式
## Go的三种安装方式
Go有多种安装方式你可以选择自己喜欢的。这里我们介绍三种最常见的安装方式
- 从编译源码安装这是一种标准的软件安装方式。对于经常使用Unix类系统的用户尤其对于开发者来说从源码安装是最方便而熟悉的。
@@ -9,7 +9,7 @@ Go有多种安装方式你可以选择自己喜欢的。这里我们介绍三
最后如果你想在同一个系统中安装多个版本的Go你可以参考第三方工具[GVM](https://github.com/moovweb/gvm),这是目前在这方面做得最好的工具,除非你知道怎么处理。
###Go源码安装
### Go源码安装
在Go的源代码中有些部分是用Plan 9 C和AT&T汇编写的因此假如你要想从源码安装就必须安装C的编译工具。
在Mac系统中只要你安装了Xcode就已经包含了相应的编译工具。
@@ -43,9 +43,9 @@ Go使用[Mercurial][hg]进行版本管理首先你必须安装了Mercurial
如果出现Go的Usage信息那么说明Go已经安装成功了如果出现该命令不存在那么可以检查一下自己的PATH环境变中是否包含了Go的安装目录。
##Go标准包安装
## Go标准包安装
###如何判断自己的操作系统是32位还是64位
### 如何判断自己的操作系统是32位还是64位
我们接下来的Go安装需要判断操作系统的位数所以这小节我们先确定自己的系统类型。
@@ -99,8 +99,8 @@ Linux系统用户可通过在Terminal中执行命令`uname -a`来查看系统信
如果出现Go的Usage信息那么说明Go已经安装成功了如果出现该命令不存在那么可以检查一下自己的PATH环境变中是否包含了Go的安装目录。
##第三方工具安装
###GVM
## 第三方工具安装
### GVM
gvm是第三方开发的Go多版本管理工具类似ruby里面的rvm工具。使用起来相当的方便安装gvm使用如下命令
bash < <(curl -s https://raw.github.com/moovweb/gvm/master/binscripts/gvm-installer)
@@ -112,14 +112,14 @@ gvm是第三方开发的Go多版本管理工具类似ruby里面的rvm工具
执行完上面的命令之后GOPATH、GOROOT等环境变量会自动设置好这样就可以直接使用了。
###apt-get
### apt-get
Ubuntu是目前使用最多的Linux桌面系统使用`apt-get`命令来管理软件包我们可以通过下面的命令来安装Go
sudo add-apt-repository ppa:gophers/go
sudo apt-get update
sudo apt-get install golang-stable
###homebrew
### homebrew
homebrew是Mac系统下面目前使用最多的管理软件的工具目前已支持Go可以通过命令直接安装Go
brew install go

18
1.3.md
View File

@@ -1,6 +1,6 @@
# 1.3 Go 命令
##Go 命令
## Go 命令
Go语言自带有一套完整的命令操作工具你可以通过在命令行中执行`go`来查看它们:
@@ -8,7 +8,7 @@
这些命令对于我们平时编写的代码非常有用,接下来就让我们了解一些常用的命令。
##go build
## go build
这个命令主要用于测试编译。在包的编译过程中,若有必要,会同时编译与之相关联的包。
@@ -33,7 +33,7 @@
`go build`的时候会选择性地编译以系统名结尾的文件linux、darwin、windows、freebsd。例如Linux系统下面编译只会选择array_linux.go文件其它系统命名后缀文件全部忽略。
##go clean
## go clean
这个命令是用来移除当前源码包里面编译的文件的。这些文件包括
@@ -50,11 +50,11 @@
我一般都是利用这个命令进行清除编译文件然后github递交源码在本机测试的时候这些编译文件都是和系统相关的但是对于源码管理来说没必要
##go fmt
## go fmt
有过C/C++经验的读者会知道,一些人经常为代码采取K&R风格还是ANSI风格而争论不休。在go中代码则有标准的风格。由于之前已经有的一些习惯或其它的原因我们常将代码写成ANSI风格或者其它更合适自己的格式这将为人们在阅读别人的代码时添加不必要的负担所以go强制了代码格式比如左大括号必须放在行尾不按照此格式的代码将不能编译通过为了减少浪费在排版上的时间go工具集中提供了一个`go fmt`命令 它可以帮你格式化你写好的代码文件,使你写代码的时候不需要关心格式,你只需要在写完之后执行`go fmt <文件名>.go`,你的代码就被修改成了标准格式,但是我平常很少用到这个命令,因为开发工具里面一般都带了保存时候自动格式化功能,这个功能其实在底层就是调用了`go fmt`。接下来的一节我将讲述两个工具,这两个工具都自带了保存文件时自动化`go fmt`功能。
##go get
## go get
这个命令是用来动态获取远程代码包的目前支持的有BitBucket、GitHub、Google Code和Launchpad。这个命令在内部实际上分成了两步操作第一步是下载源码包第二步是执行`go install`。下载源码包的go工具会自动根据不同的域名调用不同的源码工具对应关系如下
@@ -65,11 +65,11 @@
所以为了`go get` 能正常工作你必须确保安装了合适的源码管理工具并同时把这些命令加入你的PATH中。其实`go get`支持自定义域名的功能,具体参见`go help remote`
##go install
## go install
这个命令在内部实际上分成了两步操作:第一步是`go build`第二步会把编译好的东西move到`$GOPATH/pkg`或者`$GOPATH/bin`
##go test
## go test
执行这个命令,会自动读取源码目录下面名为`*_test.go`的文件,生成并运行测试用的可执行文件。输出的信息类似
@@ -80,7 +80,7 @@
默认的情况下不需要任何的参数它会自动把你源码包下面所有test文件测试完毕当然你也可以带上参数详情请参考`go help testflag`
##go doc
## go doc
很多人说go不需要任何的第三方文档例如chm手册之类的其实我已经做了一个了[chm手册](https://github.com/astaxie/godoc)),因为它内部就有一个很强大的文档工具。
@@ -92,7 +92,7 @@
通过命令在命令行执行 godoc -http=:端口号 比如`godoc -http=:8080`。然后在浏览器中打开`127.0.0.1:8080`你将会看到一个golang.org的本地copy版本通过它你可以查询pkg文档等其它内容。如果你设置了GOPATH在pkg分类下不但会列出标准包的文档还会列出你本地`GOPATH`中所有项目的相关文档,这对于经常被墙的用户来说是一个不错的选择。
##其它命令
## 其它命令
go还提供了其它很多的工具例如下面的这些工具

6
1.4.md
View File

@@ -1,8 +1,8 @@
#1.4 Go开发工具
# 1.4 Go开发工具
本节我将介绍几个开发工具它们都具有自动化提示自动化fmt功能。因为它们都是跨平台的所以安装步骤之类的都是通用的。
##LiteIDE
## LiteIDE
LiteIDE是一款专门为Go语言开发的集成开发环境IDE由visualfc编写。支持项目管理、集成构建、GDB调试、语法高亮、自动补全、大纲显示等功能。下载地址: [http://code.google.com/p/golangide/downloads/list](http://code.google.com/p/golangide/downloads/list)根据自己的系统下载相应的发行版本。Windows和Ubuntu系统可直接打开bin下面的liteideMac则需通过LaunchPad打开LiteIDE.app。
@@ -15,7 +15,7 @@
代码补全需要安装gocode目前LiteIDE的自动化提示只支持本文件中函数的提示还没有做到整个项目中函数的提示。
##Sublime Text
## Sublime Text
这里将介绍Sublime Text 2以下简称Sublime+GoSublime+gocode+MarGo的组合那么为什么选择这个组合呢

2
1.5.md
View File

@@ -1,4 +1,4 @@
#总结
# 总结
这一章中我们主要介绍了如何安装Go以及如何配置本地的`$GOPATH`,通过设置`$GOPATH`之后如何创建项目项目创建之后如何编译、如何安装接着介绍了一些Go的常用命令工具最后介绍了Go的开发工具希望能够通过有利的工具快速的开发Go应用。

12
10.1.md
View File

@@ -1,5 +1,5 @@
# 10.1 设置默认地区
##什么是Locale
## 什么是Locale
Locale是一组描述世界上某一特定区域文本格式和语言习惯的设置的集合。locale名通常由三个部分组成第一部分是一个强制性的表示语言的缩写例如"en"表示英文或"zh"表示中文。第二部分,跟在一个下划线之后,是一个可选的国家说明符,用于区分讲同一种语言的不同国家,例如"en_US"表示美国英语,而"en_UK"表示英国英语。最后一部分,跟在一个句点之后,是可选的字符集说明符,例如"zh_CN.gb2312"表示中国使用gb2312字符集。
GO语言默认采用"UTF-8"编码集所以我们实现i18n时不考虑第三部分接下来我们都采用locale描述的前面两部分来作为i18n标准的locale名。
@@ -7,10 +7,10 @@ GO语言默认采用"UTF-8"编码集所以我们实现i18n时不考虑第三
>在Linux和Solaris系统中可以通过`locale -a`命令列举所有支持的地区名读者可以看到这些地区名的命名规范。对于BSD等系统没有locale命令但是地区信息存储在/usr/share/locale中。
##设置Locale
## 设置Locale
有了上面对locale的定义那么我们就需要根据用户的信息(访问信息、个人信息、访问域名等)来设置与之相关的locale我们可以通过如下几种方式来设置用户的locale。
###通过域名设置Locale
### 通过域名设置Locale
设置Locale的办法这一就是在应用运行的时候采用域名分级的方式例如我们采用www.asta.com当做我们的英文站(默认站)而把域名www.asta.cn当做中文站。这样通过在应用里面设置域名和相应的locale的对应关系就可以设置好地区。这样处理有几点好处
- 通过URL就可以很明显的识别
@@ -42,7 +42,7 @@ GO语言默认采用"UTF-8"编码集所以我们实现i18n时不考虑第三
通过域名设置Locale有如上所示的优点但是我们一般开发Web应用的时候不会采用这种方式因为首先域名成本比较高开发一个Locale就需要一个域名而且往往统一名称的域名不一定能申请的到其次我们不愿意为每个站点去本地化一个配置而更多的是采用url后面带参数的方式请看下面的介绍。
###从域名参数设置Locale
### 从域名参数设置Locale
目前最常用的设置Locale的方式是在URL里面带上参数例如www.asta.com/hello?locale=zh或者www.asta.com/zh/hello。这样我们就可以设置地区`i18n.SetLocale(params["locale"])`
这种设置方式几乎拥有前面讲的通过域名设置Locale的所有优点它采用RESTful的方式以使得我们不需要增加额外的方法来处理。但是这种方式需要在每一个的link里面增加相应的参数locale这也许有点复杂而且有时候甚至相当的繁琐。不过我们可以写一个通用的函数url让所有的link地址都通过这个函数来生成然后在这个函数里面增加`locale=params["locale"]`参数来缓解一下。
@@ -51,7 +51,7 @@ GO语言默认采用"UTF-8"编码集所以我们实现i18n时不考虑第三
mux.Get("/:locale/books", listbook)
###从客户端设置地区
### 从客户端设置地区
在一些特殊的情况下我们需要根据客户端的信息而不是通过URL来设置Locale这些信息可能来自于客户端设置的喜好语言(浏览器中设置)用户的IP地址用户在注册的时候填写的所在地信息等。这种方式比较适合Web为基础的应用。
- Accept-Language
@@ -75,7 +75,7 @@ GO语言默认采用"UTF-8"编码集所以我们实现i18n时不考虑第三
当然你也可以让用户根据你提供的下拉菜单或者别的什么方式的设置相应的locale然后我们将用户输入的信息保存到与它帐号相关的profile中当用户再次登陆的时候把这个设置复写到locale设置中这样就可以保证该用户每次访问都是基于自己先前设置的locale来获得页面。
##总结
## 总结
通过上面的介绍可知设置Locale可以有很多种方式我们应该根据需求的不同来选择不同的设置Locale的方法以让用户能以它最熟悉的方式获得我们提供的服务提高应用的用户友好性。
## links

10
10.2.md
View File

@@ -1,6 +1,6 @@
# 10.2 本地化资源
前面小节我们介绍了如何设置Locale设置好Locale之后我们需要解决的问题就是如何存储相应的Locale对应的信息呢这里面的信息包括文本信息、时间和日期、货币值、图片、包含文件以及视图等资源。那么接下来我们讲对这些信息一一进行介绍Go语言中我们把这些格式信息存储在JSON中然后通过合适的方式展现出来。(接下来以中文和英文两种语言对比举例,存储格式文件en.json和zh-CN.json)
##本地化文本消息
## 本地化文本消息
文本信息是我们编写Web应用中最长用到的也是本地化资源中最多的信息你想要以适合本地的语言显示文本信息那么就需要建立相应的map来维护一个key-value的关系在打印消息之前从map中去获取相应的字符串例如下面这个例如一个包含英文和中文的食品名称的简单的map以及一个从该map中抽取单词的函数。
package main
@@ -45,7 +45,7 @@
上面的示例代码主要是为了演示我们内部实现的思路其实最后数据是存储在JSON里面的map我们可以通过`json.Unmarshal`获取对应的数据。
##本地化日期和时间
## 本地化日期和时间
时间和日期对于不同的地区会显示不同的时间点,而且相应的显示时间格式也是不同的,例如中文环境下可能显示:`2012年10月24日 星期三 23时11分13秒 CST`,而在英文环境下可能显示:`Wed Oct 24 23:11:13 CST 2012`。这里面我们需要解决两点:
1. 时区问题
@@ -77,7 +77,7 @@
//%d 替换成24
}
##本地化货币值
## 本地化货币值
各个地区的货币表示也不一样我们想要根据不同的Locale显示不同的货币金额需要实现如上时间格式的处理函数详细的实现请看下面代码:
en["money"] ="USD %d"
@@ -90,7 +90,7 @@
}
##本地化视图和资源
## 本地化视图和资源
我们在展现不同Locale的时候可能会根据不同的Locale采用不同的视图这些视图里面包含不同的图片、css、js等各种静态资源。那么我们如何来处理这些信息呢首先我们需要组织对应的locale文件信息请看下面的文件目录安排
views
@@ -125,7 +125,7 @@
这样我们在本地化视图以及资源的时候采用这种方式就可以很容易的进行扩展了。
##总结
## 总结
本小节介绍了如何使用存储本地资源本地资源有些需要通过转换函数有些通过lang设置但是最后都是通过key-value的方式存储了相应的Locale对应数据然后通过转换函数通过key读取出来相应的Locale信息如果是文本信息就直接输出了如果是时间日期或者货币需要结合`fmt.Printf`函数的处理才能转换成正确的信息展示而对于不同Locale的视图和资源是最简单的只要在路径里面增加lang就可以实现了。
## links

37
10.3.md
View File

@@ -1,35 +1,52 @@
# 10.3 国际化站点
前面小节介绍了如何处理本地化资源即Locale一个相应的配置文件那么如果处理多个的本地化资源呢而对于一些我们经常用到的例如简单的文本翻译、时间日期、数字等如果处理呢本小节将一一解决这些问题。
##管理多个本地包
## 管理多个本地包
在我们开发一个应用的时候首先我们应该知道这个Web应用要支持多少个语言例如首先这个Web应用需要支持中文和英文也许以后会支持其他语言但是结构已经有了所以扩展非常容易。那么我们设计如下Locale文件夹在config/locales下假设你要支持中文和英语那么你需要在这个文件夹下放置en.json和zh.json。大概的内容如下所示
# zh.json
zh:
submit: '提交'
create: '创建'
{
"zh": {
"submit": "提交",
"create": "创建"
}
}
#en.json
en:
submit: 'Submit'
create: 'Create'
{
"en": {
"submit": "Submit",
"create": "Create"
}
}
然后调用go-i18n包里面注册一下这个目录这样Go会加载config/locales目录下的locale文件
Tr:=i18n.NewLocale()
Tr.LoadPath("config/locales")
使用很简单,你可以通过下面的方式进行测试:
##自动加载本地包
fmt.Println(Tr.Translate("submit"))
//输出Submit
Tr.SetLocale("zn")
fmt.Println(Tr.Translate("submit"))
//输出“递交”
##template mapfunc
## 自动加载本地包
上面我们介绍了多个自定义语言包的自动加载那么在我们加载的同时其实go-i18n库会自动帮你加载一些定义的格式信息例如时间格式、货币格式。请看下面的处理过程
## template mapfunc
1. 文本信息
2. 时间日期
3. 数字
##总结
## 总结
## links
* [目录](<preface.md>)
* 上一节: [本地化资源](<10.2.md>)

8
2.1.md
View File

@@ -1,8 +1,8 @@
#2.1 你好Go
# 2.1 你好Go
在开始编写应用之前我们先从最基本的程序开始。就像你造房子之前不知道什么是地基一样编写程序也不知道如何开始。因此在本节中我们要学习用最基本的语法让Go程序运行起来。
##程序
## 程序
这就像一个传统,在学习大部分语言之前,你先学会如何编写一个可以输出`hello world`的程序。
@@ -20,7 +20,7 @@
Hello, world or καλημ ́ρα κóσμ or こんにちは世界
##详解
## 详解
首先我们要了解一个概念Go程序是通过`package`来组织的
`package <pkgName>`(在我们的例子中是`package main`)这一行告诉我们当前文件属于哪个包,而包名`main`则告诉我们它是一个可独立运行的包,它在编译后会产生可执行文件。除了`main`包之外,其它的包最后都会生成`*.a`文件(也就是包文件)并放置在`$GOPATH/pkg/$GOOS_$GOARCH`以Mac为例就是`$GOPATH/pkg/darwin_amd64`)。
@@ -42,7 +42,7 @@
最后大家可以看到我们输出的内容里面包含了很多非ASCII码字符。实际上Go是天生支持UTF-8的任何字符都可以直接输出你甚至可以用UTF-8中的任何字符作为标识符。
##结论
## 结论
Go使用`package`和Python的模块类似来组织代码。`main.main()`函数(这个函数主要位于主包是每一个独立的可运行程序的入口点。Go使用UTF-8字符串和标识符(因为UTF-8的发明者也就是Go的发明者),所以它天生就具有多语言的支持。

36
2.2.md
View File

@@ -1,8 +1,8 @@
#2.2 Go基础
# 2.2 Go基础
这小节我们将要介绍如何定义变量、常量、Go内置类型以及Go程序设计中的一些技巧。
##定义变量
## 定义变量
Go语言里面定义变量有多种方式。
@@ -61,7 +61,7 @@ Go对于已声明但未使用的变量会在编译阶段报错比如下面的
var i int
}
##常量
## 常量
所谓常量也就是在程序编译阶段就确定下来的值而程序在运行时则无法改变该值。在Go程序中常量可定义为数值、布尔值或字符串等类型。
@@ -79,9 +79,9 @@ Go对于已声明但未使用的变量会在编译阶段报错比如下面的
const prefix = "astaxie_"
##内置基础类型
## 内置基础类型
###Boolean
### Boolean
在Go中布尔值的类型为`bool`,值是`true``false`,默认为`false`
@@ -95,7 +95,7 @@ Go对于已声明但未使用的变量会在编译阶段报错比如下面的
}
###数值类型
### 数值类型
整数类型有无符号和带符号两种。Go同时支持`int``uint`这两种类型的长度相同但具体长度取决于不同编译器的实现。当前的gcc和gccgo编译器在32位和64位平台上都使用32位来表示`int``uint`但未来在64位平台上可能增加到64位。Go里面也有直接定义好位数的类型`rune`, `int8`, `int16`, `int32`, `int64``byte`, `uint8`, `uint16`, `uint32`, `uint64`。其中`rune``int32`的别称,`byte``uint8`的别称。
@@ -118,7 +118,7 @@ Go对于已声明但未使用的变量会在编译阶段报错比如下面的
fmt.Printf("Value is: %v", c)
###字符串
### 字符串
我们在上一节中讲过Go中的字符串都是采用`UTF-8`字符集编码。字符串是用一对双引号(`""`)或反引号(`` ` `` `` ` ``)括起来定义,它的类型是`string`。
@@ -166,7 +166,7 @@ Go中可以使用`+`操作符来连接两个字符串:
`` ` `` 括起的字符串为Raw字符串即字符串在代码中的形式就是打印时的形式它没有字符转义换行也将原样输出。
###错误类型
### 错误类型
Go内置有一个`error`类型专门用来处理错误信息Go的`package`里面还专门有一个包`errors`来处理错误:
err := errors.New("emit macho dwarf: elf header corrupted")
@@ -174,15 +174,15 @@ Go内置有一个`error`类型专门用来处理错误信息Go的`package`
fmt.Print(err)
}
###Go数据底层的存储
### Go数据底层的存储
下面这张图来源于[Russ Cox Blog](http://research.swtch.com/)中一篇介绍[Go数据结构](http://research.swtch.com/godata)的文章,大家可以看到这些基础类型底层都是分配了一块内存,然后存储了相应的值。
![](images/2.2.basic.png?raw=true)
##一些技巧
## 一些技巧
###分组声明
### 分组声明
在Go语言中同时声明多个常量、变量或者导入多个包时可采用分组的方式进行声明。
@@ -220,7 +220,7 @@ Go内置有一个`error`类型专门用来处理错误信息Go的`package`
>除非被显式设置为其它值或`iota`,每个`const`分组的第一个常量被默认设置为它的0值第二及后续的常量被默认设置为它前面那个常量的值如果前面那个常量的值是`iota`,则它也被设置为`iota`。
###iota枚举
### iota枚举
Go里面有一个关键字`iota`,这个关键字用来声明`enum`的时候采用它默认开始值是0每调用一次加1
@@ -233,14 +233,14 @@ Go里面有一个关键字`iota`,这个关键字用来声明`enum`的时候采
const v = iota // 每遇到一个const关键字iota就会重置此时v == 0
###Go程序设计的一些规则
### Go程序设计的一些规则
Go之所以会那么简洁是因为它有一些默认的行为
- 大写字母开头的变量是可导出的,也就是其它包可以读取的,是公用变量;小写字母开头的就是不可导出的,是私有变量。
- 大写字母开头的函数也是一样,相当于`class`中的带`public`关键词的公有函数;小写字母开头的就是有`private`关键词的私有函数。
##array、slicemap
## array、slicemap
###array
### array
`array`就是数组,它的定义方式如下:
var arr [n]type
@@ -276,7 +276,7 @@ Go之所以会那么简洁是因为它有一些默认的行为
![](images/2.2.array.png?raw=true)
###slice
### slice
在很多应用场景中数组并不能满足我们的需求。在初始定义数组时我们并不知道需要多大的数组因此我们就需要“动态数组”。在Go里面这种数据结构叫`slice`
@@ -360,7 +360,7 @@ slice有一些简便的操作
注:`append`函数会改变`slice`所引用的数组的内容,从而影响到引用同一数组的其它`slice`。
但当`slice`中没有剩余空间(即`(cap-len) == 0`)时,此时将动态分配新的数组空间。返回的`slice`数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的`slice`则不受影响。
###map
### map
`map`也就是Python中字典的概念它的格式为`map[keyType]valueType`
@@ -410,7 +410,7 @@ slice有一些简便的操作
m1["Hello"] = "Salut" // 现在m["hello"]的值已经是Salut了
###make、new操作
### make、new操作
`make`用于内建类型(`map`、`slice` 和`channel`)的内存分配。`new`用于各种类型的内存分配。

28
2.3.md
View File

@@ -1,8 +1,8 @@
#2.3 流程和函数
# 2.3 流程和函数
这小节我们要介绍Go里面的流程控制以及函数操作
##流程控制
## 流程控制
流程控制在编程语言中是最伟大的发明了,因为有了它,你可以通过很简单的流程描述来表达很复杂的逻辑。流程控制包含分三大类:条件判断,循环控制和无条件跳转。
###if
### if
`if`也许是各种编程语言中最常见的了,它的语法概括起来就是:`如果满足条件就做某事,否则做另一件事`
Go里面`if`条件判断语句中不需要括号,如下代码所示
@@ -35,7 +35,7 @@ Go的`if`还有一个强大的地方就是条件判断语句里面允许声明
fmt.Println("The integer is greater than 3")
}
###goto
### goto
Go有`goto`语句——请明智地使用它。用`goto`跳转到必须在当前函数内定义的标签。例如假设这样一个循环:
@@ -49,7 +49,7 @@ Go有`goto`语句——请明智地使用它。用`goto`跳转到必须在当前
>标签名是大小写敏感的。
###for
### for
Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读取数据,又可以当作`while`来控制逻辑,还能迭代操作。它的语法如下:
for expression1; expression2; expression3 {
@@ -117,7 +117,7 @@ Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读
}
###switch
### switch
有些时候你需要写很多的`if-else`来实现一些逻辑处理,这个时候代码看上去就很丑很冗长,而且也不易于以后的维护,这个时候`switch`就能很好的解决这个问题。它的语法如下
switch sExpr {
@@ -176,7 +176,7 @@ Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读
default case
##函数
## 函数
函数是Go里面的核心设计它通过关键字`func`来声明,它的格式如下:
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
@@ -223,7 +223,7 @@ Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读
上面这个里面我们可以看到`max`函数有两个参数,它们的类型都是`int`,那么第一个变量的类型可以省略(即 a,b int,而非 a int, b int)默认为离它最近的类型同理多于2个同类型的变量或者返回值。同时我们注意到它的返回值就是一个类型这个就是省略写法。
###多个返回值
### 多个返回值
Go语言比C更先进的特性其中一点就是函数能够返回多个值。
我们直接上代码看例子
@@ -254,7 +254,7 @@ Go语言比C更先进的特性其中一点就是函数能够返回多个值
return
}
###变参
### 变参
Go函数支持变参。接受变参的函数是有着不定数量的参数的。为了做到这点首先需要定义函数使其接受变参
func myfunc(arg ...int) {}
@@ -264,7 +264,7 @@ Go函数支持变参。接受变参的函数是有着不定数量的参数的。
fmt.Printf("And the number is: %d\n", n)
}
###传值与传指针
### 传值与传指针
当我们传一个参数值到被调用函数里面时实际上是传了这个值的一份copy当在被调用函数中修改参数值的时候调用函数中相应实参不会发生任何变化因为数值变化只作用在copy上。
为了验证我们上面的说法,我们来看一个例子
@@ -323,7 +323,7 @@ Go函数支持变参。接受变参的函数是有着不定数量的参数的。
- 传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销内存和时间。所以当你要传递大的结构体的时候用指针是一个明智的选择。
- Go语言中`string``slice``map`这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变`slice`的长度,则仍需要取地址传递指针)
###defer
### defer
Go里面有一个不错的设计就是回调函数有点类似面向对象语言里面的析构函数当函数执行完之后再执行。特别是当你在进行一些打开资源的操作时遇到错误需要提前返回在返回前你需要关闭相应的资源不然很容易造成资源泄露等问题。如下代码所示我们一般写打开一个资源是这样操作的
func ReadWrite() bool {
@@ -363,7 +363,7 @@ Go里面有一个不错的设计就是回调函数有点类似面向对象
defer fmt.Printf("%d ", i)
}
###函数作为值、类型
### 函数作为值、类型
在Go中函数也是一种变量我们可以通过`type`来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型
@@ -413,7 +413,7 @@ Go里面有一个不错的设计就是回调函数有点类似面向对象
函数当做值和类型在我们写一些通用接口的时候非常有用,通过上面例子我们看到`testInt`这个类型是一个函数类型,然后两个`filter`函数的参数和返回值与`testInt`类型是一样的,但是我们可以实现很多种的逻辑,这样使得我们的程序变得非常的灵活。
###Panic和Recover
### Panic和Recover
Go没有像Java那样的异常机制它不能抛出异常而是使用了`panic``recover`机制。一定要记住,你应当把它作为最后的手段来使用,也就是说,你的代码中应当没有,或者很少有`panic`的东西。这是个强大的工具,请明智地使用它。那么,我们应该如何使用它呢?
@@ -445,7 +445,7 @@ Recover
return
}
###`main`函数和`init`函数
### `main`函数和`init`函数
Go里面有两个保留的函数`init`函数(能够应用于所有的`package`)和`main`函数(只能应用于`package main`)。这两个函数在定义时不能有任何的参数和返回值。虽然一个`package`里面可以写任意多个`init`函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个`package`中只写一个`init`函数。

6
2.4.md
View File

@@ -1,5 +1,5 @@
#2.4 struct类型
##struct
# 2.4 struct类型
## struct
Go语言中也和C或者其他语言一样我们可以声明新的类型作为其它类型的属性或字段的容器。例如我们可以创建一个自定义类型`person`代表一个人的实体。这个实体拥有属性:姓名和年龄。这样的类型我们称之`struct`。如下代码所示:
type person struct {
@@ -78,7 +78,7 @@ Go语言中也和C或者其他语言一样我们可以声明新的类型
bob.name, paul.name, bp_Older.name, bp_diff)
}
###struct的匿名字段
### struct的匿名字段
我们上面介绍了如何定义一个struct定义的时候是字段名与其类型一一对应实际上Go支持只提供类型而不写字段名的方式也就是匿名字段也称为嵌入字段。
当匿名字段是一个struct的时候那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个struct。

10
2.5.md
View File

@@ -1,7 +1,7 @@
#2.5面向对象
# 2.5面向对象
前面两章我们介绍了函数和struct那你是否想过函数当作struct的字段一样来处理呢今天我们就讲解一下函数的另一种形态带有接收者的函数我们称为`method`
##method
## method
现在假设有这么一个场景你定义了一个struct叫做长方形你现在想要计算他的面积那么按照我们一般的思路应该会用下面的方式来实现
package main
@@ -213,7 +213,7 @@ method的语法如下
上面的代码通过文字描述出来之后一看是不是很简单?我们一般解决问题都是通过问题的描述,然后去用这样的代码实现。
###指针作为receivers
### 指针作为receivers
现在让我们回头看看上面的SetColor的method它的receiver是一个指向Box的指针是的你可以使用*Box。想想为啥要使用指针而不是Box本身呢
我们先来看看我们上面SetColor的真正目的我们是想改变这个Box的颜色那么如果我们不传Box的指针那么我们接受的其实是Box的一个copy如果改变了颜色值其实是修改的copy而不是真正的Box。所以我们需要传入指针。
@@ -236,7 +236,7 @@ method的语法如下
所以你不用担心你是调用的指针的method还是不是指针的methodGo知道你要做的一切这对于有多年C/C++编程经验的同学来说,真是解决了一个很大的痛苦。
###method继承
### method继承
前面一章我们学习了字段的继承那么你也会发现Go的一个神奇之处method也是可以继承的。如果匿名字段实现了一个method那么包含这个匿名字段的struct也能调用该method。让我们来看下面这个例子
package main
@@ -271,7 +271,7 @@ method的语法如下
sam.SayHi()
}
###method重载
### method重载
上面的例子中如果Emplyee想要实现自己的SayHi,怎么办简单和匿名字段冲突一样的道理我们可以在Emplyee上面定义一个method重载了匿名字段的方法。请看下面的例子
package main

20
2.6.md
View File

@@ -1,8 +1,8 @@
#2.6interface
# 2.6interface
##interface
## interface
Go语言里面设计最精妙的应该算interface它让面向对象内容组织实现非常的方便当你看完这一章你就会被interface的巧妙设计所折服。
###什么是interface
### 什么是interface
简单的说interface是一组method的组合我们通过interface来定义对象的一组行为。
我们前面一章最后一个例子中Student和Employee都能Sayhi虽然他们的内部实现不一样但是那不重要重要的是他们都能`say hi`
@@ -12,7 +12,7 @@ Go语言里面设计最精妙的应该算interface它让面向对象内容
这样Student实现了三个方法Sayhi、Sing、BorrowMoney而Employee实现了Sayhi、Sing、SpendSalary。
上面这些方法的组合称为interface(被对象Student和Employee实现)。例如Student和Employee都实现了interfaceSayhi和Sing也就是这两个对象是该interface类型。而Employee没有实现这个interfaceSayhi、Sing和BorrowMoney因为Employee没有实现BorrowMoney这个方法。
###interface类型
### interface类型
interface类型定义了一组方法如果某个对象实现了某个接口的所有方法则此对象就实现了此接口。详细的语法参考下面这个例子
type Human struct {
@@ -87,7 +87,7 @@ interface类型定义了一组方法如果某个对象实现了某个接口
最后任意的类型都实现了空interface(我们这样定义interface{})也就是包含0个method的interface。
###interface值
### interface值
那么interface里面到底能存什么值呢如果我们定义了一个interface的变量那么这个变量里面可以存实现这个interface的任意类型的对象。例如上面例子中我们定义了一个Men interface类型的变量m那么m里面可以存Human、Student或者Employee值。
因为m能够持有这三种类型的对象所以我们可以定义一个包含Men类型元素的slice这个slice可以被赋予实现了Men接口的任意结构的对象这个和我们传统意义上面的slice有所不同。
@@ -172,7 +172,7 @@ interface类型定义了一组方法如果某个对象实现了某个接口
通过上面的代码你会发现interface就是一组抽象方法的集合它必须由其他非interface类型实现而不能自我实现 go 通过interface实现了duck-typing:即"当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子"。
###空interface
### 空interface
空interface(interface{})不包含任何的method正因为如此所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method但是空interface在我们需要存储任意类型的数值的时候相当有用因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。
// 定义a为空接口
@@ -184,7 +184,7 @@ interface类型定义了一组方法如果某个对象实现了某个接口
a = s
一个函数把interface{}作为参数那么他可以接受任意类型的值作为参数如果一个函数返回interface{},那么也就可以返回任意类型的值。是不是很有用啊!
###interface函数参数
### interface函数参数
interface的变量可以持有任意实现该interface类型的对象这给我们编写函数(包括method)提供了一些额外的思考我们是不是可以通过定义interface参数让函数接受各种类型的参数。
举个例子我们已经知道fmt.Println是我们常用的一个函数但是你是否注意到它可以接受任意类型的数据。打开fmt的源码文件你会看到这样一个定义:
@@ -222,7 +222,7 @@ interface的变量可以持有任意实现该interface类型的对象这给
fmt.Println("The biggest one is", boxes.BiggestsColor())
实现了error接口的对象即实现了Error() string的对象使用fmt输出时会调用Error()方法因此不必再定义String()方法了。
###interface变量存储的类型
### interface变量存储的类型
我们知道interface的变量里面可以存储任意类型的数值(该类型实现了interface)。那么我们怎么反向知道这个变量里面实际保存了的是哪个类型的对象呢?目前常用的有两种方法:
@@ -322,7 +322,7 @@ interface的变量可以持有任意实现该interface类型的对象这给
这里有一点需要强调的是:`element.(type)`语法不能在switch外的任何逻辑里面使用如果你要在switch外面判断一个类型就使用`comma-ok`
###嵌入interface
### 嵌入interface
Go里面真正吸引人的是他内置的逻辑语法就像我们在学习Struct时学习的匿名字段多么的优雅啊那么相同的逻辑引入到interface里面那不是更加完美了。如果一个interface1作为interface2的一个嵌入字段那么interface2隐式的包含了interface1里面的method。
我们可以看到源码包container/heap里面有这样的一个定义
@@ -353,7 +353,7 @@ Go里面真正吸引人的是他内置的逻辑语法就像我们在学习Str
Writer
}
###反射
### 反射
Go语言实现了反射所谓反射就是动态运行时的状态。我们一般用到的包是reflect包。如何运用reflect包官方的这篇文章详细的讲解了reflect包的实现原理[laws of reflection](http://golang.org/doc/articles/laws_of_reflection.html)
下面我简要的讲解一下一般的使用我们使用reflect大概的分成三步首先我们要去反射是一个类型的值(这些值都实现了空interface)需要把它转化成reflect对象(reflect.Type或者reflect.Value根据不同的情况调用不同的函数)。这两种获取方式如下:

12
2.7.md
View File

@@ -1,8 +1,8 @@
#2.7 并发
# 2.7 并发
有人把Go比作21世纪的C语言第一是因为Go语言设计简单第二21世纪最重要的就是并行程序设计而GO从语言层面就支持了并行。
##Goroutines
## Goroutines
Goroutines是Go并行设计的核心。Goroutines说到底其实就是线程但是他比线程更小十几个Goroutines可能体现在底层就是五六个线程Go语言内部帮你实现了这些Goroutines之间的内存共享。Go语言的作者经常说着这样一句话不要通过共享来通信而要通过通信来共享。
@@ -43,7 +43,7 @@ Goroutines是通过Go的runtime管理的一个线程管理器。Goroutines通过
hello
我们可以看到go关键字很方便的就实现了并发编程。
##channels
## channels
Goroutines运行在相同的地址空间因此访问共享内存必须做好同步。那么Goroutines之间如何进行数据的通信呢Go提供了一个很好的通信机制channel。channel可以与Unix shell 中的双向管道做类比可以通过它发送或者接收值。这些值只能是特定的类型channel类型。定义一个channel 时也需要定义发送到channel 的值的类型。注意必须使用make 创建channel
ci := make(chan int)
@@ -82,7 +82,7 @@ channel通过操作符`<-`来接收和发送数据
默认情况下channel接收和发送数据都是阻塞的除非另一端已经准备好这样就使得Goroutines同步变的更加的简单而不需要显式的lock。所谓阻塞也就是如果读取value := <-ch它将会被阻塞直到有数据接收。其次任何发送ch<-5将会被阻塞直到数据被读出。无缓冲channel 是在多个goroutine之间同步很棒的工具。
##Buffered Channels
## Buffered Channels
上面我们介绍了默认的非缓存类型的channel不过Go也允许指定channel的缓冲大小很简单就是channel可以存储多少元素。ch:= make(chan bool, 4)创建了可以存储4个元素的bool 型channel。在这个channel 中前4个元素可以无阻塞的写入。当写入第5个元素时代码将会阻塞直到其他goroutine从channel 中读取一些元素,腾出空间。
ch := make(chan type, value)
@@ -105,7 +105,7 @@ channel通过操作符`<-`来接收和发送数据
fmt.Println(<-c)
}
##Range和Close
## Range和Close
上面这个例子中我们需要读取两次c这样不是很方便Go考虑到了这一点所以也可以通过range像操作slice或者map一样操作缓存类型的channel请看下面的例子
package main
@@ -137,7 +137,7 @@ channel通过操作符`<-`来接收和发送数据
>另外记住一点的就是channel不像文件之类的不需要经常去关闭只有当你确实没有任何发送数据了或者你想显式的结束range循环之类的
##Select
## Select
我们上面介绍的都是只有一个channel的情况那么如果存在多个channel的时候我们该如何操作呢Go里面提供了一个关键字`select`,通过`select`可以监听channel上的数据流动。
`select`默认是阻塞的只有当监听的channel中有发送或接收可以进行时才会运行当多个channel都准备好的时候select是随机的选择一个执行的。

2
2.8.md
View File

@@ -1,4 +1,4 @@
#2.8总结
# 2.8总结
这一章我们主要介绍了Go语言的一些语法通过语法我们可以发现Go是多么的简单只有二十五个关键字。让我们再来回顾一下这些关键字都是用来干什么的。

14
3.1.md
View File

@@ -1,4 +1,4 @@
#3.1 Web工作方式
# 3.1 Web工作方式
我们平时浏览网页的时候,打开浏览器,输入网址,按下回车键,然后就出来了内容。在这个看似简单的行为背后,到底隐藏了些什么呢?
@@ -17,7 +17,7 @@ Web服务器的工作原理简单的可以归纳为
一个简单的事务处理事件就是这样实现的,看起来很复杂,做起来其实是挺简单的,需要注意的是客户机与服务器之间的通信是非连接的,也就是当服务器发送了应答后就与客户机断开连接,等待下一次请求。
##URL和DNS解析
## URL和DNS解析
我们浏览网页都是通过URL访问的那么URL到底是怎么样的呢
URL(Uniform Resource Locator)是“统一资源定位符”的英文缩写,用于描述一个网络上的资源, 基本格式如下
@@ -56,7 +56,7 @@ URL(Uniform Resource Locator)是“统一资源定位符”的英文缩写,用
通过上面的步骤我们最后获取的是IP地址也就是浏览器最后发起请求的时候是基于IP来和服务器做信息交互的。
##HTTP协议详解
## HTTP协议详解
HTTP协议是Web工作的核心所以要了解清楚Web的工作方式就需要详细的了解清楚HTTP是怎么样工作的。
@@ -66,7 +66,7 @@ HTTP协议是无状态的同一个客户端的这次请求和上次请求是
>HTTP协议是建立在TCP协议之上的因此TCP攻击一样会影响HTTP的通讯例如比较常见的一些攻击SYN Flood是当前最流行的DoS拒绝服务攻击与DdoS分布式拒绝服务攻击的方式之一这是一种利用TCP协议缺陷发送大量伪造的TCP连接请求从而使得被攻击方资源耗尽CPU满负荷或内存不足的攻击方式。
###HTTP请求信息(浏览器信息)
### HTTP请求信息(浏览器信息)
我们先来看看Request消息的结构, Request 消息分为3部分第一部分叫Request line, 第二部分叫Request header,第三部分是body。header和body之间有个空行详细的如下所示
@@ -95,7 +95,7 @@ Http协议定义了很多与服务器交互的方法最基本的有4种
2. GET提交的数据大小有限制因为浏览器对URL的长度有限制而POST方法提交的数据没有限制.
3. GET方式提交数据会带来安全问题比如一个登录页面通过GET方式提交数据时用户名和密码将出现在URL上如果页面可以被缓存或者其他人可以访问这台机器就可以从历史记录获得该用户的账号和密码。
###HTTP响应信息(服务器信息)
### HTTP响应信息(服务器信息)
我们再来看看HTTP的response信息他的结构如下
- 状态行HTTP版本 服务器状态(比如404找不到...) 描述信息
@@ -123,7 +123,7 @@ HTTP/1.1中定义了5类状态码 状态码由三位数字组成,第一个
![](images/3.1.response.png?raw=true)
###HTTP协议是无状态的和Connection: keep-alive的区别
### HTTP协议是无状态的和Connection: keep-alive的区别
无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。从另一方面讲,打开一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系。
HTTP是一个无状态的面向连接的协议无状态不代表HTTP不能保持TCP连接更不能代表HTTP使用的是UDP协议无连接
@@ -132,7 +132,7 @@ HTTP是一个无状态的面向连接的协议无状态不代表HTTP不能保
Keep-Alive不会永久保持连接它有一个保持时间可以在不同的服务器软件如Apache中设定这个时间
##请求实例
## 请求实例
![](images/3.1.web.png?raw=true)

4
3.2.md
View File

@@ -1,8 +1,8 @@
#3.2 GO搭建一个web服务器
# 3.2 GO搭建一个web服务器
前面小节已经介绍了Web是基于http协议的一个服务Go语言里面提供了一个完善的net/http包通过http包可以很方便的就搭建起来一个可以运行的web服务。同时使用这个包能很简单地对web的路由静态文件模版cookie等数据进行设置和操作。
##http包建立web服务器
## http包建立web服务器
package main

6
3.3.md
View File

@@ -1,7 +1,7 @@
#3.3 Go如何使得Web工作
# 3.3 Go如何使得Web工作
前面小节介绍了如何通过Go搭建一个Web服务我们可以看到简单的应用了一个net/http包就方便的搭建起来了。那么他底层到底是怎么做的呢万变不离其宗Go也离不开我们第一小节介绍的Web工作方式。
##对应web工作方式的几个概念
## 对应web工作方式的几个概念
以下均是服务器端的相应概念
@@ -13,7 +13,7 @@ Conn用户的每次请求链接
Handler处理请求和生成返回信息的处理逻辑
##分析http包运行机制
## 分析http包运行机制
如下图所示是Go实现Web工作模式的流程图

8
3.4.md
View File

@@ -1,9 +1,9 @@
#3.4 Go的http包详解
# 3.4 Go的http包详解
前面小节介绍了Go怎么样实现了Web工作模式的一个流程这一小节我们将来详细的解剖一下http包看它到底怎么样实现整个的过程的。
Go的http有两个核心功能Conn、ServeMux
##Conn的goroutine
## Conn的goroutine
与我们一般编写的http服务器不同, Go为了实现高并发和高性能, 使用了goroutines来处理Conn的读写事件, 这样每个请求都能保持独立相互不会阻塞可以高效的响应网络事件。这是Go高效的保证。
Go在等待客户端请求里面是这样写的
@@ -16,7 +16,7 @@ Go在等待客户端请求里面是这样写的
这里我们可以看到客户端的每次请求都会创建一个Conn这个Conn里面保存了该次请求的信息然后再传递到handler的时候可以读取到相应的header信息这样保证了每个请求的独立性。
##ServeMux的自定义
## ServeMux的自定义
我们前面小节讲述conn.server的时候其实内部是调用了http包默认的路由器通过路由器把本次请求的信息传递到了后端的处理函数。那么这个路由器是怎么实现的呢
它的结构如下:
@@ -103,7 +103,7 @@ handler是一个接口但是前一小节中的`sayhelloName`函数并没有
http.ListenAndServe(":9090", mux)
}
##Go代码的执行流程
## Go代码的执行流程
通过对http包的分析之后现在让我们来梳理一下整个的代码执行过程。

2
3.5.md
View File

@@ -1,4 +1,4 @@
#3.5小结
# 3.5小结
这一章我们介绍了HTTP协议, DNS解析的过程, 如何用go实现一个简陋的web server。并深入到net/http包的源码中为大家揭开如何实现此server的秘密。
我希望通过这一章的学习你能够对Go开发Web有了初步的了解我们也看到相应的代码了Go开发Web应用是很方便的同时又是相当的灵活。

2
4.1.md
View File

@@ -1,4 +1,4 @@
#4.1处理表单的输入
# 4.1处理表单的输入
我们先来看一个表单递交的例子我们有如下的表单内容命名成文件login.gtpl(放入当前新建项目的目录里面)

24
4.2.md
View File

@@ -1,10 +1,10 @@
#4.2验证表单的输入
# 4.2验证表单的输入
我们开发Web的一个原则就是不能信任用户输入的任何信息所以验证和过滤用户的输入信息就变得相当重要我们经常会在微博、新闻中听到某某网站被入侵了存在什么漏洞这些大多是是因为对于用户输入的信息没有做严格的验证引起的所以为了编写出安全可靠的Web程序验证表单输入变得相当的重要。
我们平常编写Web应用主要有两方面的数据验证一个是在页面端的js验证(目前很多这方面的验证库),一个是在服务器端的验证,我们这小节讲解的是如何在服务器端验证。
##必填字段
## 必填字段
你想要确保从一个表单元素中得到一个值例如前面小节里面的用户名我们如何处理呢Go有一个内置函数`len`可以获取字符串的长度这样我们就可以通过len来测试获取数据的长度例如
if len(r.Form["username"][0])==0{
@@ -13,7 +13,7 @@
`r.Form`对不同类型的表单元素的留空有不同的处理, 对于空文本框、空文本区域以及文件上传,元素的值为空值,而如果是未选中的复选框和单选按钮则根本不会在r.Form中产生相应条目如果我们用上面例子中的方式去获取数据时程序就会报错。所以我们需要通过`r.Form.Get()`来获取值,因为如果字段不存在,通过该方式获取的是空值。但是通过`r.Form.Get()`只能获取单个的值如果是map的值必须通过上面的方式来获取。
##数字
## 数字
你想要确保一个表单输入框中获取的只能是数字例如你想通过表单获取某个人的具体年龄是50岁还是10岁而不是像“一把年纪了”或“年轻着呢”这种描述
如果我们是判断正整数那么我们先转化成int类型然后进行处理
@@ -38,14 +38,14 @@
>Go实现的正则是[RE2](http://code.google.com/p/re2/wiki/Syntax)所有的字符都是UTF-8编码的。
##中文
## 中文
有时候我们想通过表单元素获取一个用户的中文名字,但是又为了保证获取的是正确的中文,我们需要进行验证,而不是用户随便的一些输入。对于中文我们目前有效的验证只有正则方式来验证,如下代码所示
if m, _ := regexp.MatchString("^[\\x{4e00}-\\x{9fa5}]+$", r.Form.Get("realname")); !m {
return false
}
##英文
## 英文
我们期望通过表单元素获取一个英文值例如我们想知道一个用户的英文名应该是astaxie而不是asta谢。
我们可以很简单的通过正则验证数据:
@@ -55,7 +55,7 @@
}
##电子邮件地址
## 电子邮件地址
你想知道用户输入的一个Email地址是否正确通过如下这个方式可以验证
if m, _ := regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`, r.Form.Get("email")); !m {
@@ -65,14 +65,14 @@
}
##手机号码
## 手机号码
你想要判断用户输入的手机号码是否正确,通过正则也可以验证:
if m, _ := regexp.MatchString(`^(1[3|4|5|8][0-9]\d{4,8})$`, r.Form.Get("mobile")); !m {
return false
}
##下拉菜单
## 下拉菜单
如果我们想要判断表单里面`<select>`元素生成的下拉菜单中是否有被选中的项目。有些时候黑客可能会伪造这个下拉菜单不存在的值发送给你,那么如何判断这个值是否是我们预设的值呢?
我们的select可能是这样的一些元素
@@ -96,7 +96,7 @@
上面这个函数包含在我开源的一个库里面(操作slice和map的库)[https://github.com/astaxie/beeku](https://github.com/astaxie/beeku)
##单选按钮
## 单选按钮
如果我们想要判断radio按钮是否有一个被选中了我们页面的输出可能就是一个男、女性别的选择但是也可能一个15岁大的无聊小孩一手拿着http协议的书另一只手通过telnet客户端向你的程序在发送请求呢你设定的性别男值是1女是2他给你发送一个3你的程序会出现异常吗因此我们也需要像下拉菜单的判断方式类似判断我们获取的值是我们预设的值而不是额外的值。
<input type="radio" name="gender" value="1">男
@@ -113,7 +113,7 @@
}
return false
##复选框
## 复选框
有一项选择兴趣的复选框,你想确定用户选中的和你提供给用户选择的是同一个类型的数据。
<input type="checkbox" name="interest" value="football">足球
@@ -131,7 +131,7 @@
return false
##日期和时间
## 日期和时间
你想确定用户填写的日期或时间是否有效。例如
你想确保用户在日程表中安排8月份的第45天开会或者提供还没到的时间作为生日。
@@ -142,7 +142,7 @@ Go里面提供了一个time的处理包我们可以把用户的输入年月
获取时间之后我们就可以进行很多时间函数的操作。具体的判断就根据自己的需求调整。
##身份证号码
## 身份证号码
如果我们想验证表单输入的是否是身份证通过正则也可以方便的验证但是身份证有15位和18位我们两个都需要验证
//验证15位身份证15位的是全部数字

2
4.3.md
View File

@@ -1,4 +1,4 @@
#4.3 预防跨站脚本
# 4.3 预防跨站脚本
现在的网站包含大量的动态内容以提高用户体验比过去要复杂得多。所谓动态内容就是根据用户环境和需要Web应用程序能够输出相应的内容。动态站点会受到一种名为“跨站脚本攻击”Cross Site Scripting, 安全专家们通常将其缩写成 XSS的威胁而静态站点则完全不受其影响。

2
4.4.md
View File

@@ -1,4 +1,4 @@
#4.4防止多次递交表单
# 4.4防止多次递交表单
不知道你是否曾经看到过一个论坛或者博客,在一个帖子或者文章后面出现多条重复的记录,这些大多数是因为用户重复递交了留言的表单引起的。由于种种原因,用户经常会重复递交表单。通常这只是鼠标的误操作,如双击了递交按钮,也可能是为了编辑或者再次核对填写过的信息,点击了浏览器的后退按钮,然后又再次点击了递交按钮而不是浏览器的前进按钮。当然,也可能是故意的——比如,在某项在线调查或者博彩活动中重复投票。那我们如何有效的防止用户多次递交相同的表单呢?

4
4.5.md
View File

@@ -1,4 +1,4 @@
#4.5处理文件上传
# 4.5处理文件上传
你想处理一个由用户上传的文件。比如你正在建设一个类似Instagram的网站你需要存储用户拍摄的照片。这种需求该如何实现呢
文件要能够上传首先第一步就是要修改form的`enctype`属性,`enctype`属性有如下三种情况:
@@ -79,7 +79,7 @@
![](images/4.5.upload2.png?raw=true)
##客户端上传文件
## 客户端上传文件
我们上面的例子演示了如何通过表单上传文件然后在服务器端处理文件其实Go支持模拟客户端表单功能支持文件上传详细用法请看如下示例

2
4.6.md
View File

@@ -1,4 +1,4 @@
#4.6 小结
# 4.6 小结
这一章里面我们学习了Go里面如何处理表单信息我们通过一个登陆、一个上传例子展示了Go处理form表单信息处理文件上传的能力。但是在处理表单过程中我们需要验证用户输入的信息考虑到网站安全、数据过滤就变得相当重要了因此专门一个小节讲解了各方面的数据过滤顺带讲了一下Go里面对正则的处理。
通过这一章能够让你了解客户端和服务器端如何进行数据的交互,让客户端的数据进入我们的系服务器统,让我们系统处理之后的数据展现给客户端。

28
5.1.md
View File

@@ -1,7 +1,7 @@
#5.1 database/sql接口
# 5.1 database/sql接口
Go和PHP不同的地方是他没有官方提供数据库驱动而是为开发数据库驱动定义了一些标准接口第三方用户可以根据定义的接口来开发相应的数据库驱动这样做有一个好处我们按照标准接口开发的代码 在需要迁移数据库时不需要任何修改。那么Go都定义了那些标准接口呢让我们来详细的分析一下
##sql.Register
## sql.Register
这个存在于database/sql的函数是用来注册数据库驱动的当第三方开发者开发数据库驱动时都会实现init函数在init里面会调用这个`Register(name string, driver driver.Driver)`完成本驱动的注册。
我们来看一下mymysql、sqlite3的驱动里面都是怎么调用的
@@ -34,7 +34,7 @@ Go和PHP不同的地方是他没有官方提供数据库驱动而是为开
>
>新手都会被这个`_`所迷惑其实这个就是Go设计的巧妙之处我们在变量赋值的时候经常看到这个它是用来忽略变量的占位符那么这个包引入也是这儿的意思是引入此包而不直接使用这个包中定义的函数变量等资源我们在2.3流程和函数里面介绍过init函数的初始化过程包在引入的时候会去调用包的init函数以完成对包的初始化因此我们引入上面的数据库驱动包之后会去调用init函数然后在init函数里面注册了这个数据库驱动这样我们就可以在接下来的代码中直接使用这个数据库驱动了。
##driver.Driver
## driver.Driver
Driver是一个数据库驱动的接口他定义了一个method Open(name string)这个方法返回一个数据库的Conn接口。
type Driver interface {
@@ -52,7 +52,7 @@ Driver是一个数据库驱动的接口他定义了一个method Open(name
第三方驱动都会定义这个函数它会解析name参数来获取相关数据库的连接信息解析完成后它将使用此信息来初始化一个Conn并返回它。
##driver.Conn
## driver.Conn
Conn是一个数据库连接的接口定义他定义了一系列方法这个Conn只能应用在一个goroutine里面不能使用在多个goroutine里面详情请参考上面的说明。
type Conn interface {
@@ -67,7 +67,7 @@ Close函数关闭当前的连接以及执行释放连接拥有的资源等清
Begin函数返回一个代表事务处理的Tx通过它你可以进行查询,更新等操作,或者对事务进行回滚、递交。
##driver.Stmt
## driver.Stmt
Stmt是一种准备好的状态和Conn相关联而且是只能应用于一个goroutine中不能应用在多个goroutine中。
type Stmt interface {
@@ -86,7 +86,7 @@ Exec函数执行Prepare准备好的sql传入参数执行update/insert等操
Query函数执行Prepare准备好的sql传入需要的参数执行select操作返回Rows结果集
##driver.Tx
## driver.Tx
事务处理一般就两个过程,递交或者回滚。数据库驱动里面也只需要实现这两个函数就可以
type Tx interface {
@@ -96,7 +96,7 @@ Query函数执行Prepare准备好的sql传入需要的参数执行select操
这两个函数一个用来递交一个事务,一个用来回滚事务。
##driver.Execer
## driver.Execer
这是一个Conn可选择实现的接口
type Execer interface {
@@ -105,7 +105,7 @@ Query函数执行Prepare准备好的sql传入需要的参数执行select操
如果这个接口没有定义那么在调用DB.Exec,就会首先调用Prepare返回Stmt然后执行Stmt的Exec然后关闭Stmt。
##driver.Result
## driver.Result
这个是执行Update/Insert等操作返回的结果接口定义
type Result interface {
@@ -117,7 +117,7 @@ LastInsertId函数返回由数据库执行插入操作得到的自动增长ID号
RowsAffected函数返回query操作影响的数据条目数。
##driver.Rows
## driver.Rows
Rows是执行查询返回的结果集接口定义
type Rows interface {
@@ -136,7 +136,7 @@ Close函数用来关闭Rows迭代器。
Next函数用来返回下一条数据把数据赋值给dest。dest里面的元素必须是driver.Value的值除了string返回的数据里面所有的string都必须要转换成[]byte。如果最后没数据了Next函数最后返回io.EOF。
##driver.RowsAffected
## driver.RowsAffected
RowsAffested其实就是一个int64的别名但是他实现了Result接口用来底层实现Result的表示方式
type RowsAffected int64
@@ -145,7 +145,7 @@ RowsAffested其实就是一个int64的别名但是他实现了Result接口
func (v RowsAffected) RowsAffected() (int64, error)
##driver.Value
## driver.Value
Value其实就是一个空接口他可以容纳任何的数据
type Value interface{}
@@ -159,7 +159,7 @@ Value的值必须所有的驱动里面控制的Value要么是nil要么是
string [*]除了Rows.Next返回的不能是string.
time.Time
##driver.ValueConverter
## driver.ValueConverter
ValueConverter接口定义了如何把一个普通的值转化成driver.Value的接口
type ValueConverter interface {
@@ -172,7 +172,7 @@ ValueConverter接口定义了如何把一个普通的值转化成driver.Value的
- 把数据库查询结果转化成driver.Value值
- 在scan函数里面如何把dirve.Value值转化成用户定义的值
##driver.Valuer
## driver.Valuer
Valuer接口定义了返回一个driver.Value的方式
type Valuer interface {
@@ -182,7 +182,7 @@ Valuer接口定义了返回一个driver.Value的方式
通过上面的讲解,你应该对于驱动的开发有了一个基本的了解,一个驱动只要实现了这些接口就能完成增删查改等基本操作了,剩下的就是与相应的数据库进行数据交互等细节问题了,在此不再赘述。
##database/sql
## database/sql
database/sql在database/sql/driver提供的接口基础上定义了一些更高阶的方法用以简化数据库操作,同时内部还实现了一个建议的conn pool。
type DB struct {

6
5.2.md
View File

@@ -1,7 +1,7 @@
#5.2使用MySQL数据库
# 5.2使用MySQL数据库
目前Internet上流行的网站构架方式是LAMP其中的M即MySQL, 作为数据库MySQL以免费、开源、使用方便为优势成为了很多Web开发的后端数据库存储引擎。
##MySQL驱动
## MySQL驱动
Go中支持MySQL的驱动目前比较多有如下几种有些是支持database/sql标准而有些是采用了自己的实现接口,常用的有如下几种:
- http://code.google.com/p/go-mysql-driver/ 支持database/sql全部采用go写。
@@ -14,7 +14,7 @@ Go中支持MySQL的驱动目前比较多有如下几种有些是支持data
- 完全支持database/sql接口
- 支持keepalive保持长连接,虽然[星星](http://www.mikespook.com)fork的mymysql也支持keepalive但不是线程安全的这个从底层就支持了keepalive。
##示例代码
## 示例代码
接下来的几个小节里面我们都将采用同一个数据库表结构数据库test用户表userinfo关联用户信息表userdetail。
CREATE TABLE `userinfo` (

6
5.3.md
View File

@@ -1,8 +1,8 @@
#5.3使用SQLite数据库
# 5.3使用SQLite数据库
SQLite 是一个开源的嵌入式关系数据库实现自包容、零配置、支持事务的SQL数据库引擎。其特点是高度便携、使用方便、结构紧凑、高效、可靠。 与其他数据库管理系统不同SQLite 的安装和运行非常简单,在大多数情况下,只要确保SQLite的二进制文件存在即可开始创建、连接和使用数据库。如果您正在寻找一个嵌入式数据库项目或解决方案SQLite是绝对值得考虑。SQLite可以是说开源的Access。
##驱动
## 驱动
Go支持sqlite的驱动也比较多但是好多都是不支持database/sql接口的
- https://github.com/mattn/go-sqlite3 支持database/sql接口基于cgo(关于cgo的知识请参看官方文档或者本书后面的章节)写的
@@ -11,7 +11,7 @@ Go支持sqlite的驱动也比较多但是好多都是不支持database/sql接
目前支持database/sql的SQLite数据库驱动只有第一个我目前也是采用它来开发项目的。采用标准接口有利于以后出现更好的驱动的时候做迁移。
##实例代码
## 实例代码
示例的数据库表结构如下所示相应的建表SQL
CREATE TABLE `userinfo` (

6
5.4.md
View File

@@ -1,4 +1,4 @@
#5.4使用PostgreSQL数据库
# 5.4使用PostgreSQL数据库
PostgreSQL 是一个自由的对象-关系数据库服务器(数据库管理系统),它在灵活的 BSD-风格许可证下发行。它提供了相对其他开放源代码数据库系统(比如 MySQL 和 Firebird),和对专有系统比如 Oracle、Sybase、IBM 的 DB2 和 Microsoft SQL Server的一种选择。
@@ -6,7 +6,7 @@ PostgreSQL和MySQL比较它更加庞大一点因为它是用来替代Oracl
现在MySQL被Oracle收购之后有传闻Oracle正在逐步的封闭MySQL,,鉴于此将来我们也许会选择PostgreSQL而不是MySQL作为项目的后端数据库。
##驱动
## 驱动
Go实现的支持PostgreSQL的驱动也很多因为国外很多人在开发中使用了这个数据库。
- https://github.com/bmizerany/pq 支持database/sql驱动纯Go写的
@@ -15,7 +15,7 @@ Go实现的支持PostgreSQL的驱动也很多因为国外很多人在开发
在下面的示例中我采用了第一个驱动因为它目前使用的人最多在github上也比较活跃。
##实例代码
## 实例代码
数据库建表语句:
CREATE TABLE userinfo

20
5.5.md
View File

@@ -1,4 +1,4 @@
#5.5使用beedb库进行ORM开发
# 5.5使用beedb库进行ORM开发
beedb是我开发的一个Go进行ORM操作的库它采用了Go style方式对数据库进行操作实现了struct到数据表记录的映射。beedb是一个十分轻量级的Go ORM框架开发这个库的本意降低复杂的ORM学习曲线尽可能在ORM的运行效率和功能之间寻求一个平衡beedb是目前开源的Go ORM框架中实现比较完整的一个库而且运行效率相当不错功能也基本能满足需求。但是目前还不支持关系关联这个是接下来版本升级的重点。
beedb是支持database/sql标准接口的ORM库所以理论上来说只要数据库驱动支持database/sql接口就可以无缝的接入beedb。目前我测试过的驱动包括下面几个
@@ -15,13 +15,13 @@ MS ADODB: github.com/mattn/go-adodb[*]
ODBC: bitbucket.org/miquella/mgodbc[*]
##安装
## 安装
beedb支持go get方式安装是完全按照Go Style的方式来实现的。
go get github.com/astaxie/beedb
##如何初始化
## 如何初始化
首先你需要import相应的数据库驱动包、database/sql标准接口包以及beedb包如下所示
import (
@@ -64,7 +64,7 @@ beedb的New函数实际上应该有两个参数第一个参数标准接口的
>注意一点beedb针对驼峰命名会自动帮你转化成下划线字段例如你定义了Struct名字为`UserInfo`,那么转化成底层实现的时候是`user_info`,字段命名也遵循该规则。
##插入数据
## 插入数据
下面的代码演示了如何插入一条记录可以看到我们操作的是strcut对象而不是原生的sql语句最后通过调用Save接口将数据保存到数据库。
var saveone Userinfo
@@ -101,7 +101,7 @@ beedb接口提供了另外一种插入的方式map数据插入。
上面我们调用的SetTable函数是显式的告诉ORM我要执行的这个map对应的数据库表是`userinfo`
##更新数据
## 更新数据
继续上面的例子来演示更新操作现在saveone的主键已经有值了此时调用save接口beedb内部会自动调用update以进行数据的更新而非插入操作。
saveone.Username = "Update Username"
@@ -122,7 +122,7 @@ SetPK显式的告诉ORM数据库表`userinfo`的主键是`uid`。
Where:用来设置条件支持多个参数第一个参数如果为整数相当于调用了Where("主键=?",值)。
Updata函数接收map类型的数据执行更新数据。
##查询数据
## 查询数据
beedb的查询接口比较灵活具体使用请看下面的例子
例子1根据主键获取数据
@@ -180,7 +180,7 @@ OrderBy:这个函数用来进行查询排序,参数是需要排序的条件。
FindMap()函数返回的是`[]map[string][]byte`类型,所以你需要自己作类型转换。
##删除数据
## 删除数据
beedb提供了丰富的删除数据接口请看下面的例子
例子1删除单条数据
@@ -198,7 +198,7 @@ beedb提供了丰富的删除数据接口请看下面的例子
orm.SetTable("userinfo").Where("uid>?", 3).DeleteRow()
##关联查询
## 关联查询
目前beedb还不支持struct的关联关系但是有些应用却需要用到连接查询所以现在beedb提供了一个简陋的实现方案
a, _ := orm.SetTable("userinfo").Join("LEFT", "userdeatail", "userinfo.uid=userdeatail.uid").Where("userinfo.uid=?", 1).Select("userinfo.uid,userinfo.username,userdeatail.profile").FindMap()
@@ -210,7 +210,7 @@ beedb提供了丰富的删除数据接口请看下面的例子
- 第三个参数表示连接的条件
##Group By和Having
## Group By和Having
针对有些应用需要用到group by和having的功能beedb也提供了一个简陋的实现
a, _ := orm.SetTable("userinfo").GroupBy("username").Having("username='astaxie'").FindMap()
@@ -221,7 +221,7 @@ GroupBy:用来指定进行groupby的字段
Having:用来指定having执行的时候的条件
##进一步的发展
## 进一步的发展
目前beedb已经获得了很多来自国内外用户的反馈我目前也正在考虑重构接下来会在几个方面进行改进
- 实现interface设计类似databse/sql/driver的设计设计beedb的接口然后去实现相应数据库的CRUD操作

6
5.6.md
View File

@@ -1,9 +1,9 @@
#5.6NOSQL数据库操作
# 5.6NOSQL数据库操作
NoSQL(Not Only SQL)指的是非关系型的数据库。随着Web2.0的兴起传统的关系数据库在应付Web2.0网站特别是超大规模和高并发的SNS类型的Web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。
而Go语言作为21世纪的C语言对NOSQL的支持也是很好目前流行的NOSQL主要有redis、mongoDB、Cassandra和Membase等。这些数据库都有高性能、高并发读写等特点目前已经广泛应用于各种应用中。我接下来主要讲解一下redis和mongoDB的操作。
##redis
## redis
redis是一个key-value存储系统。和Memcached类似它支持存储的value类型相对更多包括string(字符串)、list(链表)、set(集合)和zset(有序集合)。
目前应用redis最广泛的应该是新浪微博平台其次还有Facebook收购的图片社交网站instagram。以及其他一些有名的[互联网企业](http://redis.io/topics/whos-using-redis)
@@ -52,7 +52,7 @@ https://github.com/astaxie/goredis
我们可以看到操作redis非常的方便而且我实际项目中应用下来性能也很高。client的命令和redis的命令基本保持一致。所以和原生态操作redis非常类似。
##mongoDB
## mongoDB
MongoDB是一个高性能开源无模式的文档型数据库是一个介于关系数据库和非关系数据库之间的产品是非关系数据库当中功能最丰富最像关系数据库的。他支持的数据结构非常松散采用的是类似json的bjson格式来存储数据因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大其语法有点类似于面向对象的查询语言几乎可以实现类似关系数据库单表查询的绝大部分功能而且还支持对数据建立索引。

2
5.7.md
View File

@@ -1,4 +1,4 @@
#5.7小结
# 5.7小结
这一章我们讲解了Go如何设计database/sql接口然后介绍了各种第三方关系型数据库驱动的使用。接着介绍了beedb一种基于关系型数据库的ORM库如何对数据库进行简单的操作。最后介绍了NOSQL的一些知识目前Go对于NOSQL支持还是不错因为Go作为21世纪的C语言那么对于21世纪的数据库也是支持的相当好。
通过这一章的学习我们学会了如何操作各种数据库那么就解决了我们数据存储的问题这是Web里面最重要的一部分所以希望大家能够深入的去了解database/sql的设计思想。

2
5.md
View File

@@ -1,4 +1,4 @@
#5 访问数据库
# 5 访问数据库
对许多Web应用程序而言数据库都是其核心所在。数据库几乎可以用来存储你想查询和修改的任何信息比如用户信息、产品目录或者新闻列表等。
Go没有内置的驱动支持任何的数据库但是Go定义了database/sql接口用户可以基于驱动接口开发相应数据库的驱动5.1小节里面介绍Go设计的一些驱动介绍Go是如何设计数据库驱动接口的。5.2至5.4小节介绍目前使用的比较多的一些关系型数据驱动已经如何使用5.5小节介绍我自己开发一个ORM库基于database/sql标准接口开发的可以兼容几乎所有支持database/sql的数据库驱动可以方便的使用Go style来进行数据库操作。

12
6.1.md
View File

@@ -1,4 +1,4 @@
#6.1 session和cookie
# 6.1 session和cookie
session和cookie是网站浏览中较为常见的两个概念也是比较难以辨析的两个概念但它们在浏览需要认证的服务页面以及页面统计中却相当关键。我们先来了解一下session和cookie怎么来的考虑这样一个问题
如何抓取一个访问受限的网页?如新浪微博好友的主页,个人微博页面等。
@@ -15,7 +15,7 @@ session简而言之就是在服务器上保存用户操作的历史信息。
![](images/6.1.session.png?raw=true)
##cookie
## cookie
Cookie是由浏览器维持的存储在客户端的一小段文本信息伴随着用户请求和页面在Web服务器和浏览器之间传递。用户每次访问站点时Web应用程序都可以读取cookie包含的信息。浏览器设置里面有cookie隐私数据选项打开它可以看到很多已访问网站的cookies如下图所示
![](images/6.1.cookie.png?raw=true)
@@ -27,7 +27,7 @@ cookie是有时间限制的根据生命期不同分成两种会话cookie
如果设置了过期时间(setMaxAge(60*60*24))浏览器就会把cookie保存到硬盘上关闭后再次打开浏览器这些cookie依然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏览器进程间共享比如两个IE窗口。而对于保存在内存的cookie不同的浏览器有不同的处理方式。
  
###Go设置cookie
### Go设置cookie
Go语言中通过net/http包中的SetCookie来设置
http.SetCookie(w ResponseWriter, cookie *Cookie)
@@ -60,7 +60,7 @@ w表示需要写入的responsecookie是一个struct让我们来看一下co
http.SetCookie(w, &cookie)
  
###Go读取cookie
### Go读取cookie
上面的例子演示了如何设置cookie数据我们这里来演示一下如何读取cookie
cookie, _ := r.Cookie("username")
@@ -74,7 +74,7 @@ w表示需要写入的responsecookie是一个struct让我们来看一下co
可以看到通过request获取cookie非常方便。
##session
## session
session中文经常翻译为会话其本来的含义是指有始有终的一系列动作/消息比如打电话是从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个session。然而当session一词与网络协议相关联时它又往往隐含了“面向连接”和/或“保持状态”这样两个含义。
@@ -86,7 +86,7 @@ session机制是一种服务器端的机制服务器使用一种类似于散
session机制本身并不复杂然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器服务器的经验当作普遍适用的。
##小结
## 小结
如上文所述session和cookie的目的相同都是为了克服http协议无状态的缺陷但完成的方法不同。session通过cookie在客户端保存session id而将用户的其他会话消息保存在服务端的session对象中与此相对的cookie需要将所有信息都保存在客户端。因此cookie存在着一定的安全隐患例如本地cookie中保存的用户名密码被破译或cookie被其他网站收集例如1. appA主动设置域B cookie让域B cookie获取2. XSS在appA上通过javascript获取document.cookie并传递给自己的appB

22
6.2.md
View File

@@ -1,7 +1,7 @@
#6.2 Go如何使用session
# 6.2 Go如何使用session
通过上一小节的介绍我们知道session是在服务器端实现的一种用户和服务器之间认证的解决方案目前Go标准包没有为session提供任何支持这小节我们将会自己动手来实现go版本的session管理和创建。
##session创建过程
## session创建过程
session的基本原理是由服务器为每个会话维护一份信息数据客户端和服务端依靠一个全局唯一的标识来访问这份数据以达到交互的目的。当用户访问Web应用时服务端程序会随需要创建session这个过程可以概括为三个步骤
- 生成全局唯一标识符sessionid
@@ -15,10 +15,10 @@ session的基本原理是由服务器为每个会话维护一份信息数据
2. URL重写
所谓URL重写就是在返回给用户的页面里的所有的URL后面追加session标识符这样用户在收到响应之后无论点击响应页面里的哪个链接或提交表单都会自动带上session标识符从而就实现了会话的保持。虽然这种做法比较麻烦但是如果客户端禁用了cookie的话此种方案将会是首选。
##Go实现session管理
## Go实现session管理
通过上面session创建过程的讲解读者应该对session有了一个大体的认识但是具体到动态页面技术里面又是怎么实现session的呢下面我们将结合session的生命周期lifecycle来实现go语言版本的session管理。
###session管理设计
### session管理设计
我们知道session管理涉及到如下几个因素
- 全局session管理器
@@ -29,7 +29,7 @@ session的基本原理是由服务器为每个会话维护一份信息数据
接下来我将讲解一下我关于session管理的整个设计思路以及相应的go代码示例
###Session管理器
### Session管理器
定义一个全局的session管理器
@@ -96,7 +96,7 @@ Go实现整个的流程应该也是这样的在main包中创建一个全部
provides[name] = provide
}
###全局唯一的Session ID
### 全局唯一的Session ID
Session ID是用来识别访问Web应用的每一个用户因此必须保证它是全局唯一的GUID下面代码展示了如何满足这一需求
@@ -108,7 +108,7 @@ Session ID是用来识别访问Web应用的每一个用户因此必须保证
return base64.URLEncoding.EncodeToString(b)
}
###session创建
### session创建
我们需要为每个来访用户分配或获取与他相关连的Session以便后面根据Session信息来验证操作。SessionStart这个函数就是用来检测是否已经有某个Session与当前来访用户发生了关联如果没有则创建之。
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session Session) {
@@ -142,7 +142,7 @@ Session ID是用来识别访问Web应用的每一个用户因此必须保证
}
}
###操作值:设置、读取和删除
### 操作值:设置、读取和删除
SessionStart函数返回的是一个满足Session接口的变量那么我们该如何用他来对session数据进行操作呢
上面的例子中的代码`session.Get("uid")`已经展示了基本的读取数据的操作,现在我们再来看一下详细的操作:
@@ -171,7 +171,7 @@ SessionStart函数返回的是一个满足Session接口的变量那么我们
因为Session有过期的概念所以我们定义了GC操作当访问过期时间满足GC的触发条件后将会引起GC但是当我们进行了任意一个session操作都会对Session实体进行更新都会触发对最后访问时间的修改这样当GC的时候就不会误删除还在使用的Session实体。
###session重置
### session重置
我们知道Web应用中有用户退出这个操作那么当用户退出应用的时候我们需要对该用户的session数据进行销毁操作上面的代码已经演示了如何使用session重置操作下面这个函数就是实现了这个功能
//Destroy sessionid
@@ -190,7 +190,7 @@ SessionStart函数返回的是一个满足Session接口的变量那么我们
}
###session销毁
### session销毁
我们来看一下Session管理器如何来管理销毁,只要我们在Main启动的时候启动
func init() {
@@ -206,7 +206,7 @@ SessionStart函数返回的是一个满足Session接口的变量那么我们
我们可以看到GC充分利用了time包中的定时器功能当超时`maxLifeTime`之后调用GC函数这样就可以保证`maxLifeTime`时间内的session都是可用的类似的方案也可以用于统计在线用户数之类的。
##总结
## 总结
至此 我们实现了一个用来在Web应用中全局管理Session的SessionManager定义了用来提供Session存储实现Provider的接口,下一小节我们将会通过接口定义来实现一些Provider,供大家参考学习。
## links

2
6.3.md
View File

@@ -1,4 +1,4 @@
#6.3 session存储
# 6.3 session存储
上一节我们介绍了Session管理器的实现原理定义了存储session的接口这小节我们将示例一个基于内存的session存储接口的实现其他的存储方式读者可以自行参考示例来实现内存的实现请看下面的例子代码
package memory

10
6.4.md
View File

@@ -1,8 +1,8 @@
#6.4 预防session劫持
# 6.4 预防session劫持
session劫持是一种广泛存在的比较严重的安全威胁在session技术中客户端和服务端通过session的标识符来维护会话 但这个标识符很容易就能被嗅探到,从而被其他人利用.它是中间人攻击的一种类型。
本节将通过一个实例来演示会话劫持希望通过这个实例能让读者更好地理解session的本质。
##session劫持过程
## session劫持过程
我们写了如下的代码来展示一个count计数器
func count(w http.ResponseWriter, r *http.Request) {
@@ -41,8 +41,8 @@ count.gtpl的代码如下所示
![](images/6.4.hijacksuccess.png?raw=true)
可以看到虽然换了浏览器但是我们却获得了sessionID然后模拟了cookie存储的过程。这个例子是在同一台计算机上做的不过即使换用两台来做其结果仍然一样。此时如果交替点击两个浏览器里的链接你会发现它们其实操纵的是同一个计数器。不必惊讶此处firefox盗用了chrome和goserver之间的维持会话的钥匙即gosessionid这是一种类型的“会话劫持”。在goserver看来它从http请求中得到了一个gosessionid由于HTTP协议的无状态性它无法得知这个gosessionid是从chrome那里“劫持”来的它依然会去查找对应的session并执行相关计算。与此同时 chrome也无法得知自己保持的会话已经被“劫持”。
##session劫持防范
###cookieonly和token
## session劫持防范
### cookieonly和token
通过上面session劫持的简单演示可以了解到session一旦被其他人劫持就非常危险劫持者可以假装成被劫持者进行很多非法操作。那么如何有效的防止session劫持呢
其中一个解决方案就是sessionID的值只允许cookie设置而不是通过URL重置方式设置同时设置cookie的httponly为true,这个属性是设置是否可通过客户端脚本访问这个设置的cookie第一这个可以防止这个cookie被XSS读取从而引起session劫持第二cookie设置不会像URL重置方式那么容易获取sessionID。
@@ -59,7 +59,7 @@ count.gtpl的代码如下所示
sess.Set("token",token)
###间隔生成新的SID
### 间隔生成新的SID
还有一个解决方案就是我们给session额外设置一个创建时间的值一旦过了一定的时间我们销毁这个sessionID重新生成新的session这样可以一定程度上防止session劫持的问题。
createtime := sess.Get("createtime")

2
6.5.md
View File

@@ -1,4 +1,4 @@
#6.5 小结
# 6.5 小结
这章我们学习了什么是session什么是cookie以及他们两者之间的关系。但是目前Go官方标准包里面不支持session所以我们设计了一个session管理器实现了session从创建到销毁的整个过程。然后定义了Provider的接口使得可以支持各种后端的session存储然后我们在第三小节里面介绍了如何使用内存存储来实现session的管理。第四小节我们讲解了session劫持的过程以及我们如何有效的来防止session劫持。通过这一章的讲解希望能够让读者了解整个sesison的执行原理以及如何实现而且是如何更加安全的使用session。
## links
* [目录](<preface.md>)

2
6.md
View File

@@ -1,4 +1,4 @@
#6 session和数据存储
# 6 session和数据存储
Web开发中一个很重要的议题就是如何做好用户的整个浏览过程的控制因为HTTP协议是无状态的所以用户的每一次请求都是无状态的我们不知道在整个Web操作过程中哪些连接与该用户有关我们应该如何来解决这个问题呢Web里面经典的解决方案是cookie和sessioncookie机制是一种客户端机制把用户数据保存在客户端而session机制是一种服务器端的机制服务器使用一种类似于散列表的结构来保存信息每一个网站访客都会被分配给一个唯一的标志符,即sessionID,它的存放形式无非两种:要么经过url传递,要么保存在客户端的cookies里.当然,你也可以将Session保存到数据库里,这样会更安全,但效率方面会有所下降。
6.1小节里面讲介绍session机制和cookie机制的关系和区别6.2讲解Go语言如何来实现session里面讲实现一个简易的session管理器6.3小节讲解如何防止session被劫持的情况如何有效的保护session。我们知道session其实可以存储在任何地方6.3小节里面实现的session是存储在内存中的但是如果我们的应用进一步扩展了要实现应用的session共享那么我们可以把session存储在数据库中(memcache或者redis)6.4小节将详细的讲解如何实现这些功能。

6
7.1.md
View File

@@ -1,4 +1,4 @@
#7.1 XML处理
# 7.1 XML处理
XML作为一种数据交换和信息传递的格式已经十分普及。而随着Web服务日益广泛的应用现在XML在日常的开发工作中也扮演了愈发重要的角色。这一小节 我们将就Go语言标准包中的XML相关处理的包进行介绍。
这个小节不会涉及XML规范相关的内容如需了解相关知识请参考其他文献而是介绍如何用Go语言来编解码XML文件相关的知识。
@@ -19,7 +19,7 @@ XML作为一种数据交换和信息传递的格式已经十分普及。而随
上面的XML文档描述了两个服务器的信息包含了服务器名和服务器的IP信息接下来的Go例子以此XML描述的信息进行操作。
##解析XML
## 解析XML
如何解析如上这个XML文件喃呢 我们可以通过xml包的`Unmarshal`函数来达到我们的目的
func Unmarshal(data []byte, v interface{}) error
@@ -120,7 +120,7 @@ Go语言的反射机制可以利用这些tag信息来将来自XML文件中的
>注意: 为了正确解析go语言的xml包要求struct定义中的所有字段必须是可导出的即首字母大写
##输出XML
## 输出XML
假若我们不是要解析如上所示的XML文件而是生成它那么在go语言中又该如何实现呢 xml包中提供了`Marshal``MarshalIndent`两个函数,来满足我们的需求。这两个函数主要的区别是第二个函数会增加前缀和缩进,函数的定义如下所示:
func Marshal(v interface{}) ([]byte, error)

10
7.2.md
View File

@@ -1,4 +1,4 @@
#7.2 JSON处理
# 7.2 JSON处理
JSONJavascript Object Notation是一种轻量级的数据交换语言以文字为基础具有自我描述性且易于让人阅读。尽管JSON是Javascript的一个子集但JSON是独立于语言的文本格式并且采用了类似于C语言家族的一些习惯。JSON与XML最大的不同在于XML是一个完整的标记语言而JSON不是。JSON由于比XML更小、更快更易解析,以及浏览器的内建快速解析支持,使得其更适用于网络数据传输领域。目前我们看到很多的开放平台基本上都是采用了JSON作为他们的数据交互的接口。既然JSON在Web开发中如此重要那么Go语言对JSON支持的怎么样呢Go语言的标准库已经非常好的支持了JSON可以很容易的对JSON数据进行编、解码的工作。
前一小节的运维的例子用json来表示结果描述如下
@@ -6,9 +6,9 @@ JSONJavascript Object Notation是一种轻量级的数据交换语言
{"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]}
本小节余下的内容将以此JSON数据为基础来介绍go语言的json包对JSON数据的编、解码。
##解析JSON
## 解析JSON
###解析到结构体
### 解析到结构体
假如有了上面的JSON串那么我们如何来解析这个JSON串呢Go的JSON包中有如下函数
func Unmarshal(data []byte, v interface{}) error
@@ -46,7 +46,7 @@ JSONJavascript Object Notation是一种轻量级的数据交换语言
聪明的你一定注意到了这一点:能够被赋值的字段必须是可导出字段(即首字母大写。同时JSON解析的时候只会解析能找得到的字段如果找不到的字段会被忽略这样的一个好处是当你接收到一个很大的JSON数据结构而你却只想获取其中的部分数据的时候你只需将你想要的数据对应的字段名大写即可轻松解决这个问题。
###解析到interface
### 解析到interface
上面那种解析方式是在我们知晓被解析的JSON数据的结构的前提下采取的方案如果我们不知道被解析的数据的格式又应该如何来解析呢
我们知道interface{}可以用来存储任意数据类型的对象这种数据结构正好用于存储解析的未知结构的json数据的结果。JSON包中采用map[string]interface{}和[]interface{}结构来存储任意的JSON对象和数组。Go类型和JSON类型的对应关系如下
@@ -118,7 +118,7 @@ JSONJavascript Object Notation是一种轻量级的数据交换语言
可以看到使用这个库操作JSON比起官方包来说简单的多,详细的请参考如下地址https://github.com/bitly/go-simplejson
##生成JSON
## 生成JSON
我们开发很多应用的时候最后都是要输出JSON数据串那么如何来处理呢JSON包里面通过`Marshal`函数来处理,函数定义如下:
func Marshal(v interface{}) ([]byte, error)

6
7.3.md
View File

@@ -1,4 +1,4 @@
#7.3 正则处理
# 7.3 正则处理
正则表达式是一种进行模式匹配和文本操纵的复杂而又强大的工具。虽然正则表达式比纯粹的文本匹配效率低但是它却更灵活。按照它的语法规则随需构造出的匹配模式就能够从原始文本中筛选出几乎任何想你要得到的字符组合。如果你在Web开发中需要从一些文本数据源中获取数据,那么你只需要按照它的语法规则,随需构造出正确的模式字符串就能够从原数据源提取出有意义的文本信息。
Go语言通过`regexp`标准包为正则表达式提供了官方支持如果你已经使用过其他编程语言提供的正则相关功能那么你应该对Go语言版本的不会太陌生但是它们之间也有一些小的差异因为Go实现的是RE2标准除了\C详细的语法描述参考http://code.google.com/p/re2/wiki/Syntax。
@@ -7,7 +7,7 @@ Go语言通过`regexp`标准包为正则表达式提供了官方支持,如果
如果你还记得在前面表单验证的小节里我们已经接触过正则处理在那里我们利用了它来验证输入的信息是否满足某些预设的条件。在使用中需要注意的一点就是所有的字符都是UTF-8编码的。接下来让我们更加深入的来学习Go语言的`regexp`包相关知识吧。
##通过正则判断是否匹配
## 通过正则判断是否匹配
`regexp`包中含有三个函数用来判断是否匹配如果匹配返回true否则返回false
func Match(pattern string, b []byte) (matched bool, error error)
@@ -40,7 +40,7 @@ Go语言通过`regexp`标准包为正则表达式提供了官方支持,如果
在上面的两个小例子中我们采用了Match(Reader|String)来判断一些字符串是否符合我们的描述需求,它们使用起来非常方便。
##通过正则获取内容
## 通过正则获取内容
Match模式只能用来对字符串的判断而无法截取字符串的一部分、过滤字符串、或者提取出符合条件的一批字符串。如果想要满足这些需求那就需要使用正则表达式的复杂模式。
我们经常需要一些爬虫程序,下面就以爬虫为例来说明如何使用正则来过滤或截取抓取到的数据:

26
7.4.md
View File

@@ -1,5 +1,5 @@
#7.4 模板处理
##什么是模板
# 7.4 模板处理
## 什么是模板
你一定听说过一种叫做MVC的设计模式Model处理数据View展现结果Controller控制用户的请求至于View层的处理在很多动态语言里面都是通过在静态HTML中插入动态语言生成的数据例如JSP中通过插入`<%=....=%>`PHP中通过插入`<?php.....?>`来实现的。
通过下面这个图可以说明模板的机制
@@ -8,7 +8,7 @@
Web应用反馈给客户端的信息中的大部分内容是静态的不变的而另外少部分是根据用户的请求来动态生成的例如要显示用户的访问记录列表。用户之间只有记录数据是不同的而列表的样式则是固定的此时采用模板可以复用很多静态代码。
##Go模板使用
## Go模板使用
在Go语言中我们使用`template`包来进行模板处理,使用类似`Parse``ParseFile``Execute`等方法从文件或者字符串加载模板然后执行类似上面图片展示的模板的merge操作。请看下面的例子
func handler(w http.ResponseWriter, r *http.Request) {
@@ -26,10 +26,10 @@ Web应用反馈给客户端的信息中的大部分内容是静态的不变
- 不使用handler来写演示代码而是每个测试一个main方便测试
- 使用`os.Stdout`代替`http.ResponseWriter`,因为`os.Stdout`实现了`io.Writer`接口
##模板中如何插入数据?
## 模板中如何插入数据?
上面我们演示了如何解析并渲染模板接下来让我们来更加详细的了解如何把数据渲染出来。一个模板都是应用在一个Go的对象之上Go对象的字段如何插入到模板中呢
###字段操作
### 字段操作
Go语言的模板通过`{{}}`来包含需要在渲染时被替换的字段,`{{.}}`表示当前的对象这和Java或者C++中的this类似如果要访问当前对象的字段通过`{{.FieldName}}`,但是需要注意一点:这个字段必须是导出的(字段首字母必须是大写的),否则在渲染的时候就会报错,请看下面的这个例子:
package main
@@ -63,7 +63,7 @@ Go语言的模板通过`{{}}`来包含需要在渲染时被替换的字段,`{{
如果模板中输出`{{.}}`这个一般应用与字符串对象默认会调用fmt包输出字符串的内容。
###输出嵌套字段内容
### 输出嵌套字段内容
上面我们例子展示了如何针对一个对象的字段输出,那么如果字段里面还有对象,如何来循环的输出这些内容呢?我们可以使用`{{with …}}…{{end}}``{{range …}}{{end}}`来进行数据的输出。详细的使用请看下面的例子:
package main
@@ -103,14 +103,14 @@ Go语言的模板通过`{{}}`来包含需要在渲染时被替换的字段,`{{
t.Execute(os.Stdout, p)
}
###pipelines
### pipelines
Unix用户已经很熟悉什么是`pipe`了,`ls | grep "name"`类似这样的语法你是不是经常使用,过滤当前目录下面的文件,显示含有"name"的数据他表达的意思就是前面的输出可以当做后面的输入最后显示我们想要的数据而Go语言模板最强大的一点就是支持pipe数据在Go语言里面任何`{{}}`里面的都是pipelines数据例如我们上面输出的email里面如果还有一些可能引起XSS注入的那么我们如何来进行转化呢
{{. | html}}
在email输出的地方我们可以采用如上方式可以把输出全部转化html的实体上面的这种方式和我们平常写Unix的方式是不是一模一样操作起来相当的简便调用其他的函数也是类似的方式。
###条件处理
### 条件处理
在Go模板里面如果需要进行条件判断那么我们可以使用和Go语言的`if-else`语法类似的方式来咱先如果pipeline为空那么if就认为是false下面的例子展示了如何使用`if-else`语法:
package main
@@ -136,7 +136,7 @@ Unix用户已经很熟悉什么是`pipe`了,`ls | grep "name"`类似这样的
通过上面的演示代码我们知道`if-else`语法相当的简单,在使用过程中很容易集成到我们的模板代码中。
###模板变量
### 模板变量
有时候,我们在模板使用过程中需要定义一些局部变量,我们可以在一些操作中申明局部变量,例如`with``range``if`过程中申明局部变量,这个变量的作用域是`{{end}}`之前Go语言通过申明的局部变量格式如下所示
$variable := pipeline
@@ -146,7 +146,7 @@ Unix用户已经很熟悉什么是`pipe`了,`ls | grep "name"`类似这样的
{{with $x := "output" | printf "%q"}}{{$x}}{{end}}
{{with $x := "output"}}{{printf "%q" $x}}{{end}}
{{with $x := "output"}}{{$x | printf "%q"}}{{end}}
###模板函数
### 模板函数
模板在输出对象的字段值时,采用了`fmt`包把对象转化成了字符串。但是有时候我们的需求可能不是这样的,例如有时候我们为了防止垃圾邮件发送者通过采集网页的方式来发送给我们的邮箱信息,我们希望把`@`替换成`at`例如:`astaxie at beego.me`,如果要实现这样的功能,我们就需要自定义函数来做这个功能。
每一个模板函数都有一个唯一值的名字然后与一个Go函数关联通过如下的方式来关联
@@ -240,7 +240,7 @@ Unix用户已经很熟悉什么是`pipe`了,`ls | grep "name"`类似这样的
}
##Must操作
## Must操作
模板包里面有一个函数`Must`它的作用是检测模板是否正确例如大括号是否匹配注释是否正确的关闭变量是否正确的书写。接下来我们演示一个例子用Must来判断模板是否正确
package main
@@ -270,7 +270,7 @@ Unix用户已经很熟悉什么是`pipe`了,`ls | grep "name"`类似这样的
The next one ought to fail.
panic: template: check parse error with Must:1: unexpected "}" in command
##嵌套模板
## 嵌套模板
我们平常开发Web应用的时候经常会遇到一些模板有些部分是固定不变的然后可以抽取出来作为一个独立的部分例如一个博客的头部和尾部是不变的而唯一改变的是中间的内容部分。所以我们可以定义成`header``content``footer`三个部分。Go语言中通过如下的语法来申明
{{define "子模板名称"}}内容{{end}}
@@ -332,7 +332,7 @@ Unix用户已经很熟悉什么是`pipe`了,`ls | grep "name"`类似这样的
>同一个集合类的模板是互相知晓的,如果同一模板被多个集合使用,则它需要在多个集合中分别解析
##总结
## 总结
通过上面对模板的详细介绍我们了解了如何把动态数据与模板融合如何输出循环数据、如何自定义函数、如何嵌套模板等等。通过模板技术的应用我们可以完成MVC模式中V的处理接下来的章节我们将介绍如何来处理M和C。
## links

2
7.5.md
View File

@@ -1,4 +1,4 @@
#7.5 小结
# 7.5 小结
这一章给大家介绍了一些文本处理的工具包括XML、JSON、正则和模板技术XML和JSON是数据交互的工具通过XML和JSON你可以表达各种含义通过正则你可以处理文本(搜索、替换、截取)通过模板技术你可以展现这些数据给用户。这些都是你开发Web应用过程中需要用到的技术通过这个小节的介绍你能够了解如何处理文本、展现文本。
## links

2
7.md
View File

@@ -1,4 +1,4 @@
#7 文本处理
# 7 文本处理
Web开发中对于文本处理是非常重要的一部分我们往往需要对输出或者输入的内容进行处理这里的文本包括字符串、数字、Json、XMl等等。Go语言作为一门高性能的语言对这些文本的处理都有官方的标准库来支持。而且在你使用中你会发现Go标准库的一些设计相当的巧妙而且对于使用者来说也很方便就能处理这些文本。本章我们将通过四个小节的介绍让用户对Go语言处理文本有一个很好的认识。
XML是目前很多标准接口的交互语言很多时候和一些Java编写的webserver进行交互都是基于XML标准进行交互7.1小节将介绍如何处理XML文本我们使用XML之后发现它太复杂了现在很多互联网企业对外的API大多数采用了JSON格式这种格式描述简单但是又能很好的表达意思7.2小节我们将讲述如何来处理这样的JSON格式数据。正则是一个让人又爱又恨的工具它处理文本的能力非常强大我们在前面表单验证里面已经有所领略它的强大7.3小节将详细的更深入的讲解如何利用好Go的正则。Web开发中一个很重要的部分就是MVC分离在Go语言的Web开发中V有一个专门的包来支持`template`,7.4小节将详细的讲解如何使用模版来进行输出内容。

26
8.1.md
View File

@@ -1,33 +1,33 @@
#8.1 Socket编程
# 8.1 Socket编程
在很多底层网络应用开发者的眼里一切编程都是Socket话虽然有点夸张但却也几乎如此了现在的网络编程几乎都是用Socket来编程。你想过这些情景么我们每天打开浏览器浏览网页时浏览器进程怎么和Web服务器进行通信的呢当你用QQ聊天时QQ进程怎么和服务器或者是你的好友所在的QQ进程进行通信的呢当你打开PPstream观看视频时PPstream进程如何与视频服务器进行通信的呢 如此种种都是靠Socket来进行通信的以一斑窥全豹可见Socket编程在现代编程中占据了多么重要的地位这一节我们将介绍Go语言中如何进行Socket编程。
##什么是Socket
## 什么是Socket
Socket起源于Unix而Unix基本哲学之一就是“一切皆文件”都可以用“打开open > 读写write/read > 关闭close”模式来操作。Socket就是该模式的一个实现网络的Socket数据传输是一种特殊的I/OSocket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket()该函数返回一个整型的Socket描述符随后的连接建立、数据传输等操作都是通过该Socket实现的。
常用的Socket类型有两种流式SocketSOCK_STREAM和数据报式SocketSOCK_DGRAM。流式是一种面向连接的Socket针对于面向连接的TCP服务应用数据报式Socket是一种无连接的Socket对应于无连接的UDP服务应用。
##Socket如何通信
## Socket如何通信
网络中的进程之间如何通过Socket通信呢首要解决的问题是如何唯一标识一个进程否则通信无从谈起在本地可以通过进程PID来唯一标识一个进程但是在网络中这是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题网络层的“ip地址”可以唯一标识网络中的主机而传输层的“协议+端口”可以唯一标识主机中的应用程序进程。这样利用三元组ip地址协议端口就可以标识网络的进程了网络中需要互相通信的进程就可以利用这个标志在他们之间进行交互。请看下面这个TCP/IP协议结构图
![](images/8.1.socket.png?raw=true)
使用TCP/IP协议的应用程序通常采用应用编程接口UNIX BSD的套接字socket和UNIX System V的TLI已经被淘汰来实现网络进程之间的通信。就目前而言几乎所有的应用程序都是采用socket而现在又是网络时代网络中进程通信是无处不在这就是为什么说“一切皆Socket”。
##Socket基础知识
## Socket基础知识
通过上面的介绍我们知道Socket有两种TCP Socket和UDP SocketTCP和UDP是协议而要确定一个进程的需要三元组需要IP地址和端口。
###IPv4地址
### IPv4地址
目前的全球因特网所采用的协议族是TCP/IP协议。IP是TCP/IP协议中网络层的协议是TCP/IP协议族的核心协议。目前主要采用的IP协议的版本号是4(简称为IPv4)发展至今已经使用了30多年。
IPv4的地址位数为32位也就是最多有2的32次方的网络设备可以联到Internet上。近十年来由于互联网的蓬勃发展IP位址的需求量愈来愈大使得IP位址的发放愈趋紧张前一段时间据报道IPV4的地址已经发放完毕我们公司目前很多服务器的IP都是一个宝贵的资源。
地址格式类似这样127.0.0.1 172.122.121.111
###IPv6地址
### IPv6地址
IPv6是下一版本的互联网协议也可以说是下一代互联网的协议它是为了解决IPv4在实施过程中遇到的各种问题而被提出的IPv6采用128位地址长度几乎可以不受限制地提供地址。按保守方法估算IPv6实际可分配的地址整个地球的每平方米面积上仍可分配1000多个地址。在IPv6的设计过程中除了一劳永逸地解决了地址短缺问题以外还考虑了在IPv4中解决不好的其它问题主要有端到端IP连接、服务质量QoS、安全性、多播、移动性、即插即用等。
地址格式类似这样2002:c0e8:82e7:0:0:0:c0e8:82e7
###Go支持的IP类型
### Go支持的IP类型
在Go的`net`包中定义了很多类型、函数和方法用来网络编程其中IP的定义如下
type IP []byte
@@ -57,7 +57,7 @@ IPv6是下一版本的互联网协议也可以说是下一代互联网的协
执行之后你就会发现只要你输入一个IP地址就会给出相应的IP格式
##TCP Socket
## TCP Socket
当我们知道如何通过网络端口访问一个服务时,那么我们能够做什么呢?作为客户端来说,我们可以通过向远端某台机器的的某个网络端口发送一个请求,然后得到在机器的此端口上监听的服务反馈的信息。作为服务端,我们需要把服务绑定到某个指定端口,并且在此端口上监听,当有客户端来访问时能够读取信息并且写入反馈信息。
在Go语言的`net`包中有一个类型`TCPConn`,这个类型可以用来作为客户端和服务器端交互的通道,他有两个主要的函数:
@@ -81,7 +81,7 @@ IPv6是下一版本的互联网协议也可以说是下一代互联网的协
- addr表示域名或者IP地址例如"www.google.com:80" 或者"127.0.0.1:22".
###TCP client
### TCP client
Go语言中通过net包中的`DialTCP`函数来建立一个TCP连接并返回一个`TCPConn`类型的对象,当连接建立时服务器端也创建一个同类型的对象,此时客户端和服务器段通过各自拥有的`TCPConn`对象来进行数据交换。一般而言,客户端通过`TCPConn`对象将请求信息发送到服务器端,读取服务器端响应的信息。服务器端读取并解析来自客户端的请求,并返回应答信息,这个连接只有当任一端关闭了连接之后才失效,不然这连接可以一直在使用。建立连接的函数定义如下:
func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error)
@@ -141,7 +141,7 @@ Go语言中通过net包中的`DialTCP`函数来建立一个TCP连接并返回
通过上面的代码我们可以看出:首先程序将用户的输入作为参数`service`传入`net.ResolveTCPAddr`获取一个tcpAddr,然后把tcpAddr传入DialTCP后创建了一个TCP连接`conn`,通过`conn`来发送请求信息,最后通过`ioutil.ReadAll``conn`中读取全部的文本,也就是服务端响应反馈的信息。
###TCP server
### TCP server
上面我们编写了一个TCP的客户端程序也可以通过net包来创建一个服务器端程序在服务器端我们需要绑定服务到指定的非激活端口并监听此端口当有客户端请求到达的时候可以接收到来自客户端连接的请求。net包中有相应功能的函数函数定义如下
func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error)
@@ -224,7 +224,7 @@ Go语言中通过net包中的`DialTCP`函数来建立一个TCP连接并返回
通过把业务处理分离到函数`handleClient`,我们就可以进一步地实现多并发执行了。看上去是不是很帅,增加`go`关键词就实现了服务端的多并发从这个小例子也可以看出goroutine的强大之处。
###控制TCP连接
### 控制TCP连接
TCP有很多连接控制函数我们平常用到比较多的有如下几个函数
func (c *TCPConn) SetTimeout(nsec int64) os.Error
@@ -235,7 +235,7 @@ TCP有很多连接控制函数我们平常用到比较多的有如下几个
第二个函数用来设置客户端是否和服务器端一直保持着连接,即使没有任何的数据发送。
更多的内容请查看`net`包的文档。
##UDP Socket
## UDP Socket
Go语言包中处理UDP Socket和TCP Socket不同的地方就是在服务器端处理多个客户端请求数据包的方式不同,UDP缺少了对客户端连接请求的Accept函数。其他基本几乎一模一样只有TCP换成了UDP而已。UDP的几个主要函数如下所示
func ResolveUDPAddr(net, addr string) (*UDPAddr, os.Error)
@@ -316,7 +316,7 @@ Go语言包中处理UDP Socket和TCP Socket不同的地方就是在服务器端
}
}
##总结
## 总结
通过对TCP和UDP Socket编程的描述和实现可见Go已经完备地支持了Socket编程而且使用起来相当的方便Go提供了很多函数通过这些函数可以很容易就编写出高性能的Socket应用。

6
8.2.md
View File

@@ -1,4 +1,4 @@
#8.2 WebSocket
# 8.2 WebSocket
WebSocket是HTML5的重要特性它实现了基于浏览器的远程socket它使浏览器和服务器可以进行全双工通信许多浏览器Firefox、Google Chrome和Safari都已对此做了支持。
在WebSocket出现之前为了实现即时通信采用的技术都是“轮询”即在特定的时间间隔内由浏览器对服务器发出HTTP Request服务器在收到请求后返回最新的数据给浏览器刷新“轮询”使得浏览器需要对服务器不断发出请求这样会占用大量带宽。
@@ -13,7 +13,7 @@ WebSocket URL的起始输入是ws://或是wss://在SSL上。下图展示
![](images/8.2.websocket.png?raw=true)
##WebSocket原理
## WebSocket原理
WebSocket的协议颇为简单在第一次handshake通过以后连接便建立成功其后的通讯数据都是以”\x00″开头以”\xFF”结尾。在客户端这个是透明的WebSocket组件会自动将原始数据“掐头去尾”。
浏览器发出WebSocket连接请求然后服务器发出回应然后连接建立成功这个过程通常称为“握手” (handshaking)。请看下面的请求和反馈信息:
@@ -34,7 +34,7 @@ WebSocket的协议颇为简单在第一次handshake通过以后连接便
将之作为响应头`Sec-WebSocket-Accept`的值反馈给客户端。
##Go实现WebSocket
## Go实现WebSocket
Go语言标准包里面没有提供对WebSocket的支持但是在由官方维护的go.net子包中有对这个的支持你可以通过如下的命令获取该包
go get code.google.com/p/go.net/websocket

8
8.3.md
View File

@@ -1,6 +1,6 @@
#8.3 REST
# 8.3 REST
RESTful是目前最为流行的一种互联网软件架构。因为它结构清晰、符合标准、易于理解、扩展方便所以正得到越来越多网站的采用。本小节我们将来学习它到底是一种什么样的架构以及在Go里面如何来实现它。
##什么是REST
## 什么是REST
REST(REpresentational State Transfer)这个概念,首次出现是在 2000年Roy Thomas Fielding他是HTTP规范的主要编写者之一的博士论文中它指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是RESTful的。
要理解什么是REST我们需要理解下面几个概念:
@@ -43,7 +43,7 @@ Web应用要满足REST最重要的原则是:客户端和服务器之间的交互
![](images/8.3.rest.png?raw=true)
##RESTful的实现
## RESTful的实现
Go没有为REST提供直接支持但是因为RESTful是基于HTTP协议实现的所以我们可以利用`net/http`包来自己实现当然需要针对REST做一些改造REST是根据不同的method来处理相应的资源目前已经存在的很多自称是REST的应用其实并没有真正的实现REST我暂且把这些应用根据实现的method分成几个级别请看下图
![](images/8.3.rest3.png?raw=true)
@@ -100,7 +100,7 @@ Go没有为REST提供直接支持但是因为RESTful是基于HTTP协议实现
上面的代码演示了如何编写一个REST的应用我们访问的资源是用户我们通过不同的method来访问不同的函数这里使用了第三方库`github.com/drone/routes`在前面章节我们介绍过如何实现自定义的路由器这个库实现了自定义路由和方便的路由规则映射通过它我们可以很方便的实现REST的架构。通过上面的代码可知REST就是根据不同的method访问同一个资源的时候实现不同的逻辑处理。
##总结
## 总结
REST是一种架构风格汲取了WWW的成功经验无状态以资源为中心充分利用HTTP协议和URI协议提供统一的接口定义使得它作为一种设计Web服务的方法而变得流行。在某种意义上通过强调URI和HTTP等早期Internet标准REST是对大型应用程序服务器时代之前的Web方式的回归。目前Go对于REST的支持还是很简单的通过实现自定义的路由规则我们就可以为不同的method实现不同的handle这样就实现了REST的架构。
## links

14
8.4.md
View File

@@ -1,11 +1,11 @@
#8.4 RPC
# 8.4 RPC
前面几个小节我们介绍了如何基于Socket和HTTP来编写网络应用通过学习我们了解了Socket和HTTP采用的是类似"信息交换"模式,即客户端发送一条信息到服务端,然后(一般来说)服务器端都会返回一定的信息以表示响应。客户端和服务端之间约定了交互信息的格式,以便双方都能够解析交互所产生的信息。但是很多独立的应用并没有采用这种模式,而是采用类似常规的函数调用的方式来完成想要的功能。
RPC就是想实现函数调用模式的网络化。客户端就像调用本地函数一样然后客户端把这些参数打包之后通过网络传递到服务端服务端解包到处理过程中执行然后执行的结果反馈给客户端。
RPCRemote Procedure Call Protocol——远程过程调用协议是一种通过网络从远程计算机程序上请求服务而不需要了解底层网络技术的协议。它假定某些传输协议的存在如TCP或UDP以便为通信程序之间携带信息数据。通过它可以使函数调用模式网络化。在OSI网络通信模型中RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
##RPC工作原理
## RPC工作原理
![](images/8.4.rpc.png?raw=true)
@@ -22,7 +22,7 @@ RPCRemote Procedure Call Protocol——远程过程调用协议是一
- 9.客户句柄由内核接收消息
- 10.客户接收句柄返回的数据
##Go RPC
## Go RPC
Go标准包中已经提供了对RPC的支持而且支持三个级别的RPCTCP、HTTP、JSONRPC。但Go的RPC包是独一无二的RPC它和传统的RPC系统不同它只支持Go开发的服务器与客户端之间的交互因为在内部它们采用了Gob来编码。
Go RPC的函数只有符合下面的条件才能被远程访问不然会被忽略详细的要求如下
@@ -40,7 +40,7 @@ T、T1和T2类型必须能被`encoding/gob`包编解码。
任何的RPC都需要通过网络来传递数据Go RPC可以利用HTTP和TCP来传递数据利用HTTP的好处是可以直接复用`net/http`里面的一些函数。详细的例子请看下面的实现
###HTTP RPC
### HTTP RPC
http的服务端代码实现如下
package main
@@ -145,7 +145,7 @@ http的服务端代码实现如下
Arith: 17/8=2 remainder 1
通过上面的调用可以看到参数和返回值是我们定义的struct类型在服务端我们把它们当做调用函数的参数的类型在客户端作为`client.Call`的第23两个参数的类型。客户端最重要的就是这个Call函数它有3个参数第1个要调用的函数的名字第2个是要传递的参数第3个要返回的参数(注意是指针类型)通过上面的代码例子我们可以发现使用Go的RPC实现相当的简单方便。
###TCP RPC
### TCP RPC
上面我们实现了基于HTTP协议的RPC接下来我们要实现基于TCP协议的RPC服务端的实现代码如下所示
package main
@@ -263,7 +263,7 @@ http的服务端代码实现如下
这个客户端代码和http的客户端代码对比唯一的区别一个是DialHTTP一个是Dial(tcp),其他处理一模一样。
###JSON RPC
### JSON RPC
JSON RPC是数据编码采用了JSON而不是gob编码其他和上面介绍的RPC概念一模一样下面我们来演示一下如何使用Go提供的json-rpc标准包请看服务端代码的实现
package main
@@ -379,7 +379,7 @@ JSON RPC是数据编码采用了JSON而不是gob编码其他和上面介
}
##总结
## 总结
Go已经提供了对RPC的良好支持通过上面HTTP、TCP、JSON RPC的实现,我们就可以很方便的开发很多分布式的Web应用我想作为读者的你已经领会到这一点。但遗憾的是目前Go尚未提供对SOAP RPC的支持欣慰的是现在已经有第三方的开源实现了。

2
8.5.md
View File

@@ -1,4 +1,4 @@
#8.5 小结
# 8.5 小结
这一章我们介绍了目前流行的几种主要的网络应用开发方式,第一小节介绍了网络编程中的基础:Socket编程因为现在网络正在朝云的方向快速进化作为这一技术演进的基石的的socket知识作为开发者的你是必须要掌握的。第二小节介绍了正愈发流行的HTML5中一个重要的特性WebSocket通过它,服务器可以实现主动的push消息以简化以前ajax轮询的模式。第三小节介绍了REST编写模式这种模式特别适合来开发网络应用API目前移动应用的快速发展我觉得将来会是一个潮流。第四小节介绍了Go实现的RPC相关知识对于上面四种开发方式Go都已经提供了良好的支持net包及其子包,是所有涉及到网络编程的工具的所在地。如果你想更加深入的了解相关实现细节,可以尝试阅读这个包下面的源码。
## links
* [目录](<preface.md>)

2
8.md
View File

@@ -1,4 +1,4 @@
#8 Web服务
# 8 Web服务
Web服务可以让你在HTTP协议的基础上通过XML或者JSON来交换信息。如果你想知道上海的天气预报、中国石油的股价或者淘宝商家的一个商品信息你可以编写一段简短的代码通过抓取这些信息然后通过标准的接口开放出来就如同你调用一个本地函数并返回一个值。
Web服务背后的关键在于平台的无关性你可以运行你的服务在Linux系统可以与其他Window的asp.net程序交互同样的也可以通过同一个接口和运行在FreeBSD上面的JSP无障碍地通信。

8
9.1.md
View File

@@ -1,13 +1,13 @@
# 9.1 预防CSRF攻击
##什么是CSRF
## 什么是CSRF
CSRFCross-site request forgery中文名称跨站请求伪造也被称为one click attack/session riding缩写为CSRF/XSRF。
那么CSRF到底能够干嘛呢你可以这样简单的理解攻击者可以盗用你的登陆信息以你的身份模拟发送各种请求。攻击者只要借助少许的社会工程学的诡计例如通过QQ等聊天软件发送的链接(有些还伪装成短域名,用户无法分辨)攻击者就能迫使Web应用的用户去执行攻击者预设的操作。例如当用户登录网络银行去查看其存款余额在他没有退出时就点击了一个QQ好友发来的链接那么该用户银行帐户中的资金就有可能被转移到攻击者指定的帐户中。
所以遇到CSRF攻击时将对终端用户的数据和操作指令构成严重的威胁当受攻击的终端用户具有管理员帐户的时候CSRF攻击将危及整个Web应用程序。
##CSRF的原理
## CSRF的原理
下图简单阐述了CSRF攻击的思想
![](images/9.1.csrf.png?raw=true)
@@ -27,7 +27,7 @@ CSRFCross-site request forgery中文名称跨站请求伪造
CSRF攻击主要是因为Web的隐式身份验证机制Web的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器但却无法保证该请求是用户批准发送的。
##如何预防CSRF
## 如何预防CSRF
过上面的介绍,读者是否觉得这种攻击很恐怖,意识到恐怖是个好事情,这样会促使你接着往下看如何改进和防止类似的漏洞出现。
CSRF的防御可以从服务端和客户端两方面着手防御效果是从服务端着手效果比较好现在一般的CSRF防御也都在服务端进行。
@@ -82,7 +82,7 @@ CSRF的防御可以从服务端和客户端两方面着手防御效果是从
这样基本就实现了安全的POST但是也许你会说如果破解了token的算法呢按照理论上是但是实际上破解是基本不可能的因为有人曾计算过暴力破解该串大概需要2的11次方时间。
##总结
## 总结
跨站请求伪造即CSRF是一种非常危险的Web安全威胁它被Web安全界称为“沉睡的巨人”其威胁程度由此“美誉”便可见一斑。本小节不仅对跨站请求伪造本身进行了简单介绍还详细说明造成这种漏洞的原因所在然后以此提了一些防范该攻击的建议希望对读者编写安全的Web应用能够有所启发。
## links

8
9.2.md
View File

@@ -7,12 +7,12 @@
- 2、过滤数据弄明白我们需要什么样的数据
- 3、区分已过滤及被污染数据如果存在攻击数据那么保证过滤之后可以让我们使用更安全的数据
##识别数据
## 识别数据
“识别数据”作为第一步是因为在你不知道“数据是什么,它来自于哪里”的前提下,你也就不能正确地过滤它。这里的数据是指所有源自非代码内部提供的数据。例如:所有来自客户端的数据,但客户端并不是唯一的外部数据源,数据库和第三方提供的接口数据等也可以是外部数据源。
由用户输入的数据我们通过Go非常容易识别Go通过`r.ParseForm`之后把用户POST和GET的数据全部放在了`r.Form`里面。其它的输入要难识别得多,例如,`r.Header`中的很多元素是由客户端所操纵的。常常很难确认其中的哪些元素组成了输入,所以,最好的方法是把里面所有的数据都看成是用户输入。(例如`r.Header.Get("Accept-Charset")`这样的也看做是用户输入,虽然这些大多数是浏览器操纵的)
##过滤数据
## 过滤数据
在知道数据来源之后,就可以过滤它了。过滤是一个有点正式的术语,它在平时表述中有很多同义词,如验证、清洁及净化。尽管这些术语表面意义不同,但它们都是指的同一个处理:防止非法数据进入你的应用。
过滤数据有很多种方法其中有一些安全性较差。最好的方法是把过滤看成是一个检查的过程在你使用数据之前都检查一下看它们是否是符合合法数据的要求。而且不要试图好心地去纠正非法数据而要让用户按你制定的规则去输入数据。历史证明了试图纠正非法数据往往会导致安全漏洞。这里举个例子“最近建设银行系统升级之后如果密码后面两位是0只要输入前面四位就能登录系统”这是一个非常严重的漏洞。
@@ -25,7 +25,7 @@
过滤数据除了检查验证之外,在特殊时候,还可以采用白名单。即假定你正在检查的数据都是非法的,除非能证明它是合法的。使用这个方法,如果出现错误,只会导致把合法的数据当成是非法的,而不会是相反,尽管我们不想犯任何错误,但这样总比把非法数据当成合法数据要安全得多。
##区分过滤数据
## 区分过滤数据
如果完成了上面的两步数据过滤的工作就基本完成了但是在编写Web应用的时候我们还需要区分已过滤和被污染数据因为这样可以保证过滤数据的完整性而不影响输入的数据。我们约定把所有经过过滤的数据放入一个叫全局的Map变量中(CleanMap)。这时需要用两个重要的步骤来防止被污染数据的注入:
- 每个请求都要初始化CleanMap为一个空Map。
- 加入检查及阻止来自外部数据源的变量命名为CleanMap。
@@ -63,7 +63,7 @@
CleanMap["username"] = username
}
##总结
## 总结
数据过滤在Web安全中起到一个基石的作用大多数的安全问题都是由于没有过滤数据和验证数据引起的例如前面小节的CSRF攻击以及接下来将要介绍的XSS攻击、SQL注入等都是没有认真地过滤数据引起的因此我们需要特别重视这部分的内容。
## links

8
9.3.md
View File

@@ -1,7 +1,7 @@
# 9.3 避免XSS攻击
随着互联网技术的发展现在的Web应用都含有大量的动态内容以提高用户体验。所谓动态内容就是应用程序能够根据用户环境和用户请求输出相应的内容。动态站点会受到一种名为“跨站脚本攻击”Cross Site Scripting, 安全专家们通常将其缩写成 XSS的威胁而静态站点则完全不受其影响。
##什么是XSS
## 什么是XSS
XSS攻击跨站脚本攻击(Cross-Site Scripting),为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆故将跨站脚本攻击缩写为XSS。XSS是一种常见的web安全漏洞它允许攻击者将恶意代码植入到提供给其它用户使用的页面中。不同于大多数攻击(一般只涉及攻击者和受害者)XSS涉及到三方即攻击者、客户端与Web应用。XSS的攻击目标是为了盗取存储在客户端的cookie或者其他网站用于识别客户端身份的敏感信息。一旦获取到合法用户的信息后攻击者甚至可以假冒合法用户与网站进行交互。
XSS通常可以分为两大类一类是存储型XSS主要出现在让用户输入数据供其他浏览此页的用户进行查看的地方包括留言、评论、博客日志和各类表单等。应用程序从数据库中查询数据在页面中显示出来攻击者在相关页面输入恶意的脚本数据后用户浏览此类页面时就可能受到攻击。这个流程简单可以描述为:恶意用户的Html输入Web程序->进入数据库->Web程序->用户浏览器。另一类是反射型XSS主要做法是将脚本代码加入URL地址的请求参数里请求参数进入程序后在页面直接输出用户点击类似的恶意链接就可能受到攻击。
@@ -14,7 +14,7 @@ XSS目前主要的手段和目的如下
- 利用可被攻击的域受到其他域信任的特点,以受信任来源的身份请求一些平时不允许的操作,如进行不当的投票活动。
- 在访问量极大的一些页面上的XSS可以攻击一些小型网站实现DDoS攻击的效果
##XSS的原理
## XSS的原理
Web应用未对用户提交请求的数据做充分的检查过滤允许用户在提交的数据中掺入HTML代码(最主要的是“>”、“<”)并将未经转义的恶意代码输出到第三方用户的浏览器解释执行是导致XSS漏洞的产生原因。
接下来以反射性XSS举例说明XSS的过程现在有一个网站根据参数输出用户的名称例如访问url`http://127.0.0.1/?name=astaxie`,就会在浏览器输出如下信息:
@@ -25,7 +25,7 @@ Web应用未对用户提交请求的数据做充分的检查过滤允许用
更加详细的关于XSS的分析大家可以参考这篇叫做《[新浪微博XSS事件分析](http://www.rising.com.cn/newsletter/news/2011-08-18/9621.html)》的文章
##如何预防XSS
## 如何预防XSS
答案很简单坚决不要相信用户的任何输入并过滤掉输入中的所有特殊字符。这样就能消灭绝大部分的XSS攻击。
目前防御XSS主要有如下几种方式
@@ -43,7 +43,7 @@ Web应用未对用户提交请求的数据做充分的检查过滤允许用
这样就可以让浏览器解析javascript代码而不会是html输出。
##总结
## 总结
XSS漏洞是相当有危害的在开发Web应用的时候一定要记住过滤数据特别是在输出到客户端之前这是现在行之有效的防止XSS的手段。
## links

8
9.4.md
View File

@@ -1,9 +1,9 @@
# 9.4 避免SQL注入
##什么是SQL注入
## 什么是SQL注入
SQL注入攻击SQL Injection简称注入攻击是Web开发中最常见的一种安全漏洞。可以用它来从数据库获取敏感信息或者利用数据库的特性执行添加用户导出文件等一系列恶意操作甚至有可能获取数据库乃至系统用户最高权限。
而造成SQL注入的原因是因为程序没有有效过滤用户的输入使攻击者成功的向服务器提交恶意的SQL查询代码程序在接收后错误的将攻击者的输入作为查询语句的一部分执行导致原始的查询逻辑被改变额外的执行了攻击者精心构造的恶意代码。
##SQL注入实例
## SQL注入实例
很多Web开发者没有意识到SQL查询是可以被篡改的从而把SQL查询当作可信任的命令。殊不知SQL查询是可以绕开访问控制从而绕过身份验证和权限检查的。更有甚者有可能通过SQL查询去运行主机系统级的命令。
下面将通过一些真实的例子来详细讲解SQL注入的方式。
@@ -46,7 +46,7 @@ MSSQL服务器会执行这条SQL语句包括它后面那个用于向系统添
>虽然以上的例子是针对某一特定的数据库系统的,但是这并不代表不能对其它数据库系统实施类似的攻击。针对这种安全漏洞,只要使用不同方法,各种数据库都有可能遭殃。
##如何预防SQL注入
## 如何预防SQL注入
也许你会说攻击者要知道数据库结构的信息才能实施SQL注入攻击。确实如此但没人能保证攻击者一定拿不到这些信息一旦他们拿到了数据库就存在泄露的危险。如果你在用开放源代码的软件包来访问数据库比如论坛程序攻击者就很容易得到相关的代码。如果这些代码设计不良的话风险就更大了。目前Discuz、phpwind、phpcms等这些流行的开源程序都有被SQL注入攻击的先例。
这些攻击总是发生在安全性不高的代码上。所以,永远不要信任外界输入的数据,特别是来自于用户的数据,包括选择框、表单隐藏域和 cookie。就如上面的第一个例子那样就算是正常的查询也有可能造成灾难。
@@ -60,7 +60,7 @@ SQL注入攻击的危害这么大那么该如何来防治呢?下面这些建
5. 在应用发布之前建议使用专业的SQL注入检测工具进行检测以及时修补被发现的SQL注入漏洞。网上有很多这方面的开源工具例如sqlmap、SQLninja等。
6. 避免网站打印出SQL错误信息比如类型错误、字段不匹配等把代码里的SQL语句暴露出来以防止攻击者利用这些错误信息进行SQL注入。
##总结
## 总结
通过上面的示例我们可以知道SQL注入是危害相当大的安全漏洞。所以对于我们平常编写的Web应用应该对于每一个小细节都要非常重视细节决定命运生活如此编写Web应用也是这样。
## links

8
9.5.md
View File

@@ -3,7 +3,7 @@
那么我们作为一个Web应用开发者在选择密码存储方案时, 容易掉入哪些陷阱, 以及如何避免这些陷阱?
##普通方案
## 普通方案
目前用的最多的密码存储方案是将明文密码做单向哈希后存储,单向哈希算法有一个特征:无法通过哈希后的摘要(digest)恢复原始数据这也是“单向”二字的来源。常用的单向哈希算法包括SHA-256, SHA-1, MD5等。
Go语言对这三种加密算法的实现如下所示
@@ -31,7 +31,7 @@ Go语言对这三种加密算法的实现如下所示
结合上面两个特点,考虑到多数人所使用的密码为常见的组合,攻击者可以将所有密码的常见组合进行单向哈希,得到一个摘要组合, 然后与数据库中的摘要进行比对即可获得对应的密码。这个摘要组合也被称为`rainbow table`
因此通过单向加密之后存储的数据,和明文存储没有多大区别。因此,一旦网站的数据库泄露,所有用户的密码本身就大白于天下。
##进阶方案
## 进阶方案
通过上面介绍我们知道黑客可以用`rainbow table`来破解哈希后的密码,很大程度上是因为加密时使用的哈希算法是公开的。如果黑客不知道加密的哈希算法是什么,那他也就无从下手了。
一个直接的解决办法是,自己设计一个哈希算法。然而,一个好的哈希算法是很难设计的——既要避免碰撞,又不能有明显的规律,做到这两点要比想象中的要困难很多。因此实际应用中更多的是利用已有的哈希算法进行多次哈希。
@@ -62,7 +62,7 @@ Go语言对这三种加密算法的实现如下所示
在两个salt没有泄露的情况下黑客如果拿到的是最后这个加密串就几乎不可能推算出原始的密码是什么了。
##专家方案
## 专家方案
上面的进阶方案在几年前也许是足够安全的方案,因为攻击者没有足够的资源建立这么多的`rainbow table`。 但是,时至今日,因为并行计算能力的提升,这种攻击已经完全可行。
怎么解决这个问题呢?只要时间与资源允许,没有破译不了的密码,所以方案是:故意增加密码计算所需耗费的资源和时间,使得任何人都不可获得足够的资源建立所需的`rainbow table`
@@ -77,7 +77,7 @@ Go语言对这三种加密算法的实现如下所示
通过上面的的方法可以获取唯一的相应的密码值,这是目前为止最难破解的。
##总结
## 总结
看到这里,如果你产生了危机感,那么就行动起来:
- 1如果你是普通用户那么我们建议使用LastPass进行密码存储和生成对不同的网站使用不同的密码

6
9.6.md
View File

@@ -1,7 +1,7 @@
# 9.6 加密和解密数据
前面小节介绍了如何存储密码,但是有的时候,我们想把一些敏感数据加密后存储起来,在将来的某个时候,随需将它们解密出来,此时我们应该在选用对称加密算法来满足我们的需求。
##base64加解密
## base64加解密
如果Web应用足够简单数据的安全性没有那么严格的要求那么可以采用一种比较简单的加解密方法是`base64`这种方式实现起来比较简单Go语言的`base64`包已经很好的支持了这个,请看下面的例子:
package main
@@ -43,7 +43,7 @@
fmt.Println(string(enbyte))
}
##高级加解密
## 高级加解密
Go语言的`crypto`里面支持对称加密的高级加解密包有:
@@ -106,7 +106,7 @@ Go语言的`crypto`里面支持对称加密的高级加解密包有:
这三个函数实现了加解密操作,详细的操作请看上面的例子。
##总结
## 总结
这小节介绍了几种加解密的算法在开发Web应用的时候可以根据需求采用不同的方式进行加解密一般的应用可以采用base64算法更加高级的话可以采用aes或者des算法。

View File

@@ -1,4 +1,4 @@
#《Go Web 编程》
# 《Go Web 编程》
因为自己对Web开发比较感兴趣所以最近抽空在写一本开源的书籍《Go Web编程》《Build Web Application with Golang》。写这本书不表示我能力很强而是我愿意分享和大家一起分享Go写Web应用的一些东西。
- 对于从PHP/Python/Ruby转过来的同学了解Go怎么写Web应用开发的
@@ -21,22 +21,22 @@
### 代码
代码要**`go fmt`**后提交。注释文件注明其所属章节。
##如何编译
## 如何编译
`build.go`依赖markdown的一个解析包所以第一步先
go get github.com/russross/blackfriday
这样读者就可以把相应的Markdown文件编译成html文件执行`go build build.go`执行生成的文件就会在底目录下生成相应的html文件
##如何编译
## 如何编译
目前可以把相应的Markdown编译成html文件执行`go build build.go`执行生成的文件就会在底目录下生成相应的html文件。
##交流
## 交流
欢迎大家加入QQ群259316004 《Go Web编程》专用交流群
大家有问题还可以上德问上一起交流学习http://www.dewen.org/topic/165
##致谢
## 致谢
首先要感谢Golang-China的QQ群102319854里面的每一个人都很热心同时要特别感谢几个人
- [四月份平民](https://plus.google.com/110445767383269817959) (review代码)
@@ -45,10 +45,10 @@
- [Oling Cat](https://github.com/OlingCat)(review代码)
- [Wenlei Wu](mailto:spadesacn@gmail.com)(提供一些图片展示)
##授权许可
## 授权许可
除特别声明外,本书中的内容使用[CC BY-SA 3.0 License](http://creativecommons.org/licenses/by-sa/3.0/)(创作共用 署名-相同方式共享3.0许可协议)授权,代码遵循[BSD 3-Clause License](<https://github.com/astaxie/build-web-application-with-golang/blob/master/LICENSE.md>)3项条款的BSD许可协议
##开始阅读
## 开始阅读
[开始阅读](<https://github.com/astaxie/build-web-application-with-golang/blob/master/preface.md>)

View File

@@ -3,15 +3,19 @@ if [ ! -f build-web-application-with-golang ];then
go build
fi
if [ ! -d tmp ];then
mkdir tmp
if [ ! -d html ];then
mkdir html
fi
cd tmp
cd html
cp ../*.md .
for i in *.md;do
#<23><><EFBFBD>¸<EFBFBD>ʽ<EFBFBD><CABD>md<6D>ļ<EFBFBD>
sed -i '/^[#]\{1,\}/s!^\([#]\{1,\}\)\([^#]\{1,\}\)!\1 \2!' $i
sed -i '/^[#]\{1,\}/s! ! !' $i
#<23><><EFBFBD><EFBFBD>md<6D>ļ<EFBFBD><C4BC>е<EFBFBD>image src<72><63><EFBFBD><EFBFBD>
sed -i '/!\[\](images/s#images\(.*\)?raw=true#../Images\1#' $i
done
cd ../build-web-application-with-golang
rm -rf *.md
../build-web-application-with-golang >/dev/null
rm *.md
echo "<22>ļ<EFBFBD><C4BC>Ѿ<EFBFBD><D1BE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>sigil<69><6C><EFBFBD><EFBFBD>htmlĿ¼<C4BF>е<EFBFBD>html<6D>ļ<EFBFBD><C4BC><EFBFBD>imagesĿ¼<C4BF>е<EFBFBD>ͼƬ<CDBC>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>epub"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 141 KiB