一、项目结构
beego:请求访问控制器
controller:控制器结构体
models:定义需要映射建表的结构体
orm:实现models中结构体
routers:初始化路由入口
二、控制器接口设计
ControllerInterface:控制器接口,提供Get和Post函数。
Controller:公共控制器,实现ControllerInterface接口,并实现了Get和Post函数。
自定义Controller:继承Controller,根据业务需求处理不同请求。
上面三个Controller的关系如下所示:
自定义Controller(我们程序员写的)----------->Controller(公共Controller)------------->ControllerInterface(接口)
(1)定义ControllerInterface接口
// 控制器接口
type ControllerInterface interface {
Get()
Post()
}
(2)定义Controller结构体,并实现ControllerInterface的Get和Post方法。
// 定义一个控制器结构,实现ControllerInterface接口
type Controller struct {}
func (c *Controller) Get() {
("GET空实现")
}
func (c *Controller) Post() {
("Post空实现")
}
(3)定义一个LoginController和RegistController,继承Controller。
type LoginController struct {
}
func (this *LoginController) Get(){
("LoginController get method is executing....")
}
func (this *LoginController) Post(){
("LoginController post method is executing....")
}
// 自己定义方法,处理Post或者Get请求
func (this *LoginController) LoginGet(){
("LoginController LoginGet method is executing...")
}
func (this *LoginController) LoginPost(){
("LoginController LoginPost method is executing...")
}
type RegisterController struct {
}
func (this *RegisterController) Get(){
("RegistController get method is executing....")
}
func (this *RegisterController) Post(){
("RegistController post method is executing....")
}
func (this *RegisterController) RegistGet(){
("RegistController RegistGet method is executing....")
}
三、路由设计
定义ControllerInfo结构体,用于存储路由信息 。
// 该结构体存储路由信息
type ControllerInfo struct {
pattern string // 存储请求url
controllerType // 存储控制器类型
methods map[string]string // 存储请求方法
initialize func() ControllerInterface // 初始化方法
}
在文件中定义init方法,程序启动时候调用该方法初始化路由信息。
func init() {
//将自己编写的控制器,匹配到路由上
("/login",&{},"get:LoginGet;post:LoginPost")
("/register",&{})
}
在main方法中导入routers包。
import (
......
_ "Project012/routers"
)
定义Router方法,把路由信息解析成ControllerInfo对象,并保存在上下文中。
// 该切片存储所有的路由信息
var routers []*ControllerInfo
var ctx // context代表
// 定义路由方法
// 参数一:访问地址
// 参数二:控制器接口对象的地址
// 参数三:访问方式和方法的映射关系
func Router(pattern string, c ControllerInterface, mappingMethods ...string) {
// 获取控制器接口的Value对象
cValue := (c)
//获取控制器接口的Type对象
cType := (cValue).Type()
// 该map存储请求方式和方法的映射关系
methods := make(map[string]string)
if len(mappingMethods) > 0 {
// 映射格式一:get:Login;post:HandleLogin
mappings := (mappingMethods[0], ";")
for _, v := range mappings {
mapping := (v, ":")
if len(mapping) != 2 {
panic("method mapping format is invalid")
}
// 映射格式二:get,post:login
requestMethods := (mapping[0], ",")
for _, requestMethod := range requestMethods {
// 判断请求方式是否正确和映射方法是否存在,只有两个条件同时成立,则保存在methods中
if requestMethod == "*" || HTTPMETHOD[(requestMethod)] {
// 判断映射方法是否在Controller中存在
if val := (mapping[1]); () {
// 把方法名保存在methods中
methods[(requestMethod)] = mapping[1]
} else {
//异常
panic("'" + mapping[1] + "' method doesn't exist in the controller " + ())
}
} else {
panic(v + " is an invalid method mapping. Method doesn't exist " + requestMethod)
}
}
}
}
// 创建ControllerInfo对象
route := &ControllerInfo{}
// 初始化ControllerInfo对象
= pattern
= methods
= cType
= func() ControllerInterface {
// 使用放射创建Controller的Type对象
o := ()
// 把o转换成ControllerInterface类型
controllerInterface, ok := ().(ControllerInterface)
// 判断控制器的类型是否是ControllerInterface类型,如果不是抛出异常
if !ok {
panic("controller is not ControllerInterface")
}
// ----------把控制器c中的数据赋值到控制器controllerInterface中---------
// 获取传入控制器接口的值
controllerValueEl := (c).Elem()
// 获取控制器的类型
controllerTypeEl := (c).Elem()
// 获取新创建控制器的值
controllerInterfaceValue := (controllerInterface).Elem()
// 获取控制器中字段个数
numOfFields := ()
for i := 0; i < numOfFields; i++ {
// 根据索引获取字段类型
fieldType := (i)
// 判断字段是否可以修改,只有该字段可以修改的情况下才设置字段的值
elemField := ()
if () {
// 设置字段的值
fieldVal := (i) //赋值给elemField
(fieldVal)
}
}
return controllerInterface
}
routers = append(routers, route)
// 将每个ControllerInfo中信息存储在routes中后,需要将routes中信息存储在上下文环境中,便于后续获取
ctx = ((), "routers", routers)
}
定义请求结构体。
var (
HTTPMETHOD = map[string]bool{
"GET": true,
"POST": true,
}
)
四、监听浏览器请求
(1)定义Run函数,项目启动时候调用该函数。
func Run() {
//监听网络请求
(":8080", HttpHandler{})
}
(2)定义HttpHandler,该对象提供了ServeHTTP方法处理请求。
type HttpHandler struct {
// 实现Handler接口
}
func (HttpHandler) ServeHTTP(respWriter , request *) {
//在此处去获取请求方式,请求链接,根据这2部分信息,匹配routes中的路由信息,调用相应方法
//获取Get或者Post方法,拿到方法以后,首先匹配路由,然后看看此路由,是否重新定义Get或者Post方法
method := // 请求方式
path := // 请求路径
// 过滤请求,google浏览器默认会请求这个图片,所以过滤掉
if path != "/" {
var controllerInterface ControllerInterface
// 从上下文中获取routers
routers := ("routers").([]*ControllerInfo)
if routers != nil {
// 遍历routers
for _, controllerInfo := range routers {
// 判断请求路径是否匹配
if == path {
// 如果路径匹配,再判断initialize方法是否存在。如果存在则调用该方法执行初始化操作
if != nil{
controllerInterface = ()
// 遍历controllerInfo中的methods
for key, value := range {
if key == method {
method = value //获取控制器中处理请求的方法名
break
}
}
break
}
}
}
}
//("controllerInterface = ", controllerInterface)
switch method {
case :
()
case :
()
default:
// 调用控制器的处理方法
vc := (controllerInterface)
method := (method)
in := make([], 0)
(in)
}
}
}
(3)修改main函数。
func main() {
()
}
五、建表
(1)表设计
type User struct {
Id int
Name string `orm:"size(20)"` //用户名
Age int `orm:"size(10)"` //登陆密码
Email string `orm:"size(50)"` //邮箱
}
type Goods struct { //商品SPU表
Id int
Name string `orm:"size(20)"` //商品名称
Detail string `orm:"size(200)"` //详细描述
}
(2)在models包下定义init方法,程序启动时候自动调用该方法进行建表操作。
func init(){
("mysql", "root:root@tcp(127.0.0.1:3306)/user?charset=utf8")
(new(User),new(Goods))
}
(3)修改文件,导入models包。
import (
......
_ "Project012/models"
)
(4)在orm包下定义RegisterDataBase和RegisterModel函数。
var db *
var err error
// 连接数据库
func RegisterDataBase(driverName ,dataSourceName string) {
db, err := (driverName,dataSourceName)
if err != nil {
("数据库开启失败")
return
}
errPing := ()
if errPing!=nil {
("数据库链接失败")
return
}
}
//在数据库中建表方法
func RegisterModel(models ...interface{}){
//根据传递进来的多个结构体的对象,进行建表操作
//1.因为models中有多个对象,所以在此处需要对models进行循环遍历,每遍历一次,就需要创建一个sql
//2.根据遍历的sql,进行sql语句触发,相应进行建表操作
sqlInfo := getModelsInfo(models)
for _, sql := range sqlInfo {
pstmt, err := (sql)
if err != nil {
return
}
()
}
defer ()
}
var sqlStr []string
// 通过反射获取结构体中字段,拼接sql
func getModelsInfo(models []interface{}) []string {
for index, model := range models {
modelType := (model) //*结构体指针
tableName := ().Name()
sql := ""
for i := 0; i < ().NumField(); i++ {
curFieldName := ().Field(i).Name
curFieldTag := ().Field(i).Tag
curFieldType := ().Field(i).()
tagValue := ().Field(i).("orm") // 获取指定名字的标签的内容
if (curFieldName) == "id" {
//得到id
sql = "CREATE TABLE "+(tableName)+" (id int(10) unsigned NOT NULL AUTO_INCREMENT,"
continue
}
sql = ([]string{sql,(curFieldName)},"")
split := (tagValue, ";")
sizeNumber := "10"
fileType := "undefined Type"
for _, value := range split {
indexStart := (value,"(")
indexEnd := (value,")")
if (value,"size") {
sizeNumber = value[indexStart+1 : indexEnd]
("sizeNumber = ",sizeNumber)
}
if curFieldType == "int" {
fileType ="int("+sizeNumber+")"
}
if curFieldType == "string"{
fileType ="varchar("+sizeNumber+")"
}
}
sql = ([]string{sql," ",fileType},"")
if i == ().NumField()-1 {
sql = ([]string{sql," ","DEFAULT NULL,PRIMARY KEY (`id`)"},"")
}else{
sql = ([]string{sql," ","DEFAULT NULL,"},"")
}
}
sql = ([]string{sql,") ENGINE=InnoDB DEFAULT CHARSET=utf8;"},"")
sqlStr = append(sqlStr, sql)
}
return sqlStr
}
(5)导入mysql驱动。
import (
......
_ "/go-sql-driver/mysql"
)