在本教學中,我們將逐步介紹使用 Go 為簡單部落格應用程式建立 RESTful API 的過程。我們將使用以下技術:
首先,讓我們設定 Go 專案並安裝必要的依賴項:
mkdir blog-api cd blog-api go mod init github.com/yourusername/blog-api go get github.com/gin-gonic/gin go get github.com/deepmap/oapi-codegen/cmd/oapi-codegen go get github.com/FerretDB/FerretDB
在專案根目錄中建立一個名為 api.yaml 的文件,並為我們的部落格 API 定義 OpenAPI 3.0 規格:
openapi: 3.0.0 info: title: Blog API version: 1.0.0 paths: /posts: get: summary: List all posts responses: '200': description: Successful response content: application/json: schema: type: array items: $ref: '#/components/schemas/Post' post: summary: Create a new post requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/NewPost' responses: '201': description: Created content: application/json: schema: $ref: '#/components/schemas/Post' /posts/{id}: get: summary: Get a post by ID parameters: - name: id in: path required: true schema: type: string responses: '200': description: Successful response content: application/json: schema: $ref: '#/components/schemas/Post' put: summary: Update a post parameters: - name: id in: path required: true schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/NewPost' responses: '200': description: Successful response content: application/json: schema: $ref: '#/components/schemas/Post' delete: summary: Delete a post parameters: - name: id in: path required: true schema: type: string responses: '204': description: Successful response components: schemas: Post: type: object properties: id: type: string title: type: string content: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time NewPost: type: object required: - title - content properties: title: type: string content: type: string
現在,讓我們使用 oapi-codegen 根據我們的 API 規格產生伺服器程式碼:
oapi-codegen -package api api.yaml > api/api.go
此指令將建立一個名為 api 的新目錄,並產生包含伺服器介面和模型的 api.go 檔案。
建立一個名為 db/db.go 的新文件,以使用 FerretDB 實作資料庫層:
package db import ( "context" "time" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) type Post struct { ID primitive.ObjectID `bson:"_id,omitempty"` Title string `bson:"title"` Content string `bson:"content"` CreatedAt time.Time `bson:"createdAt"` UpdatedAt time.Time `bson:"updatedAt"` } type DB struct { client *mongo.Client posts *mongo.Collection } func NewDB(uri string) (*DB, error) { client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(uri)) if err != nil { return nil, err } db := client.Database("blog") posts := db.Collection("posts") return &DB{ client: client, posts: posts, }, nil } func (db *DB) Close() error { return db.client.Disconnect(context.Background()) } func (db *DB) CreatePost(title, content string) (*Post, error) { post := &Post{ Title: title, Content: content, CreatedAt: time.Now(), UpdatedAt: time.Now(), } result, err := db.posts.InsertOne(context.Background(), post) if err != nil { return nil, err } post.ID = result.InsertedID.(primitive.ObjectID) return post, nil } func (db *DB) GetPost(id string) (*Post, error) { objectID, err := primitive.ObjectIDFromHex(id) if err != nil { return nil, err } var post Post err = db.posts.FindOne(context.Background(), bson.M{"_id": objectID}).Decode(&post) if err != nil { return nil, err } return &post, nil } func (db *DB) UpdatePost(id, title, content string) (*Post, error) { objectID, err := primitive.ObjectIDFromHex(id) if err != nil { return nil, err } update := bson.M{ "$set": bson.M{ "title": title, "content": content, "updatedAt": time.Now(), }, } var post Post err = db.posts.FindOneAndUpdate( context.Background(), bson.M{"_id": objectID}, update, options.FindOneAndUpdate().SetReturnDocument(options.After), ).Decode(&post) if err != nil { return nil, err } return &post, nil } func (db *DB) DeletePost(id string) error { objectID, err := primitive.ObjectIDFromHex(id) if err != nil { return err } _, err = db.posts.DeleteOne(context.Background(), bson.M{"_id": objectID}) return err } func (db *DB) ListPosts() ([]*Post, error) { cursor, err := db.posts.Find(context.Background(), bson.M{}) if err != nil { return nil, err } defer cursor.Close(context.Background()) var posts []*Post for cursor.Next(context.Background()) { var post Post if err := cursor.Decode(&post); err != nil { return nil, err } posts = append(posts, &post) } return posts, nil }
建立一個名為 handlers/handlers.go 的新檔案來實作 API 處理程序:
package handlers import ( "net/http" "time" "github.com/gin-gonic/gin" "github.com/yourusername/blog-api/api" "github.com/yourusername/blog-api/db" ) type BlogAPI struct { db *db.DB } func NewBlogAPI(db *db.DB) *BlogAPI { return &BlogAPI{db: db} } func (b *BlogAPI) ListPosts(c *gin.Context) { posts, err := b.db.ListPosts() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } apiPosts := make([]api.Post, len(posts)) for i, post := range posts { apiPosts[i] = api.Post{ Id: post.ID.Hex(), Title: post.Title, Content: post.Content, CreatedAt: post.CreatedAt, UpdatedAt: post.UpdatedAt, } } c.JSON(http.StatusOK, apiPosts) } func (b *BlogAPI) CreatePost(c *gin.Context) { var newPost api.NewPost if err := c.ShouldBindJSON(&newPost); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } post, err := b.db.CreatePost(newPost.Title, newPost.Content) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, api.Post{ Id: post.ID.Hex(), Title: post.Title, Content: post.Content, CreatedAt: post.CreatedAt, UpdatedAt: post.UpdatedAt, }) } func (b *BlogAPI) GetPost(c *gin.Context) { id := c.Param("id") post, err := b.db.GetPost(id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Post not found"}) return } c.JSON(http.StatusOK, api.Post{ Id: post.ID.Hex(), Title: post.Title, Content: post.Content, CreatedAt: post.CreatedAt, UpdatedAt: post.UpdatedAt, }) } func (b *BlogAPI) UpdatePost(c *gin.Context) { id := c.Param("id") var updatePost api.NewPost if err := c.ShouldBindJSON(&updatePost); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } post, err := b.db.UpdatePost(id, updatePost.Title, updatePost.Content) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Post not found"}) return } c.JSON(http.StatusOK, api.Post{ Id: post.ID.Hex(), Title: post.Title, Content: post.Content, CreatedAt: post.CreatedAt, UpdatedAt: post.UpdatedAt, }) } func (b *BlogAPI) DeletePost(c *gin.Context) { id := c.Param("id") err := b.db.DeletePost(id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Post not found"}) return } c.Status(http.StatusNoContent) }
在專案根目錄中建立一個名為 main.go 的新檔案來設定和執行應用程式:
package main import ( "log" "github.com/gin-gonic/gin" "github.com/yourusername/blog-api/api" "github.com/yourusername/blog-api/db" "github.com/yourusername/blog-api/handlers" ) func main() { // Initialize the database connection database, err := db.NewDB("mongodb://localhost:27017") if err != nil { log.Fatalf("Failed to connect to the database: %v", err) } defer database.Close() // Create a new Gin router router := gin.Default() // Initialize the BlogAPI handlers blogAPI := handlers.NewBlogAPI(database) // Register the API routes api.RegisterHandlers(router, blogAPI) // Start the server log.Println("Starting server on :8080") if err := router.Run(":8080"); err != nil { log.Fatalf("Failed to start server: %v", err) } }
現在我們已經啟動並運行了 API,讓我們使用curl 命令對其進行測試:
curl -X POST -H "Content-Type: application/json" -d '{"title":"My First Post","content":"This is the content of my first post."}' http://localhost:8080/posts
curl http://localhost:8080/posts
curl http://localhost:8080/posts/{id}
curl -X PUT -H "Content-Type: application/json" -d '{"title":"Updated Post","content":"This is the updated content."}' http://localhost:8080/posts/{id}
curl -X DELETE http://localhost:8080/posts/{id}
在本教程中,我們使用 Gin 框架、FerretDB 和 oapi-codegen 建立了一個簡單的部落格 API。我們已經介紹了以下步驟:
專案示範如何利用程式碼產生和 MongoDB 相容資料庫的強大功能,使用 Go 建立 RESTful API。您可以透過新增身份驗證、分頁和更複雜的查詢功能來進一步擴充此 API。
請記住在將此 API 部署到生產環境之前適當處理錯誤、新增適當的日誌記錄並實施安全措施。
您是否面臨著具有挑戰性的問題,或需要外部視角來看待新想法或專案?我可以幫忙!無論您是想在進行更大投資之前建立技術概念驗證,還是需要解決困難問題的指導,我都會為您提供協助。
如果您有興趣與我合作,請透過電子郵件與我聯繫:hungaikevin@gmail.com。
讓我們將挑戰轉化為機會!
以上是使用 Gin、FerretDB 和 oapi-codegen 建立部落格 API的詳細內容。更多資訊請關注PHP中文網其他相關文章!