序言
示例代码 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":"文件已存在,请勿重复上传"} |