序言
示例代码 github.com/acrossmountain/gs-demo
上章 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 "file=@./1.jpg" http://127.0.0.1:8080/upload
$ {"code":0,"data":{"url":"temp/1.jpg"},"msg":"上传文件成功"}
$ # 重复上传,会发现文件已存在
$ curl -F "file=@./1.jpg" http://127.0.0.1:8080/upload
$ {"code":-1,"msg":"文件已存在,请勿重复上传"}
在项目下的 temp 文件夹中能够找到上传后的文件。
以上能正常运行但是 controller 中包含了大量的逻辑而且均为文件操作,api 耦合性过高。我们需要把上面的文件操作拆分到 service 当中。
services/filesystem/filesystem.go
将文件操作逻辑抽取为 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 {
// ...
}
services/services.go
package services
import (
"learn/services/filesystem"
"github.com/go-spring/spring-core/gs"
)
func init() {
gs.Object(new(filesystem.Service))
}
main.go
增加 service 的导入
package main
import (
// ...
_ "learn/services"
// ...
)
controllers/upload/upload.go
在 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 "file=@./1.jpg" http://127.0.0.1:8080/upload
$ {"code":0,"data":{"url":"temp/1.jpg"},"msg":"上传文件成功"}
$ curl -F "file=@./1.jpg" http://127.0.0.1:8080/upload
$ {"code":-1,"msg":"文件已存在,请勿重复上传"}