第七篇:《初识 net/http:构建第一个 RESTful API》 掌握了 Go 的基础语法和并发模型后我们正式进入 Web 开发领域。Go 的标准库 net/http 已经足够强大无需第三方框架就能构建一个完整的 RESTful API 服务。本文从 http.Handler 接口和 http.ServeMux 路由器讲起深入讲解如何编写 Handler 处理 HTTP 请求、如何读取请求参数和请求体、如何返回 JSON 响应以及如何用闭包实现中间件。最后通过一个完整的“图书管理”CRUD API 实战帮你建立 Go Web 开发的基础认知。一、net/http 核心接口http.Handlernet/http 包的核心是 http.Handler 接口typeHandlerinterface{ServeHTTP(w http.ResponseWriter,r*http.Request)}任何实现了 ServeHTTP 方法的类型都可以作为 HTTP 处理器。1.1 函数式 Handlerhttp.HandlerFunchttp.HandlerFunc 是一个函数类型它实现了 http.Handler 接口允许将普通函数直接转换为 Handlerpackagemainimport(fmtnet/http)// 普通函数作为 HandlerfunchelloHandler(w http.ResponseWriter,r*http.Request){fmt.Fprintf(w,Hello, World!)}funcmain(){// http.HandlerFunc 将函数转换为 Handlerhttp.HandleFunc(/hello,helloHandler)// 启动服务器http.ListenAndServe(:8080,nil)}访问 http://localhost:8080/hello即可看到 Hello, World!。1.2 结构体 Handler面向对象风格typeGreetingHandlerstruct{Namestring}func(h*GreetingHandler)ServeHTTP(w http.ResponseWriter,r*http.Request){fmt.Fprintf(w,Greeting from %s,h.Name)}funcmain(){handler:GreetingHandler{Name:MyApp}http.Handle(/greeting,handler)http.ListenAndServe(:8080,nil)}二、处理 HTTP 请求2.1 读取 URL 参数Query String使用 r.URL.Query() 读取查询参数funcgetUserHandler(w http.ResponseWriter,r*http.Request){// 获取查询参数query:r.URL.Query()name:query.Get(name)// /user?nameAliceage:query.Get(age)// /user?nameAliceage30fmt.Fprintf(w,name: %s, age: %s,name,age)}2.2 读取路径参数Path ParametersGo 标准库不直接支持路由参数如 /user/123需要自行解析路径或使用 http.ServeMux 的路径匹配模式。更常用的是直接使用 r.URL.Path 解析或者后续章节引入 Gin 框架来处理更复杂的路由。funcuserHandler(w http.ResponseWriter,r*http.Request){// 解析路径/users/123path:r.URL.Path// 简单解析parts:strings.Split(path,/)iflen(parts)3{id:parts[2]// 123fmt.Fprintf(w,User ID: %s,id)}}2.3 读取请求体POST / PUT对于 POST/PUT 请求需要从请求体中读取数据import(encoding/jsonionet/http)typeUserstruct{Namestringjson:nameAgeintjson:age}funccreateUserHandler(w http.ResponseWriter,r*http.Request){// 只允许 POST 方法ifr.Method!http.MethodPost{http.Error(w,Method not allowed,http.StatusMethodNotAllowed)return}// 读取请求体body,err:io.ReadAll(r.Body)iferr!nil{http.Error(w,读取请求体失败,http.StatusBadRequest)return}deferr.Body.Close()// 解析 JSONvaruser Useriferr:json.Unmarshal(body,user);err!nil{http.Error(w,JSON 解析失败,http.StatusBadRequest)return}fmt.Fprintf(w,接收到用户: %s, 年龄: %d,user.Name,user.Age)}三、返回 HTTP 响应3.1 返回 JSON 响应使用 json.NewEncoder 可以方便地返回 JSON 格式的响应typeResponsestruct{Codeintjson:codeMessagestringjson:messageDatainterface{}json:data,omitempty}funcapiHandler(w http.ResponseWriter,r*http.Request){// 设置响应头w.Header().Set(Content-Type,application/json)// 构造响应数据resp:Response{Code:200,Message:success,Data:map[string]string{status:ok},}// 返回 JSONw.WriteHeader(http.StatusOK)json.NewEncoder(w).Encode(resp)}3.2 设置状态码和响应头funchandler(w http.ResponseWriter,r*http.Request){// 设置响应头w.Header().Set(X-Custom-Header,my-value)w.Header().Set(Content-Type,application/json)// 设置状态码w.WriteHeader(http.StatusCreated)// 201// 写入响应体w.Write([]byte({status:ok}))}四、中间件Middleware中间件是一个高阶函数它接收一个 http.Handler 并返回一个新的 http.Handler可以在请求处理前后执行逻辑。net/http 标准库中没有直接定义中间件模式但通过闭包可以非常方便地实现。// 日志中间件funcloggerMiddleware(next http.Handler)http.Handler{returnhttp.HandlerFunc(func(w http.ResponseWriter,r*http.Request){fmt.Printf([%s] %s %s\n,time.Now().Format(2006-01-02 15:04:05),r.Method,r.URL.Path)next.ServeHTTP(w,r)// 调用下一个处理器})}// Recovery 中间件捕获 panicfuncrecoveryMiddleware(next http.Handler)http.Handler{returnhttp.HandlerFunc(func(w http.ResponseWriter,r*http.Request){deferfunc(){iferr:recover();err!nil{log.Printf(panic 恢复: %v,err)http.Error(w,服务器内部错误,http.StatusInternalServerError)}}()next.ServeHTTP(w,r)})}// 应用中间件funcmain(){mux:http.NewServeMux()mux.HandleFunc(/,helloHandler)// 中间件链handler:recoveryMiddleware(loggerMiddleware(mux))http.ListenAndServe(:8080,handler)}五、实战图书管理 CRUD API纯标准库下面是一个完整的图书管理 API包含创建、查询、更新、删除操作全部基于标准库实现packagemainimport(encoding/jsonfmtnet/httpsync)// 图书模型typeBookstruct{IDstringjson:idTitlestringjson:titleAuthorstringjson:authorPriceintjson:price}// 图书仓库typeBookStorestruct{mu sync.RWMutex booksmap[string]Book}funcNewBookStore()*BookStore{returnBookStore{books:make(map[string]Book),}}func(s*BookStore)Create(book Book){s.mu.Lock()defers.mu.Unlock()s.books[book.ID]book}func(s*BookStore)Get(idstring)(Book,bool){s.mu.RLock()defers.mu.RUnlock()book,ok:s.books[id]returnbook,ok}func(s*BookStore)GetAll()[]Book{s.mu.RLock()defers.mu.RUnlock()books:make([]Book,0,len(s.books))for_,book:ranges.books{booksappend(books,book)}returnbooks}func(s*BookStore)Update(idstring,book Book)bool{s.mu.Lock()defers.mu.Unlock()if_,ok:s.books[id];!ok{returnfalse}book.IDid s.books[id]bookreturntrue}func(s*BookStore)Delete(idstring)bool{s.mu.Lock()defers.mu.Unlock()if_,ok:s.books[id];!ok{returnfalse}delete(s.books,id)returntrue}// ---- HTTP Handlers ----varstoreNewBookStore()funcrespondJSON(w http.ResponseWriter,statusint,datainterface{}){w.Header().Set(Content-Type,application/json)w.WriteHeader(status)json.NewEncoder(w).Encode(data)}funcgetAllBooks(w http.ResponseWriter,r*http.Request){books:store.GetAll()respondJSON(w,http.StatusOK,books)}funcgetBookByID(w http.ResponseWriter,r*http.Request){// 简单路径解析/books/123id:r.URL.Path[len(/books/):]ifid{http.Error(w,id 不能为空,http.StatusBadRequest)return}book,ok:store.Get(id)if!ok{http.Error(w,图书不存在,http.StatusNotFound)return}respondJSON(w,http.StatusOK,book)}funccreateBook(w http.ResponseWriter,r*http.Request){ifr.Method!http.MethodPost{http.Error(w,Method not allowed,http.StatusMethodNotAllowed)return}varbook Bookiferr:json.NewDecoder(r.Body).Decode(book);err!nil{http.Error(w,JSON 解析失败,http.StatusBadRequest)return}deferr.Body.Close()ifbook.ID{http.Error(w,id 不能为空,http.StatusBadRequest)return}store.Create(book)respondJSON(w,http.StatusCreated,book)}funcupdateBook(w http.ResponseWriter,r*http.Request){ifr.Method!http.MethodPut{http.Error(w,Method not allowed,http.StatusMethodNotAllowed)return}id:r.URL.Path[len(/books/):]ifid{http.Error(w,id 不能为空,http.StatusBadRequest)return}varbook Bookiferr:json.NewDecoder(r.Body).Decode(book);err!nil{http.Error(w,JSON 解析失败,http.StatusBadRequest)return}deferr.Body.Close()if!store.Update(id,book){http.Error(w,图书不存在,http.StatusNotFound)return}respondJSON(w,http.StatusOK,book)}funcdeleteBook(w http.ResponseWriter,r*http.Request){ifr.Method!http.MethodDelete{http.Error(w,Method not allowed,http.StatusMethodNotAllowed)return}id:r.URL.Path[len(/books/):]ifid{http.Error(w,id 不能为空,http.StatusBadRequest)return}if!store.Delete(id){http.Error(w,图书不存在,http.StatusNotFound)return}w.WriteHeader(http.StatusNoContent)}funcmain(){// 初始化一些示例数据store.Create(Book{ID:1,Title:Go 入门,Author:张三,Price:59})store.Create(Book{ID:2,Title:微服务架构,Author:李四,Price:89})// 路由http.HandleFunc(/books,func(w http.ResponseWriter,r*http.Request){switchr.Method{casehttp.MethodGet:getAllBooks(w,r)casehttp.MethodPost:createBook(w,r)default:http.Error(w,Method not allowed,http.StatusMethodNotAllowed)}})http.HandleFunc(/books/,func(w http.ResponseWriter,r*http.Request){switchr.Method{casehttp.MethodGet:getBookByID(w,r)casehttp.MethodPut:updateBook(w,r)casehttp.MethodDelete:deleteBook(w,r)default:http.Error(w,Method not allowed,http.StatusMethodNotAllowed)}})fmt.Println(服务器启动在 :8080)http.ListenAndServe(:8080,nil)}测试 API# 获取所有图书curlhttp://localhost:8080/books# 创建图书curl-XPOST http://localhost:8080/books-HContent-Type: application/json-d{id:3,title:Go 并发, author:王五, price:69}# 获取单本图书curlhttp://localhost:8080/books/1# 更新图书curl-XPUT http://localhost:8080/books/1-HContent-Type: application/json-d{title:Go 入门第二版, author:张三, price:79}# 删除图书curl-XDELETE http://localhost:8080/books/1六、小结http.Handler核心接口ServeHTTP(w http.ResponseWriter, r *http.Request) 是 Go Web 的起点。路由http.ServeMux 是标准库的路由器支持路径匹配。请求处理r.URL.Query() 读取查询参数io.ReadAll(r.Body) 读取请求体json.Unmarshal 解析 JSON。响应返回w.Header().Set() 设置响应头w.WriteHeader() 设置状态码json.NewEncoder(w).Encode() 返回 JSON。中间件使用闭包实现接收 http.Handler 返回 http.Handler可以实现日志、鉴权、恢复等功能。

