第八章:项目实战 - 构建Web服务
8.1 使用net/http构建Web服务
基础HTTP服务
package main
import (
"fmt"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
func main() {
// 注册路由
http.HandleFunc("/", helloHandler)
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "你好!")
})
// 启动服务
fmt.Println("服务器启动在 :8080")
http.ListenAndServe(":8080", nil)
}
处理不同HTTP方法
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
var users = []User{
{ID: 1, Name: "张三"},
{ID: 2, Name: "李四"},
}
func usersHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
// 获取所有用户
json.NewEncoder(w).Encode(users)
case http.MethodPost:
// 创建用户
var user User
json.NewDecoder(r.Body).Decode(&user)
users = append(users, user)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
func main() {
http.HandleFunc("/users", usersHandler)
fmt.Println("服务器启动在 :8080")
http.ListenAndServe(":8080", nil)
}
8.2 使用Gin框架
安装Gin
go get -u github.com/gin-gonic/gin
基础使用
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 创建路由
r := gin.Default()
// GET请求
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
// 带参数
r.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.JSON(http.StatusOK, gin.H{
"name": name,
})
})
// 查询参数
r.GET("/welcome", func(c *gin.Context) {
firstname := c.DefaultQuery("firstname", "Guest")
lastname := c.Query("lastname")
c.JSON(http.StatusOK, gin.H{
"firstname": firstname,
"lastname": lastname,
})
})
// POST + JSON
r.POST("/login", func(c *gin.Context) {
var json struct {
User string `json:"user" binding:"required"`
Password string `json:"password" binding:"required"`
}
if err := c.ShouldBindJSON(&json); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"status": "登录成功",
"user": json.User,
})
})
// 表单
r.POST("/form", func(c *gin.Context) {
message := c.PostForm("message")
nick := c.DefaultPostForm("nick", "anonymous")
c.JSON(http.StatusOK, gin.H{
"message": message,
"nick": nick,
})
})
// 上传文件
r.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
c.SaveUploadedFile(file, "/tmp/"+file.Filename)
c.JSON(http.StatusOK, gin.H{
"filename": file.Filename,
})
})
r.Run() // 默认在 :8080
}
路由组与中间件
package main
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.New()
// 全局中间件
r.Use(gin.Logger())
r.Use(gin.Recovery())
// 自定义中间件
r.Use(func(c *gin.Context) {
start := time.Now()
c.Next() // 执行后续处理
duration := time.Since(start)
fmt.Printf("请求耗时: %v\n", duration)
})
// 路由组
api := r.Group("/api")
{
// 需要认证的组
authorized := api.Group("/")
authorized.Use(AuthMiddleware())
{
authorized.GET("/users", getUsers)
authorized.POST("/users", createUser)
}
// 公开API
api.GET("/public", publicHandler)
}
r.Run()
}
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "未授权",
})
return
}
c.Next()
}
}
func getUsers(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"users": []string{"张三", "李四"}})
}
func createUser(c *gin.Context) {
c.JSON(http.StatusCreated, gin.H{"message": "用户创建成功"})
}
func publicHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "公开信息"})
}
8.3 项目实战:RESTful API
package main
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// 任务模型
type Task struct {
ID int `json:"id"`
Title string `json:"title" binding:"required"`
Description string `json:"description"`
Status string `json:"status"`
}
// 内存存储
type TaskStore struct {
tasks []Task
nextID int
}
func NewTaskStore() *TaskStore {
return &TaskStore{
tasks: []Task{},
nextID: 1,
}
}
func (s *TaskStore) GetAll() []Task {
return s.tasks
}
func (s *TaskStore) GetByID(id int) *Task {
for _, t := range s.tasks {
if t.ID == id {
return &t
}
}
return nil
}
func (s *TaskStore) Create(task Task) Task {
task.ID = s.nextID
task.Status = "pending"
s.nextID++
s.tasks = append(s.tasks, task)
return task
}
func (s *TaskStore) Update(id int, updated Task) *Task {
for i, t := range s.tasks {
if t.ID == id {
s.tasks[i].Title = updated.Title
s.tasks[i].Description = updated.Description
s.tasks[i].Status = updated.Status
return &s.tasks[i]
}
}
return nil
}
func (s *TaskStore) Delete(id int) bool {
for i, t := range s.tasks {
if t.ID == id {
s.tasks = append(s.tasks[:i], s.tasks[i+1:]...)
return true
}
}
return false
}
var store = NewTaskStore()
func main() {
r := gin.Default()
// 初始化一些数据
store.Create(Task{Title: "学习Go语言", Description: "完成基础教程"})
store.Create(Task{Title: "构建Web服务", Description: "使用Gin框架"})
// API路由
api := r.Group("/api/v1")
{
api.GET("/tasks", getTasks)
api.GET("/tasks/:id", getTask)
api.POST("/tasks", createTask)
api.PUT("/tasks/:id", updateTask)
api.DELETE("/tasks/:id", deleteTask)
}
r.Run(":8080")
}
func getTasks(c *gin.Context) {
tasks := store.GetAll()
c.JSON(http.StatusOK, gin.H{
"data": tasks,
})
}
func getTask(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效ID"})
return
}
task := store.GetByID(id)
if task == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "任务不存在"})
return
}
c.JSON(http.StatusOK, gin.H{"data": task})
}
func createTask(c *gin.Context) {
var task Task
if err := c.ShouldBindJSON(&task); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
created := store.Create(task)
c.JSON(http.StatusCreated, gin.H{"data": created})
}
func updateTask(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效ID