review 13.1md,13.2md,and 13.3md,updated some word

This commit is contained in:
cwl
2012-12-26 21:24:05 +08:00
parent eccfd607b2
commit d31e020317
3 changed files with 40 additions and 35 deletions

30
13.1.md
View File

@@ -1,29 +1,29 @@
# 13.1 项目规划
做任何事情都需要做好规划,那么我们在开发博客系统之前,需要好项目的规划,如何设置目录结构,如何理解整个的流程图,当我们理解了应用的执行过程,那么接下来的设计编码就会变得相对容易了
做任何事情都需要做好规划,那么我们在开发博客系统之前,同样需要好项目的规划,如何设置目录结构,如何理解整个项目的流程图,当我们理解了应用的执行过程,那么接下来的设计编码就会变得相对容易了
## gopath以及项目设置
gopath是一个文件系统的目录,我们可以随便设置一个目录为gopath前面介绍过gopath可以是多个目录在window系统设置环境变量在linux/MacOS系统只要`export gopath=/home/astaxie/gopath`但是必须保证gopath目录下面有三个目录pkg、bin、src目录。我们新建项目放在src目录下面我们暂定我们的博客目录叫做beeblog下面是在window下的环境变量和目录结构的截图
假设指定gopath是文件系统的普通目录名,当然我们可以随便设置一个目录然后将其路径存入GOPATH。前面介绍过GOPATH可以是多个目录在window系统设置环境变量在linux/MacOS系统只要输入终端命令`export gopath=/home/astaxie/gopath`但是必须保证gopath这个代码目录下面有三个目录pkg、bin、src新建项目的源码放在src目录下面现在暂定我们的博客目录叫做beeblog下面是在window下的环境变量和目录结构的截图
![](images/13.1.gopath.png?raw=true)
![](images/13.1.gopath2.png?raw=true)
## 应用程序流程图
博客系统是基于模型-视图-控制器这一设计模式的。MVC是一种将应用程序的逻辑层和表现层进行分离的方法。在实践中由于表现层从Go中分离了出来所以它允许你的网页中只包含很少的脚本。
博客系统是基于模型-视图-控制器这一设计模式的。MVC是一种将应用程序的逻辑层和表现层进行分离的结构方式。在实践中由于表现层从Go中分离了出来所以它允许你的网页中只包含很少的脚本。
- 模型 (Model) 代表你的数据结构。通常来说,你的模型类将包含取出、插入、更新你的数据库资料这些功能。
- 视图 (View) 是展示给用户的信息。一个视图通常是一个网页但是在Go中一个视图也可以是一个页面片段如页头、页尾。它还可以是一个 RSS 页面,或任何其它类型的“页面”Go实现的template包已经很好的现了View这个概念
- 模型 (Model) 代表数据结构。通常来说,模型类将包含取出、插入、更新数据库资料这些功能。
- 视图 (View) 是展示给用户的信息的结构及样式。一个视图通常是一个网页但是在Go中一个视图也可以是一个页面片段如页头、页尾。它还可以是一个 RSS 页面或其它类型的“页面”Go实现的template包已经很好的现了View层中的部分功能
- 控制器 (Controller) 是模型、视图以及其他任何处理HTTP请求所必须的资源之间的中介并生成网页。
下图设计了我们接下来的博客系统数据流如何贯穿整个系统:
下图显示了项目设计中博客系统数据流如何贯穿整个系统:
![](images/13.1.flow.png?raw=true)
1. main.go作为应用入口初始化运行博客所需要的基本资源配置信息监听端口。
2. 路由功能检查HTTP请求根据URL以及method来确定谁来处理请求
3. 如果缓存文件存在,它将绕过通常的系统执行顺序,被直接发送给浏览器。
4. 安全检测应用程序控制器调用之前HTTP请求和任用户提交的数据将被过滤。
5. 控制器装载模型、核心库、辅助函数,以及任何处理特定请求所需的其它资源,控制器主要处理业务逻辑。
6. 输出视图用来渲染需要发送到Web浏览器中的内容。如果开启缓存视图首先被缓存所以将可用于以后的请求。
1. main.go作为应用入口初始化一些运行博客所需要的基本资源,配置信息,监听端口。
2. 路由功能检查HTTP请求根据URL以及method来确定谁(控制层)来处理请求的转发资源
3. 如果缓存文件存在,它将绕过通常的流程执行,被直接发送给浏览器。
4. 安全检测应用程序控制器调用之前HTTP请求和任用户提交的数据将被过滤。
5. 控制器装载模型、核心库、辅助函数,以及任何处理特定请求所需的其它资源,控制器主要负责处理业务逻辑。
6. 输出视图层中渲染好的即将发送到Web浏览器中的内容。如果开启缓存视图首先被缓存用于以后的常规请求。
## 目录结构
根据上面的应用程序流程设计,博客的目录结构设计如下:
@@ -37,11 +37,11 @@ gopath是一个文件系统的目录我们可以随便设置一个目录为go
|——views 视图库
## 框架设计
为了实现博客的快速搭建打算基于上面的流程设计开发一个最小化的框架框架包括路由功能、支持REST的控制器、自动化的模板渲染日志系统、配置管理。
为了实现博客的快速搭建打算基于上面的流程设计开发一个最小化的框架框架包括路由功能、支持REST的控制器、自动化的模板渲染日志系统、配置管理
## 总结
本小节介绍了博客系统从设置gopath到目录建立这样的基础信息,也介绍了将要采用的MVC模式博客系统中数据流的执行流程最后通过这些流程设计了博客系统的目录结构至此我们完成一个框架的搭建,接下来的几个小节我们将会逐个实现。
本小节介绍了博客系统从设置GOPATH到目录建立这样的基础信息,也简单介绍了框架结构采用的MVC模式博客系统中数据流的执行流程最后通过这些流程设计了博客系统的目录结构至此我们基本完成一个框架的搭建,接下来的几个小节我们将会逐个实现。
## links
* [目录](<preface.md>)
* 上一章: [构建博客系统](<13.md>)
* 下一节: [自定义路由器设计](<13.2.md>)
* 下一节: [自定义路由器设计](<13.2.md>)

