前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从零开始实现简单的webapi框架【Golang 入门系列十一】

从零开始实现简单的webapi框架【Golang 入门系列十一】

作者头像
架构师精进
发布2019-08-23 16:38:41
6650
发布2019-08-23 16:38:41
举报
文章被收录于专栏:公众号文章公众号文章

之前,已经讲过很多Golang的东西,比如基础语法,mysql的使用,redis的使用等等,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html,

今天就用从头写一个完整的go的示例项目吧。

本项目完全使用原生开发,没有使用任何WEB框架和ORM。虽然大家对mvc 呀,三层架构已经很了解了。但是,我还是想从头写一个完整的示例项目。这样大家有一个更深刻的了解,这样以后介绍web框架,orm框架的时候,学习起来应该会简单一点。

项目架构

下图这种架构模式相信大家应该十分清楚

Controller组合封装

Controller"基类"封装

代码语言:javascript
复制
package framework

type Controller struct {
   Data interface{}
}
代码语言:javascript
复制

UserController定义了用户注册,登录和查询等简单的三个接口

代码语言:javascript
复制
package controller

import (
   "net/http"
   "micro-cloud/service"
   "micro-cloud/utils"
   "micro-cloud/framework"
)

/**
 * r.PostFormValue  : 可以解析 Post/PUT Content-Type=application/x-www-form-urlencoded 或 Content-Type=multipart/form-data
 */

type UserConterller struct {

}

var userService = new(service.UserService)

func (p *UserConterller) Router(router *framework.RouterHandler) {
   router.Router("/register", p.register)
   router.Router("/login", p.login)
   router.Router("/findAll", p.findAll)
}

//POST Content-Type=application/x-www-form-urlencoded
func (p *UserConterller) register(w http.ResponseWriter, r *http.Request) {
   username := r.PostFormValue("username")
   password := r.PostFormValue("password")
   if utils.Empty(username) || utils.Empty(password) {
      microcloud.ResultFail(w, "username or password can not be empty")
      return
   }
   id := userService.Insert(username, password)
   if id <= 0 {
      microcloud.ResultFail(w, "register fail")
      return
   }
   microcloud.ResultOk(w, "register success")
}

//POST Content-Type=application/x-www-form-urlencoded
func (p *UserConterller) login(w http.ResponseWriter, r *http.Request) {
   username := r.PostFormValue("username")
   password := r.PostFormValue("password")
   if utils.Empty(username) || utils.Empty(password) {
      microcloud.ResultFail(w, "username or password can not be empty")
      return
   }
   users := userService.SelectUserByName(username)
   if len(users) == 0 {
      microcloud.ResultFail(w, "user does not exist")
      return
   }
   if users[0].Password != password {
      microcloud.ResultFail(w, "password error")
      return
   }

   microcloud.ResultOk(w, "login success")
}

// GET/POST
func (p *UserConterller) findAll(w http.ResponseWriter, r *http.Request) {
   users := userService.SelectAllUser()
   framework.ResultJsonOk(w, users)
}

数据访问层

代码语言:javascript
复制
package dao

import (
    "micro-cloud/model"
    "fmt"
    )

type UserDao struct {
}

func (p *UserDao) Insert(user *model.User) int64 {
    result, err := framework.DB.Exec("INSERT INTO user(`username`,`password`,`create_time`) value(?,?,?)", user.Username, user.Password, user.CreateTime)
    if err != nil {
        fmt.Println("Insert error")
        return 0
    }
    id, err := result.LastInsertId()
    if err != nil {
        fmt.Println("Insert error")
        return 0
    }
    return id
}

func (p *UserDao) SelectUserByName(username string) []model.User {
    rows, err := framework.DB.Query("SELECT * FROM user WHERE username = ?", username)
    if err != nil {
        fmt.Println("selectuserbyname error")
        return nil
    }
    var users []model.User
    for rows.Next() {
        var user model.User
        err := rows.Scan(&user.ID, &user.Username, &user.Password, &user.CreateTime)
        if err != nil {
            fmt.Println("selectuserbyname error")
            continue
        }
        users = append(users, user)
    }
    rows.Close()
    return users
}

