Golang中如何设计高效的多重复用同一URL的POST请求方案

Golang中如何设计高效的多重复用同一URL的POST请求方案 我有一个页面作为文件浏览器,在这个页面中有许多操作,举几个例子:copypastedeleteopen 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

8 回复

使用不同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分发逻辑
  • 统一的错误处理机制

相比为每个操作创建独立路由,这种方案在文件浏览器场景下更简洁,减少了路由注册和维护开销,同时保持了良好的可读性和可维护性。

回到顶部