32
13.2.md
View File

@@ -3,10 +3,10 @@
## HTTP路由
HTTP路由组件负责将HTTP请求交到对应的函数处理(或者是一个struct的方法),如前面小节所描述的结构图,路由在框架中相当于一个事件处理器,而这个事件包括:
- 用户请求的路径(例如:/user/123,/article/123),当然还有查询串信息(例如?id=11)
- HTTP的请求method(GET、POST、PUT、DELETE、PATCH等)
- 用户请求的路径(path)(例如:/user/123,/article/123),当然还有查询串信息(例如?id=11)
- HTTP的请求方法(method)(GET、POST、PUT、DELETE、PATCH等)
路由器就是根据用户请求的这个信息定位到相应的处理函数。
路由器就是根据用户请求的事件信息转发到相应的处理函数(控制层)
## 默认的路由实现
在3.4小节有过介绍Go的http包的详解里面介绍了Go的http包如何设计和实现路由这里继续以一个例子来说明
@@ -22,14 +22,14 @@ HTTP路由组件负责将HTTP请求交到对应的函数处理(或者是一个st
log.Fatal(http.ListenAndServe(":8080", nil))
上面的例子调用了http默认的DefaultServeMux来添加路由两个参数第一个参数是前面所讲的用户请求的路径(Go中保存在r.URL.Path),第二参数是定位要执行的函数,路由的思路主要集中在两点:
上面的例子调用了http默认的DefaultServeMux来添加路由需要提供两个参数,第一个参数是希望用户访问此资源的URL路径(保存在r.URL.Path),第二参数是即将要执行的函数,以提供用户访问的资源。路由的思路主要集中在两点:
- 添加路由信息
- 根据用户请求转发到要执行的函数
Go默认的添加是通过函数`http.Handle``http.HandleFunc`等来添加,底层都是调用了`DefaultServeMux.Handle(pattern string, handler Handler)`,这个函数会把路由信息存储在一个map信息中`map[string]muxEntry`,这就解决了上面说的第一点。
Go默认的路由添加是通过函数`http.Handle``http.HandleFunc`等来添加,底层都是调用了`DefaultServeMux.Handle(pattern string, handler Handler)`,这个函数会把路由信息存储在一个map信息中`map[string]muxEntry`,这就解决了上面说的第一点。
Go监听端口然后接收到tcp连接会扔给Handler来处理上面的例子默认nil即为`http.DefaultServeMux`,通过`DefaultServeMux.ServeHTTP`函数来进行调度,循环上面存储的map信息访问url进行比对查询注册的处理函数,这样就实现了上面所说的第二点。
Go监听端口然后接收到tcp连接会扔给Handler来处理上面的例子默认nil即为`http.DefaultServeMux`,通过`DefaultServeMux.ServeHTTP`函数来进行调度,遍历之前存储的map路由信息,和用户访问的URL进行匹配以查询对应注册的处理函数,这样就实现了上面所说的第二点。
for k, v := range mux.m {
if !pathMatch(k, path) {
@@ -43,13 +43,13 @@ Go的监听端口然后接收到tcp连接会扔给Handler来处理上面
## beego框架路由实现
目前几乎所有的Web应用路由实现都是基于http默认的路由器但是默认的路由器有几个限制
目前几乎所有的Web应用路由实现都是基于http默认的路由器但是Go自带的路由器有几个限制:
- 不支持参数设定,例如/user/:uid 这种泛类型匹配
- 无法很好的支持REST模式无法限制访问的方法例如上面的例子中用户访问/foo可以用GET、POST、DELETE、HEAD等方式访问
- 默认的路由规则太多了我前面自己开发了一个API应用路由规则有三十几条这种路由多了之后其实可以进一步简化通过struct的方法进行一种简化
- 一般网站的路由规则太多了,编写繁琐。我前面自己开发了一个API应用路由规则有三十几条这种路由多了之后其实可以进一步简化通过struct的方法进行一种简化
beego框架的路由器基于上面的几点限制考虑设计了一种REST方式的路由实现路由设计也是基于上面默认设计的两点来考虑:存储路由和转发路由
beego框架的路由器基于上面的几点限制考虑设计了一种REST方式的路由实现路由设计也是基于上面Go默认设计的两点来考虑:存储路由和转发路由
### 存储路由
针对前面所说的限制点我们首先要解决参数支持就需要用到正则第二和第三点我们通过一种变通的方法来解决REST的方法对应到struct的方法中去然后路由到struct而不是函数这样在转发路由的时候就可以根据method来执行不同的方法。
@@ -74,7 +74,7 @@ ControllerRegistor对外的接口函数有
详细的实现如下所示:
func (p *ControllerRegistor) Add(pattern string, c ControllerInterface) {
func (p *ControllerRegistor) Add(pattern string, c ControllerInterface) {
parts := strings.Split(pattern, "/")
j := 0
@@ -82,8 +82,10 @@ ControllerRegistor对外的接口函数有
for i, part := range parts {
if strings.HasPrefix(part, ":") {
expr := "([^/]+)"
//a user may choose to override the defult expression
// similar to expressjs: /user/:id([0-9]+)
// similar to expressjs: /user/:id([0-9]+)
if index := strings.Index(part, "("); index != -1 {
expr = part[index:]
part = part[:index]
@@ -96,9 +98,11 @@ ControllerRegistor对外的接口函数有
//recreate the url pattern, with parameters replaced
//by regular expressions. then compile the regex
pattern = strings.Join(parts, "/")
regex, regexErr := regexp.Compile(pattern)
if regexErr != nil {
//TODO add error handling here to avoid panic
panic(regexErr)
return
@@ -116,7 +120,7 @@ ControllerRegistor对外的接口函数有
}
### 静态路由实现
上面我们实现的动态路由的实现Go的http包默认支持静态文件处理FileServer由于我们实现了自定义的路由器那么静态文件也需要自己设定beego的静态文件夹保存在全局变量StaticDir中StaticDir是一个map类型实现如下
上面我们实现的动态路由的实现Go的http包默认支持静态文件处理FileServer由于我们实现了自定义的路由器那么静态文件也需要自己设定beego的静态文件夹路径保存在全局变量StaticDir中StaticDir是一个map类型实现如下
func (app *App) SetStaticPath(url string, path string) *App {
StaticDir[url] = path
@@ -129,7 +133,7 @@ ControllerRegistor对外的接口函数有
### 转发路由
转发路由是基于ControllerRegistor的路由信息来进行转发的详细的实现如下代码所示
转发路由是基于ControllerRegistor的路由信息来进行转发的,详细的实现如下代码所示:
// AutoRoute
func (p *ControllerRegistor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -257,4 +261,4 @@ ControllerRegistor对外的接口函数有
## links
* [目录](<preface.md>)
* 上一章: [数据库设计](<13.2.md>)
* 下一节: [controller设计](<13.4.md>)
* 下一节: [controller设计](<13.4.md>)

13
13.3.md
View File

@@ -1,9 +1,9 @@
# 13.3 controller设计
传统的MVC框架大多数是基于Action设计的后缀式映射然而目前流行的Web趋势是REST风格的架构。尽管使用Filter或者rewrite能够通过URL重写实现REST风格的URL但是为什么不直接设计一个全新的REST风格的 MVC框架呢本小节就是基于这种思路来讲述如何从头设计一个基于REST风格的MVC框架中的controller最大限度地简化Web应用的开发甚至编写一行代码就可以实现“Hello, world”。
传统的MVC框架大多数是基于Action设计的后缀式映射然而现在Web流行REST风格的架构。尽管使用Filter或者rewrite能够通过URL重写实现REST风格的URL但是为什么不直接设计一个全新的REST风格的 MVC框架呢本小节就是基于这种思路来讲述如何从头设计一个基于REST风格的MVC框架中的controller最大限度地简化Web应用的开发甚至编写一行代码就可以实现“Hello, world”。
## controller作用
MVC设计模式是目前Web应用开发中最常见的一种架构模式,通过分离 Model模型、View视图和 Controller控制器可以更容易实现易于扩展的UI。Model指后台返回的数据View指需要渲染的页面通常是模板页面渲染后的结果通常是HTMLController指Web开发人员编写的处理不同URL的控制器如前面小节讲述的路由就是转发到控制器的过程controller在整个的MVC框架中起到了一个核心的作用处理业务逻辑因此控制器是整个框架中必不可少的一部分Model和View会根据不同的业务可以不写例如没有数据处理的逻辑处理没有页面输出的302调整之类的就不需要Model和View但是controller是必不可少的。
MVC设计模式是目前Web应用开发中最常见的架构模式通过分离 Model模型、View视图和 Controller控制器可以更容易实现易于扩展的用户界面(UI)。Model指后台返回的数据View指需要渲染的页面通常是模板页面渲染后的内容通常是HTMLController指Web开发人员编写的处理不同URL的控制器如前面小节讲述的路由就是URL请求转发到控制器的过程controller在整个的MVC框架中起到了一个核心的作用负责处理业务逻辑因此控制器是整个框架中必不可少的一部分Model和View对于有些业务需求是可以不写例如没有数据处理的逻辑处理没有页面输出的302调整之类的就不需要Model和View但是controller这一环节是必不可少的。
## beego的REST设计
前面小节介绍了路由实现了注册struct的功能而struct中实现了REST方式因此我们需要设计一个用于逻辑处理controller的基类这里主要设计了两个类型一个struct、一个interface
@@ -28,10 +28,11 @@ MVC设计模式是目前Web应用开发中最常见的一种架构模式
Head() //method=HEAD的处理
Patch() //method=PATCH的处理
Options() //method=OPTIONS的处理
Finish() //执行完成之后的处理 Render() error //执行完method对应的方法之后渲染页面
Finish() //执行完成之后的处理
Render() error //执行完method对应的方法之后渲染页面
}
那么前面介绍的路由add的时候是定义了ControllerInterface类型因此只要我们实现这个接口就可以所以我们的基类Controller实现如下的方法
那么前面介绍的路由add函数的时候是定义了ControllerInterface类型因此只要我们实现这个接口就可以所以我们的基类Controller实现如下的方法
func (c *Controller) Init(ct *Context, cn string) {
c.Data = make(map[interface{}]interface{})
@@ -112,7 +113,7 @@ MVC设计模式是目前Web应用开发中最常见的一种架构模式
c.Ct.Redirect(code, url)
}
上面的controller基类完成了接口定义的函数通过路由根据url执行相应的controller的原则会依次执行如下的函数
上面的controller基类已经实现了接口定义的函数通过路由根据url执行相应的controller的原则会依次执行如下
Init() 初始化
Prepare() 执行之前的初始化,每个继承的子类可以来实现该函数
@@ -159,4 +160,4 @@ index.tpl的代码如下所示我们可以看到数据的设置和显示都
## links
* [目录](<preface.md>)
* 上一章: [自定义路由器设计](<13.2.md>)
* 下一节: [日志和配置设计](<13.4.md>)
* 下一节: [日志和配置设计](<13.4.md>)