Go-Spring 入門篇(三)

李輕水 發表於 2021-11-28
Spring Go

示例程式碼後續會提交到 Github 上

上章 Go-Spring 入門篇(二)講到 controller 的主要能力為路由註冊,引數處理複雜的邏輯應當拆分到 service 中。

本章我們講複雜的邏輯拆分到 service 中,為了不作為一個示例而太簡單,讓學習者覺得沒有什麼意義,決定先做一個上傳的能力,先看拆分 service 的情況。

controllers/upload/upload.go

package upload

import (
    "io"
    "os"
    "path"

    "github.com/go-spring/spring-core/web"
)

type Controller struct {
}

func (c *Controller) Upload(ctx web.Context) {

    file, err := ctx.FormFile("file")
    if err != nil {
        // ...
        return
    }

    f, err := file.Open()
    if err != nil {
        // ...
        return
    }
    defer func() {
        // 開啟的資源。一定要記住主動關閉
        _ = f.Close()
    }()

    out := path.Join("temp", file.Filename)
    if !PathExists(out) {

        // 建立目錄
        dir := path.Dir(out)
        if !PathExists(dir) {
            err := os.MkdirAll(dir, os.ModeDir)
            if err != nil {
                // ...
                return
            }
        }

        // 建立檔案
        dst, err := os.OpenFile(out, os.O_CREATE, 0644)
        if err != nil {
            // ...
            return
        }
        defer func() {
            _ = dst.Close()
        }()

        // 儲存檔案
        _, err = io.Copy(dst, f)
        if err != nil {
            // ...
            return
        }

    } else {
        // ...
        return
    }

    ctx.JSON(map[string]interface{}{
        "code": 0,
        "msg":  "上傳檔案成功",
        "data": map[string]interface{}{
            "url": out,
        },
    })
    return
}

func PathExists(name string) bool {
    // ....
}

controllers/controllers.go

package controllers

import (
    // ...
    "learn/controllers/upload"
    // ...
)

func init() {
    // ...

    gs.Object(new(upload.Controller)).Init(func(c *upload.Controller) {
        gs.PostMapping("/upload", c.Upload)
    })
}

執行 go run main.go,然後用 curl 上傳測試

$ curl -F "[email protected]/1.jpg" http://127.0.0.1:8080/upload
$ {"code":0,"data":{"url":"temp/1.jpg"},"msg":"上傳檔案成功"}
$ # 重複上傳,會發現檔案已存在
$ curl -F "[email protected]/1.jpg" http://127.0.0.1:8080/upload
$ {"code":-1,"msg":"檔案已存在,請勿重複上傳"}

在專案下的 temp 資料夾中能夠找到上傳後的檔案。

以上能正常執行但是 controller 中包含了大量的邏輯而且均為檔案操作,api 耦合性過高。我們需要把上面的檔案操作拆分到 service 當中。

將檔案操作邏輯抽取為 PutObject(name string, r io.Reader, size int64) (string, error)ExistsObject(name string) bool

package filesystem

import (
    "errors"
    "io"
    "os"
    "path"
)

type Service struct {
}

func (s *Service) PutObject(name string, r io.Reader, size int64) (string, error) {
    // ...
}

func (s *Service) ExistsObject(name string) bool {
    // ...
}
package services

import (
    "learn/services/filesystem"

    "github.com/go-spring/spring-core/gs"
)

func init() {
    gs.Object(new(filesystem.Service))
}

增加 service 的匯入

package main

import (
    // ...
    _ "learn/services"
    // ...
)

在 Controller 上宣告 FileService 並設定 tag autowire,這樣 go-spring 會自動注入 service 那邊註冊的例項。

package upload

import (
    "learn/services/filesystem"
    // ...
)

type Controller struct {
    FileService *filesystem.Service `autowire:""`
}

func (c *Controller) Upload(ctx web.Context) {
    // ...
    if out, err := c.FileService.PutObject(file.Filename, f, file.Size); err == nil {
        // 上傳成功
        return
    } else {
        // ...
        return
    }
}

重新執行 go run main.go 並測試,功能正常

$ curl -F "[email protected]/1.jpg" http://127.0.0.1:8080/upload
$ {"code":0,"data":{"url":"temp/1.jpg"},"msg":"上傳檔案成功"}

$ curl -F "[email protected]/1.jpg" http://127.0.0.1:8080/upload
$ {"code":-1,"msg":"檔案已存在,請勿重複上傳"}

Go-Spring 官網
Github

本作品採用《CC 協議》,轉載必須註明作者和本文連結