相关新闻

最新新闻

大数据运维:MapReduce 开发环境标准化搭建、Jar 打包与集群任务全流程运维

大数据运维:MapReduce 开发环境标准化搭建、Jar 打包与集群任务全流程运维

一、运维项目背景企业离线统计任务均基于 MapReduce 开发,运维核心工作:统一团队 IDEA Maven 标准化开发环境、规范 Jar 打包流程、编写集群任务提交自动化脚本、前置清理 HDFS 输出目录、排查版本 / 路径 / 权限类高频集群故障。本文基于 WordCount 基准…

2026/7/23 11:49:36
道生物联 TK8620 无人机电台方案:全国产自主可控+30km 超远距 + 成本仅 LoRa 1/3

道生物联 TK8620 无人机电台方案:全国产自主可控+30km 超远距 + 成本仅 LoRa 1/3

面向工业巡检、FPV 穿越机、低空物流、固定翼航模及机器人远控等场景,道生物联基于全国产自研 TK8620 射频芯片,推出由 TKB-310 TurMass™ 无人机遥控发射板 TKM-300 TurMass™ 无人机遥控接收模块组成的无人机电台方案。 一、产品速览 1.TKB-310 TurM…

2026/7/23 11:49:36
@Resource 按字段名查找的坑

@Resource 按字段名查找的坑

