Golang中如何设计高效的多重复用同一URL的POST请求方案
Golang中如何设计高效的多重复用同一URL的POST请求方案
我有一个页面作为文件浏览器,在这个页面中有许多操作,举几个例子:copy、paste、delete、open folder 等。
我在想与服务器通信的最佳选择是什么?使用 JSON?使用 URL 查询参数?
我倾向于使用 JSON,我的想法是以这种格式处理请求:
{
"action": "getListOfFiles",
"currentActivePath":"/",
"targetFile": "",
"destinationFile": "",
"destinationFolder": "",
[...and more...]
}
其他可能的操作选项:"getListOfFiles"、"getSpaceDisk"、"delFile"、"moveFile"、"copyFile"。
在服务器端,我会使用 switch action case 并调用相应的函数。
所有函数都将使用相同的结构体来解析 JSON,根据用户执行的操作不同,有些字段可能被填充,有些可能为空。
这是一个好的方法吗?或者你能给我一些其他建议吗?我应该为每个操作创建一个路由吗?
更多关于Golang中如何设计高效的多重复用同一URL的POST请求方案的实战教程也可以访问 https://www.itying.com/category-94-b0.html
使用不同URL的一个额外好处是,有许多工具可以帮助从清晰的URL设计(或许从OpenAPI文件开始)自动化测试。
更多关于Golang中如何设计高效的多重复用同一URL的POST请求方案的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
mje Massimo.Costa
我喜欢你们的提议,但能否请你们告诉我,使用 JSON 与使用 REST 相比有哪些缺点?
使用 JSON 的话,我只需要创建 1 个端点,而使用 REST 则会有多个。
我想知道的是,如果我使用 JSON,将来会遇到哪些问题。
Willy:
我想知道的是,如果我使用 JSON,将来会遇到什么问题。
据我所知,除了这是一种构建 RESTful API 的非标准方式外,并没有真正的缺点。但对于一个内部项目来说,使用单一端点是没有问题的(而且你不用担心路由问题,这会让你的项目更加简单)。
我认为我会更倾向于选择REST方案,因为这样每个端点都是独立的,每个操作的维护也更简单。在我的方法中,如果我把JSON字段搞混了,可能会给每个受影响的函数带来更多的维护工作。
从我的角度来看,我的方法在初期工作量较小,但在后续的生产环境中可能会使维护变得复杂。另一方面,使用REST进行开发会稍微困难一些,但维护起来会更简单。
为每个操作实现一个端点是最典型(也最符合RESTful风格)的做法: GET /files GET /diskspace DELETE /file/:filename moveFile 请参考 How to move a REST resource? - Stack Overflow 上的建议选项 copyFile 请参考 rest - What is the restful way to represent a resource clone operation in the URL? - Stack Overflow 上的建议选项
对于MOVE和COPY操作,您可以使用以下任一方式:
- 显式URL
- 带有请求体“action”参数的通用URL
- 纯粹的RESTFUL风格
显式URL
POST /file/<id>/move → 参数在请求体中
POST /file/<id>/copy → 参数在请求体中
通用操作
POST /file/<id>/action → 参数在请求体中(action字段为必填项)
纯粹的RESTFUL风格
PATCH /file/<id> → 仅更新请求体中的值
或
PUT /file/<id> → 更新请求体中的所有值
说实话,我更喜欢第一种方案,因为它能在URL中明确表示您正在执行的操作,但这也取决于您的具体设置。
我的意思是,你可以构建一种维护方式几乎相同的方案。通过在JSON对象中包含“action”字段,你实际上是在实现自己的路由器。类似这样:
type FileRequest struct {
Action string
}
func HandleRequests(w http.ResponseWriter, r *http.Request) {
var params FileRequest
// 为简洁起见忽略错误。实际中不要忽略错误。
json.NewDecoder(r.Body).Decode(params)
switch (params.Action) {
case "list":
List(w, params)
case "read":
Read(w, params)
default:
// 可能是错误请求
}
}
func List(w http.ResponseWriter, req FileRequest) {
// 执行某些操作并写入 w
}
func Read(w http.ResponseWriter, req FileRequest) {
// 执行某些操作并写入 w
}
无论如何,我并不是在鼓励你忽略RESTful API的约定。只是想指出,如果你愿意,仍然可以用一种相当易于维护的方式来构建它。
对于复用同一URL处理多种POST操作,你提出的JSON方案是合理且高效的。以下是具体实现方案和示例代码:
1. 统一请求处理结构体
type FileOperation struct {
Action string `json:"action"`
CurrentActivePath string `json:"currentActivePath"`
TargetFile string `json:"targetFile"`
DestinationFile string `json:"destinationFile"`
DestinationFolder string `json:"destinationFolder"`
// 其他可能字段
}
type Response struct {
Success bool `json:"success"`
Data interface{} `json:"data,omitempty"`
Error string `json:"error,omitempty"`
}
2. 统一路由处理器
func HandleFileOperation(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var req FileOperation
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
var result interface{}
var err error
switch req.Action {
case "getListOfFiles":
result, err = getListOfFiles(req.CurrentActivePath)
case "getSpaceDisk":
result, err = getSpaceDisk(req.CurrentActivePath)
case "delFile":
err = deleteFile(req.TargetFile)
case "moveFile":
err = moveFile(req.TargetFile, req.DestinationFile)
case "copyFile":
err = copyFile(req.TargetFile, req.DestinationFile)
default:
http.Error(w, "Unknown action", http.StatusBadRequest)
return
}
response := Response{Success: err == nil}
if err != nil {
response.Error = err.Error()
} else if result != nil {
response.Data = result
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
3. 具体操作函数示例
func getListOfFiles(path string) ([]FileInfo, error) {
files, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
}
var fileList []FileInfo
for _, file := range files {
fileList = append(fileList, FileInfo{
Name: file.Name(),
Size: file.Size(),
IsDir: file.IsDir(),
})
}
return fileList, nil
}
func deleteFile(filePath string) error {
return os.Remove(filePath)
}
4. 路由注册
func main() {
http.HandleFunc("/api/file-operations", HandleFileOperation)
http.ListenAndServe(":8080", nil)
}
5. 客户端请求示例
// 获取文件列表
reqBody := FileOperation{
Action: "getListOfFiles",
CurrentActivePath: "/documents",
}
jsonData, _ := json.Marshal(reqBody)
resp, _ := http.Post("http://localhost:8080/api/file-operations",
"application/json", bytes.NewBuffer(jsonData))
// 删除文件
deleteReq := FileOperation{
Action: "delFile",
TargetFile: "/documents/old.txt",
}
这种设计的优势:
- 单一端点减少路由管理复杂度
- JSON格式灵活扩展字段
- 类型安全的结构体解析
- 清晰的action分发逻辑
- 统一的错误处理机制
相比为每个操作创建独立路由,这种方案在文件浏览器场景下更简洁,减少了路由注册和维护开销,同时保持了良好的可读性和可维护性。