func (p *UserDao) SelectAllUser() []model.User {
    rows, err := framework.DB.Query("SELECT * FROM user")
    if err != nil {
        fmt.Println("SelectAllUser error")
        return nil
    }
    var users []model.User
    for rows.Next() {
        var user model.User
        err := rows.Scan(&user.ID, &user.Username, &user.Password, &user.CreateTime)
        if err != nil {
            fmt.Println("SelectAllUser error")
            continue
        }
        users = append(users, user)
    }
    rows.Close()
    return users
}

实体层

代码语言:javascript
复制
package model

import "time"

type User struct {
    ID         uint      `json:"id"`
    Username   string    `json:"username"`
    Password   string    `json:"-"`
    CreateTime time.Time `json:"create_time"`
}

database

数据库操作类的实现

代码语言:javascript
复制
package microcloud

import (
    "database/sql"
    "log"
    "strings"
    _ "github.com/go-sql-driver/mysql"
)

//数据库的配置
const (
    username   = "admin"
    password   = "123456"
    ip         = "10.2.1.5"
    port       = "3306"
    dbName     = "microcloud"
    driverName = "mysql"
)

//DB数据库连接池
var DB *sql.DB

func InitDB() {
    //构建连接:"用户名:密码@tcp(IP:端口)/数据库?charset=uft8"
    //注意:要想解析time.Time类型,必须要设置parseTime=True
    path := strings.Join([]string{username, ":", password, "@tcp(", ip, ":", port, ")/", dbName, "?charset=utf8&parseTime=True&loc=Local"}, "")
    //打开数据库,前者是驱动名,所以要导入:_"github.com/go-sql-driver/mysql"
    DB, _ = sql.Open(driverName, path)
    //设置数据库最大连接数
    DB.SetConnMaxLifetime(100)
    //设置数据库最大闲置连接数
    DB.SetMaxIdleConns(10)
    //验证连接
    if err := DB.Ping(); err != nil {
        log.Panic(err)
    }
    log.Println("database connect success")
}

func CreateTable() {
    userTable := "CREATE TABLE IF NOT EXISTS `user`(" +
        "`id` INT UNSIGNED AUTO_INCREMENT," +
        "`username` VARCHAR(20) NOT NULL," +
        "`password` VARCHAR(40) NOT NULL," +
        "`create_time` DATETIME," +
        "PRIMARY KEY ( `id` )" +
        ")ENGINE=InnoDB DEFAULT CHARSET=utf8;"

    _, err := DB.Exec(userTable)
    if err != nil {
        log.Panic(err)
    }
}

http

http server的实现

代码语言:javascript
复制
server := &http.Server{
        Addr:        ":8215",
        Handler:     microcloud.Router,
        ReadTimeout: 5 * time.Second,
    }
    RegiterRouter(microcloud.Router)
    err := server.ListenAndServe()
    if err != nil {
        log.Panic(err)
    }

Router

路由处理的实现,其实也就是一个转发的功能

代码语言:javascript
复制
type RouterHandler struct {
}

var mux = make(map[string]func(http.ResponseWriter,*http.Request))

func (p *RouterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r.URL.Path)
    if fun, ok := mux[r.URL.Path]; ok {
        fun(w, r)
        return
    }
    //静态资源
    if strings.HasPrefix(r.URL.Path,constant.STATIC_BAES_PATH){
        if fun, ok := mux[constant.STATIC_BAES_PATH]; ok {
            fun(w, r)
            return
        }
    }
    http.Error(w, "error URL:"+r.URL.String(), http.StatusBadRequest)

}

func (p *RouterHandler) Router(relativePath string, handler func(http.ResponseWriter, *http.Request)) {
    mux[relativePath] = handler
}

演示

执行 go run main.go 之后,打开Postman,调相关的接口

以下就是访问API的请求与响应

/findAll 接口

/register 接口

最后

以上,用Go语言实现webapi 的例子,已经介绍完了,虽然比较简单,session,权限验证等都没有加。但是最主要的功能已经讲完了,感兴趣的可以从头编写下相关的代码。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 架构师精进 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 项目架构
  • Controller组合封装
  • 数据访问层
  • 实体层
  • database
  • http
  • Router
  • 演示
  • 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档