Go语言MongoDB DELETE路由FindOne返回mongo.ErrNoDocuments故障排查
问题:Go MongoDB HTTP服务器DELETE路由无法找到存在的文档
我用Go编写了一个与MongoDB交互的HTTP服务器,GET、POST、PUT路由均正常工作(其中PUT路由逻辑与DELETE最相似且运行正常),但DELETE路由存在异常:明明UUID对应的文档确实存在,且其他路由使用该UUID能正常操作,DELETE路由执行FindOne时却返回mongo.ErrNoDocuments错误。刚接触Go,肯定遗漏了某些细节,希望得到帮助。
DELETE路由代码
/* DELETE /{uuid} r.Body: "accessKey" -> required Deletes an existing Paste in the MongoDB database */ func (s *Server) deletePaste() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { start := time.Now() defer func() { log.Printf("%s %s [%v]", r.Method, r.URL.Path, time.Since(start), ) }() uuidStr, _ := mux.Vars(r)["uuid"] fmt.Println(uuidStr) body := struct { AccessKey string `json:"accessKey,omitempty"` }{} if err := decodeJSONBody(w, r, &body); err != nil { var mr *badRequest if errors.As(err, &mr) { http.Error(w, mr.msg, mr.status) } else { log.Print(err.Error()) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } return } coll := s.Client.Database("paste").Collection("files") // Check document exists and accessKey is the same var result bson.M filter := bson.M{"uuid": uuidStr} project := bson.M{ "_id": 0, "accessKey": 1, } err := coll.FindOne( context.TODO(), filter, options.FindOne().SetProjection(project), ).Decode(&result) if err != nil { if err == mongo.ErrNoDocuments { http.Error(w, "No document found with that UUID", http.StatusBadRequest) return } http.Error(w, err.Error(), http.StatusInternalServerError) return } // Check the sender can actually edit the paste if body.AccessKey != result["accessKey"] { http.Error(w, "Invalid access key", http.StatusUnauthorized) return } // Delete matching document //opts := options.Delete().SetHint(bson.D{{Key: "uuid", Value: 1}}) res, err := coll.DeleteOne(context.TODO(), filter, nil) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if res.DeletedCount == 0 { http.Error(w, "Error matching and deleting document", http.StatusInternalServerError) return } response := make(map[string]string) response["info"] = "Document deleted" w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(response); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } }
PUT路由代码
/* PUT /{uuid} r.Body: "accessKey" -> required "content" -> optional "name" -> optional "filetype" -> optional "expiresIn" -> optional ^ At least one of the 4 optional fields must be updated Updates an existing Paste in the MongoDB database and returns a JSON document { uuid: UUID, name: String, content: String, filetype: String, accessKey: String, expiresAt: Date } */ func (s *Server) updatePaste() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { start := time.Now() defer func() { log.Printf("%s %s [%v]", r.Method, r.URL.Path, time.Since(start), ) }() uuidStr, _ := mux.Vars(r)["uuid"] var paste Paste var body PasteBody if err := decodeJSONBody(w, r, &body); err != nil { var mr *badRequest if errors.As(err, &mr) { http.Error(w, mr.msg, mr.status) } else { log.Print(err.Error()) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } return } if err := paste.EditPaste(&body); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } doc, err := toBsonDoc(&paste) if err != nil { log.Println(err) http.Error(w, "Error converting request body to BSON document", http.StatusInternalServerError) } coll := s.Client.Database("pastes").Collection("files") // Check document and provided accessKey match var result bson.M filter := bson.M{"uuid": uuidStr} project := bson.M{ "_id": 0, "accessKey": 1, } err = coll.FindOne( context.TODO(), filter, options.FindOne().SetProjection(project), ).Decode(&result) if err != nil { if err == mongo.ErrNoDocuments { http.Error(w, "No document found with that UUID", http.StatusBadRequest) return } http.Error(w, err.Error(), http.StatusInternalServerError) return } // Check the sender can actually edit the paste if paste.AccessKey != result["accessKey"] { http.Error(w, "Invalid access key", http.StatusUnauthorized) return } // Update document filter = bson.M{"uuid": uuidStr} update := bson.M{"$set": doc} res, err := coll.UpdateOne(context.TODO(), filter, update) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if res.MatchedCount == 0 || res.ModifiedCount == 0 { http.Error(w, "Error matching and updating document", http.StatusInternalServerError) return } response := make(map[string]string) response["uuid"] = uuidStr response["expiresAt"] = paste.ExpiresAt.Time().String() w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(response); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } }
GET路由代码
/* GET /{uuid} Returns the Paste from the MongoDB database with the matching UUID in JSON { uuid: UUID, name: String, content: String, filetype: String, accessKey: String, expires: Date } */ func (s *Server) getPaste() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { start := time.Now() defer func() { log.Printf("%s %s [%v]", r.Method, r.URL.Path, time.Since(start), ) }() uuidStr, _ := mux.Vars(r)["uuid"] coll := s.Client.Database("pastes").Collection("files") var result bson.M filter := bson.M{"uuid": uuidStr} project := bson.M{ "_id": 0, "accessKey": 0, "uuid": 0, } err := coll.FindOne( context.TODO(), filter, options.FindOne().SetProjection(project), ).Decode(&result) if err != nil { if err == mongo.ErrNoDocuments { http.Error(w, "No document found with that UUID", http.StatusBadRequest) return } http.Error(w, err.Error(), http.StatusInternalServerError) return } // Convert to long date format result["expiresAt"] = primitive.DateTime(result["expiresAt"].(primitive.DateTime)).Time().String() w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(result); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } }
问题解决
排查后发现是拼写错误:DELETE路由中获取集合时使用的数据库名是"paste",而GET、PUT路由使用的正确数据库名是"pastes"。改用全局变量存储数据库名字符串后,问题得到解决。
内容的提问来源于stack exchange,提问作者h5law
相关产品推荐
相关产品推荐

