简单 (非技术框架) 实现的 Go 后端
~~众所周知,~~后端的开发一般是面向接口的开发,也可以说是 CRUD 工程师,本文章将使用 Go 描述从数据库读出数据,然后返回 JSON 数据
数据库
例子为从 MySQL 数据库的分类表,读出分类的名称与 ID,数据库的结构如下
DROP TABLE IF EXISTS categories ;
CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description VARCHAR(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
项目结构
本例子的项目结构如下
project/
├── database/ // 数据库包
│ └── database.go // 数据库连接
├── handler/ // 处理器包
│ └── category.go // 分类相关接口
├── model/ // 数据模型包
│ ├── category.go // 分类表模型
│ └── response.go // 响应数据模型
├── router/ // 路由包
│ └── router.go // 路由配置
├── utils // 工具包
│ └── response.go // 统一响应
├── main.go // 程序入口
接下来将分目录说明
database
该包是管理与数据库的连接
// database.go
package database
import (
"database/sql"
"fmt"
"log"
_ "github.com/go-sql-driver/mysql"
)
var DB *sql.DB
func InitDB() {
var err error
dsn := "username:password@tcp(address:3306)/name?charset=utf8"
DB, err = sql.Open("mysql", dsn)
if err != nil {
log.Fatalf("Failed to open database: %v", err)
}
// Test Connect
err = DB.Ping()
if err != nil {
log.Fatalf("Failed to ping database: %v", err)
}
fmt.Println("Successfully connected Database!")
}
handler
这个包是对于特定处理的逻辑 (类似于三层架构的 service),其中数据库处理还可以分层 (类似于 Mapper),但这只是一个简单的示例,就酱子啦~
// category.go
package handler
import (
"net/http"
"project/database"
"project/model"
"project/utils"
)
func GetCategories(w http.ResponseWriter, r *http.Request) {
// Select Database
rows, err := database.DB.Query("select id,name from categories")
if err != nil {
http.Error(w, "Failed to query Categories", http.StatusInternalServerError)
return
}
defer rows.Close()
// analyze data
var categories []model.Category
for rows.Next() {
var category model.Category
err = rows.Scan(&category.Id, &category.Name)
if err != nil {
utils.JSONResponse(w, http.StatusInternalServerError, "Failed to analyze Categories", nil)
//http.Error(w, "Failed to analyze Categories", http.StatusInternalServerError)
return
}
categories = append(categories, category)
}
// return JSON
//w.Header().Set("Content-Type", "application/json")
//json.NewEncoder(w).Encode(categories)
utils.JSONResponse(w, http.StatusOK, "", categories)
}
注释掉的内容为不适用统一响应接口,直接返回 JSON 的情况
model
这里的数据模型一般可以分为三个,从前端接收到的、数据库的以及返回给前端的,由于这只是一个简单的示例,所以我并没有细分
// category.go
package model
type Category struct {
Id int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
}
响应的数据模型为
package model
type Response struct {
Code int `json:"code"`
Msg string `json:"msf"`
Data interface{} `json:"data"`
}
router
该包管理路由,即访问了什么路径要指定相应的处理逻辑 (类似于三层架构的 controller)
// router.go
package router
import (
"github.com/gorilla/mux"
"project/handler"
)
func InitRouter() *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/categories", handler.GetCategories).Methods("GET")
return router
}
utils
该包为工具类,用于定义可以通用的工具
// response.go
package utils
import (
"encoding/json"
"log"
"net/http"
"project/model"
)
func JSONResponse(w http.ResponseWriter, code int, message string, data interface{}) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
err := json.NewEncoder(w).Encode(model.Response{Code: code, Msg: message, Data: data})
if err != nil {
log.Fatal(err)
return
}
}
main
程序的入口
// main.go
package main
import (
"log"
"net/http"
"project/database"
"project/router"
)
func main() {
// init Database
database.InitDB()
// Init Router
r := router.InitRouter()
// start
log.Println("Starting server on port 8848")
log.Fatal(http.ListenAndServe(":8848", r))
}
运行 go run main.go
,程序将监听 8848 端口