Spring Boot 启动失败:BeanNotOfRequiredTypeException nacosGracefulShutdownDelegate 连环报错分析 一、错误现象 应用启动时直接失败,控制台打印大量异常堆栈,主要包括两类错误: 1. 核心错误(导致启动失败&…

2026/7/23 11:49:36
基于DRV2605L评估套件的多驱动器触觉反馈系统设计与实战

基于DRV2605L评估套件的多驱动器触觉反馈系统设计与实战

1. 项目概述与核心价值 如果你正在设计下一代智能手表、游戏手柄或者车载中控屏,想让用户每一次点击、滑动和确认都获得清晰、细腻且有层次的物理反馈,那么触觉反馈技术就是你绕不开的核心。传统的“嗡嗡”震动早已过时,现代交互追求的是精准…

2026/7/23 11:49:36
网络恶搞事件的技术解析:从AI生成到传播分析

网络恶搞事件的技术解析:从AI生成到传播分析

这次我们来看一个很有意思的网络现象——"网友恶搞引发热议,谁干的?💀"事件。这个事件最近在社交媒体上引发了广泛讨论,涉及网络恶搞行为的边界、传播机制以及背后的技术实现方式。作为技术从业者,我们更关注…

2026/7/23 11:49:36
中文文本分类实战:从机器学习到深度学习

中文文本分类实战:从机器学习到深度学习

1. 项目概述中文文本分类作为自然语言处理的基础任务,在信息爆炸时代具有广泛的应用价值。这个毕业设计项目同时采用机器学习和深度学习两种技术路线,为学生提供了从传统算法到前沿模型的完整实践体验。我在实际教学中发现,这种双轨并行的设计…

2026/7/23 11:44:36

月新闻