Golang中net/http包实现RESTful API的实践指南
Golang中net/http包实现RESTful API的实践指南
你好,我是初学者,想使用 net/http 构建我的 RESTful API。查阅了网上的资料后,
func TodoHandler(w http.ResponseWriter, r *http.Request) {
var err error
switch r.Method {
case "GET":
err = getController(w, r)
case "POST":
err = postController(w, r)
case "PUT":
err = putController(w, r)
case "DELETE":
err = delController(w, r)
}
if err != nil {
panic(err)
}
}
func getController(w http.ResponseWriter, r *http.Request) error {
id, err := strconv.Atoi(path.Base(r.URL.Path))
if err != nil {
return err
}
todo, err := model.GetTodo(id)
if err != nil {
return err
}
data, err := json.Marshal(&todo)
if err != nil {
return err
}
fmt.Fprint(w, string(data))
return nil
}
func postController(w http.ResponseWriter, r *http.Request) error {
len := r.ContentLength
body := make([]byte, len)
r.Body.Read(body)
todo := new(model.Todo)
json.Unmarshal(body, &todo)
err := todo.Create()
if err != nil {
return err
}
return nil
}
...
这是我目前完成的部分,但我不知道如何像网上其他 RESTful API 那样进行扩展和设计。暂时不考虑使用第三方包。
更多关于Golang中net/http包实现RESTful API的实践指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html
如何以这种方式扩展,使其看起来像一个真正的 RESTful API
更多关于Golang中net/http包实现RESTful API的实践指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
您也可以使用Mux轻松创建美观的API 😊
mu1er:
像互联网上其他 RESTful API 那样进行扩展和设计
你指的是什么意思?
这些代码没有错误。我的意思是,如果遵循这种设计,它算是 RESTful API 吗?我是这个领域的新手,想要学习一下。
什么是真正的RESTful API?这是一个非常广泛的话题,涉及如何建模REST资源、要使用何种表示形式、是否尝试实现HATEOAS等等。
您是否有关于代码及其执行或未执行的特定操作的具体问题?
如果你没有关于Go语言的问题,这里就不讨论这个话题了。
如果你想了解REST,网上有很多资源可以阅读。但请注意,对于什么是"真正的"REST存在很多不同观点。可以看看:
以下是一个基于标准库 net/http 的 RESTful API 完整实现示例,包含路由解析、错误处理和 JSON 响应:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"strings"
)
// Todo 数据结构
type Todo struct {
ID int `json:"id"`
Title string `json:"title"`
Done bool `json:"done"`
}
// 模拟数据存储
var todos = map[int]*Todo{}
var idCounter = 1
// 统一 JSON 响应
func writeJSON(w http.ResponseWriter, status int, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(data)
}
// 错误响应
func writeError(w http.ResponseWriter, status int, message string) {
writeJSON(w, status, map[string]string{"error": message})
}
// 路由解析器
type Router struct {
routes map[string]http.HandlerFunc
}
func NewRouter() *Router {
return &Router{
routes: make(map[string]http.HandlerFunc),
}
}
func (r *Router) Handle(method, path string, handler http.HandlerFunc) {
r.routes[method+" "+path] = handler
}
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// 处理 CORS 预检请求
if req.Method == "OPTIONS" {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
return
}
path := req.URL.Path
method := req.Method
// 路由匹配逻辑
switch {
case method == "GET" && path == "/todos":
r.handleGetTodos(w, req)
case method == "GET" && strings.HasPrefix(path, "/todos/"):
r.handleGetTodo(w, req)
case method == "POST" && path == "/todos":
r.handleCreateTodo(w, req)
case method == "PUT" && strings.HasPrefix(path, "/todos/"):
r.handleUpdateTodo(w, req)
case method == "DELETE" && strings.HasPrefix(path, "/todos/"):
r.handleDeleteTodo(w, req)
default:
writeError(w, http.StatusNotFound, "Not Found")
}
}
// 获取所有 Todo
func (r *Router) handleGetTodos(w http.ResponseWriter, req *http.Request) {
var result []*Todo
for _, todo := range todos {
result = append(result, todo)
}
writeJSON(w, http.StatusOK, result)
}
// 获取单个 Todo
func (r *Router) handleGetTodo(w http.ResponseWriter, req *http.Request) {
idStr := strings.TrimPrefix(req.URL.Path, "/todos/")
id, err := strconv.Atoi(idStr)
if err != nil {
writeError(w, http.StatusBadRequest, "Invalid ID")
return
}
todo, exists := todos[id]
if !exists {
writeError(w, http.StatusNotFound, "Todo not found")
return
}
writeJSON(w, http.StatusOK, todo)
}
// 创建 Todo
func (r *Router) handleCreateTodo(w http.ResponseWriter, req *http.Request) {
var todo Todo
if err := json.NewDecoder(req.Body).Decode(&todo); err != nil {
writeError(w, http.StatusBadRequest, "Invalid JSON")
return
}
todo.ID = idCounter
idCounter++
todos[todo.ID] = &todo
writeJSON(w, http.StatusCreated, todo)
}
// 更新 Todo
func (r *Router) handleUpdateTodo(w http.ResponseWriter, req *http.Request) {
idStr := strings.TrimPrefix(req.URL.Path, "/todos/")
id, err := strconv.Atoi(idStr)
if err != nil {
writeError(w, http.StatusBadRequest, "Invalid ID")
return
}
existing, exists := todos[id]
if !exists {
writeError(w, http.StatusNotFound, "Todo not found")
return
}
var updates Todo
if err := json.NewDecoder(req.Body).Decode(&updates); err != nil {
writeError(w, http.StatusBadRequest, "Invalid JSON")
return
}
if updates.Title != "" {
existing.Title = updates.Title
}
existing.Done = updates.Done
writeJSON(w, http.StatusOK, existing)
}
// 删除 Todo
func (r *Router) handleDeleteTodo(w http.ResponseWriter, req *http.Request) {
idStr := strings.TrimPrefix(req.URL.Path, "/todos/")
id, err := strconv.Atoi(idStr)
if err != nil {
writeError(w, http.StatusBadRequest, "Invalid ID")
return
}
if _, exists := todos[id]; !exists {
writeError(w, http.StatusNotFound, "Todo not found")
return
}
delete(todos, id)
writeJSON(w, http.StatusNoContent, nil)
}
func main() {
router := NewRouter()
// 添加示例数据
todos[1] = &Todo{ID: 1, Title: "Learn Go", Done: false}
todos[2] = &Todo{ID: 2, Title: "Build REST API", Done: false}
idCounter = 3
fmt.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", router))
}
这个实现提供了以下功能:
-
完整的 CRUD 操作:
GET /todos- 获取所有待办事项GET /todos/{id}- 获取单个待办事项POST /todos- 创建新待办事项PUT /todos/{id}- 更新待办事项DELETE /todos/{id}- 删除待办事项
-
路由解析:使用路径前缀匹配来处理动态路由
-
错误处理:统一的错误响应格式
-
JSON 处理:自动的 JSON 序列化和反序列化
-
CORS 支持:处理跨域请求
测试示例:
# 获取所有 todos
curl http://localhost:8080/todos
# 创建新 todo
curl -X POST -H "Content-Type: application/json" -d '{"title":"New Todo"}' http://localhost:8080/todos
# 更新 todo
curl -X PUT -H "Content-Type: application/json" -d '{"title":"Updated","done":true}' http://localhost:8080/todos/1
# 删除 todo
curl -X DELETE http://localhost:8080/todos/1
这个架构可以轻松扩展新的端点,只需在 ServeHTTP 方法中添加新的路由匹配逻辑即可。


