同学们好!从这节课开始,我们将一起进入本系列课程的最后一个阶段,项目实战环节。我们将综合运用前序课程所学到的知识,构建一个基于微服务架构的互联网应用项目——《我家租房网》。我们将分多节课,一步一步地实现项目的主要需求,同时,将诸如RPC、ProtoBuf、gRPC、微服务、go-micro、前后端分离、Gin、RESTful、MVC、Cookie与Session、Redis、MySQL与gORM、FastDFS以及Nginx等知识贯穿其中。同学们在完成整个项目以后,对其中所使用到的技术手段和技术工具会有一个全面且深入的理解。
作为项目的启动环节,这节课我们的任务只有三个,创建目录结构,编写入口函数,定义错误代码。
首先我们来为《我家租房网》项目创建目录结构。
首先,我们在GOPATH路径下的src子目录中,创建一个名为iHome的子目录,作为《我家租房网》项目的项目目录。然后,在该目录下创建back-end和front-end两个子目录,分别作为后端和前端两个子项目的工程目录。后端子项目的目录结构,我们会在后序课程中为大家介绍。这节课先来看前端子项目的目录结构。我们需要在front-end目录下创建七个子目录,它们是model、view、controller、utils、conf、resources和test,分别用于存放模型层文件、视图层文件、控制器层文件、实用工具文件、配置文件、资源文件和测试用例文件。
在为前端子项目创建好目录结构以后,我们将着手为其编写一个入口函数,也就是main包里的main函数。
用GoLand打开front-end工程,在工程目录下创建main.go文件:
x1package main
2
3import "github.com/gin-gonic/gin"
4
5func main() {
6 // 初始化路由
7 router := gin.Default()
8
9 // 路由匹配
10 router.GET("/", func(context *gin.Context) {
11 context.Writer.WriteString("Hello iHome")
12 })
13
14 // 运行
15 router.Run(":8080")
16}
在这段代码中,我们首先通过gin包的Default方法创建了一个Engine类型的路由器对象。然后通过该路由器对象的GET方法,将HTTP请求中的GET方法和“/”路径加以组合,匹配到一个匿名函数。该函数将字符串“Hello iHome”写入到HTTP响应中。最后通过路由器对象的Run方法运行这个服务器,并启动对当前主机任意IP地址8080端口的监听。
启动前端,用浏览器访问localhost:8080,可以看到“Hello iHome”显示在浏览器窗口中。
任何程序都不可能不出现错误。这里的错误指的不是编译错误,也不是逻辑错误,这些错误都应该在开发和测试阶段被发现和修复。这里所说的错误是指,在程序正常运行的过程中,因为输入、环境、交互等方面的原因,陷入到某种非预期的状态,而无法继续执行后序流程的情况。处理这类错误的通行做法是,定义一套错误代码和错误描述,与每一种错误状态相对应。错误代码便于机器判断,错误描述便于人类理解。
HTTP协议定义了一套标准错误代码和相应的描述文本。比如我们经常见到的200表示处理成功、404表示资源不存在等。这套错误代码只能满足HTTP协议本身的需要,对我们自己的应用而言,其业务描述能力显然远远不够。
为此我们又自己定义了一套专为本应用的错误代码和错误描述,比如0表示成功、4000表示数据库错误、4001表示无数据、4002表示数据已存在,等等等等。有了这套错误代码和错误描述,我们的服务器就会在发生错误时,向客户机响应相应的错误代码和错误描述,客户机据此采取相应的措施,如为用户显示提示信息等。
在front-end工程目录的utils子目录下创建error.go文件:
xxxxxxxxxx
591package utils
2
3const (
4 ERROR_OK = "0"
5 ERROR_DATABASE = "4000"
6 ERROR_NO_DATA = "4001"
7 ERROR_DATA_EXIST = "4002"
8 ERROR_DATA = "4003"
9 ERROR_USER_NOT_LOGIN = "4100"
10 ERROR_USER_LOGIN = "4101"
11 ERROR_USER_LOGOUT = "4102"
12 ERROR_PARAMETERS = "4103"
13 ERROR_USER_REGISTERED = "4104"
14 ERROR_USER_IDENTITY = "4105"
15 ERROR_PASSWORD = "4106"
16 ERROR_USER = "4107"
17 ERROR_SMS = "4108"
18 ERROR_MOBILE = "4109"
19 ERROR_REQUEST = "4200"
20 ERROR_IP = "4201"
21 ERROR_THIRD_PARTY = "4300"
22 ERROR_IO = "4301"
23 ERROR_INTERNAL = "4400"
24 ERROR_UNKNOWN = "4401"
25)
26
27var errors = map[string]string{
28 ERROR_OK: "成功",
29 ERROR_DATABASE: "数据库错误",
30 ERROR_NO_DATA: "无数据",
31 ERROR_DATA_EXIST: "数据已存在",
32 ERROR_DATA: "数据错误",
33 ERROR_USER_NOT_LOGIN: "用户未登录",
34 ERROR_USER_LOGIN: "用户登录失败",
35 ERROR_USER_LOGOUT: "用户退出失败",
36 ERROR_PARAMETERS: "参数错误",
37 ERROR_USER_REGISTERED: "用户已注册",
38 ERROR_USER_IDENTITY: "用户身份错误",
39 ERROR_PASSWORD: "密码错误",
40 ERROR_USER: "用户不存在或未激活",
41 ERROR_SMS: "短信失败",
42 ERROR_MOBILE: "手机号错误",
43 ERROR_REQUEST: "非法请求或请求次数受限",
44 ERROR_IP: "IP受限",
45 ERROR_THIRD_PARTY: "第三方系统错误",
46 ERROR_IO: "读写文件失败",
47 ERROR_INTERNAL: "内部错误",
48 ERROR_UNKNOWN: "未知错误",
49}
50
51func StrError(code string) string {
52 desc, ok := errors[code]
53
54 if ok {
55 return desc
56 }
57
58 return errors[ERROR_UNKNOWN]
59}
在这段代码中,我们首先定义了一组字符串类型的常量作为错误代码,其中的每个常量均代表一种错误。而后又定义了一个字符串到字符串的映射。映射的键为错误代码,值为与每个错误代码相对应的错误描述。最后我们还定义了一个名为StrError的函数。该函数以从参数传入的错误代码为键,在映射中查找与之对应的值,即错误描述,并返回之。
谢谢大家,我们下节课再见!