GoLang 后端入门

简单 (非技术框架) 实现的 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 端口

This post is licensed under CC BY-NC-SA 4.0 by the author.