diff --git a/route/route.go b/route/route.go index 235b16c..a47e1ed 100644 --- a/route/route.go +++ b/route/route.go @@ -75,22 +75,39 @@ func InitRouter() *gin.Engine { v1UserGroup.GET("/info", v1.GetUserInfo) // @tiger - RESTful 规范下所有对 user 的写操作,都应该 POST /v1/user/:id + // - 不需要每个更改的属性建一个 API v1UserGroup.PUT("/username", v1.PutUserName) v1UserGroup.PUT("/password", v1.PutUserPwd) v1UserGroup.PUT("/nick", v1.PutUserNick) // 改成 /nickname v1UserGroup.PUT("/desc", v1.PutUserDesc) // 改成 /description - // @tiger - RESTful 规范下应该是 GET /v1/users/?username=xxxx + // @tiger - RESTful 规范建议是 GET /v1/users/?username=xxxx + // 这是一个查询 API,返回一个 users 数组(即使 username 是唯一的) + // 之所以不用 /v1/user/:username 是因为和 /v1/user/:id 路由冲突 + // + // 当前这个设计的问题是:GET 不应该同时接收 request body。 + // GET 方法应该只接收 URL 参数 v1UserGroup.GET("/info", v1.GetUserInfoByUserName) + + // @tiger - 改成 /user/current/custom/... 和上面的 current 对应 + // 如果未来想获得其它用户的 custom 数据,可以用 /v1/user/:id/custom/... 来保持统一 v1UserGroup.GET("/custom/:key", v1.GetUserCustomConf) v1UserGroup.POST("/custom/:key", v1.PostUserCustomConf) v1UserGroup.DELETE("/custom/:key", v1.DeleteUserCustomConf) + + // @tiger - 下面这两个 API 从感知上很难区分。 + // 如果前者是负责上传,后者负责指定的话,那么 + // 前者应该用一个统一的和目的无关的用户文件上传 API,而不是针对 image file 的 v1UserGroup.POST("/upload/image/:key", v1.PostUserUploadImage) v1UserGroup.POST("/file/image/:key", v1.PostUserFileImage) v1UserGroup.DELETE("/image", v1.DeleteUserImage) + // @tiger - 应该用上面提到的统一的文件上传 API 先上传头像文件,然后 + // 用类似上面第二个 API 的方式指定头像文件。这样整体 API 体验更加统一。 v1UserGroup.PUT("/avatar", v1.PutUserAvatar) v1UserGroup.GET("/avatar", v1.GetUserAvatar) + + // @tiger - 删除用户直接用 DELETE /v1/user/:id,不需要在路径中用谓语 v1UserGroup.DELETE("/delete/:id", v1.DeleteUser) } diff --git a/route/v1/user.go b/route/v1/user.go index e7f9a3c..e664c38 100644 --- a/route/v1/user.go +++ b/route/v1/user.go @@ -36,12 +36,6 @@ func PostUserRegister(c *gin.Context) { username := json["user_name"] pwd := json["password"] - // @tiger - POST 方法尽量统一用 JSON 本体来传参。用 query string 来传参的话, - // 1 - 有可能会被路由记录,形成不必要的泄露(这里假设 key 是关键信息) - // 2 - 开发者不需要处理两种入参方式 - // - // query string 更加适合 GET 读操作,或者运维层面的需求,比如跟踪码,回调地址等 - key := c.Param("key") if _, ok := service.UserRegisterHash[key]; !ok { c.JSON(http.StatusOK, @@ -353,6 +347,9 @@ func GetUserInfo(c *gin.Context) { // @Router /user/info [get] func GetUserInfoByUserName(c *gin.Context) { json := make(map[string]string) + + // @tiger 当前这个设计的问题是:GET 不应该同时接收 request body。 + // GET 方法应该只接收 URL 参数 c.BindJSON(&json) userName := json["user_name"] if len(userName) == 0 { @@ -415,6 +412,7 @@ func GetUserCustomConf(c *gin.Context) { return } filePath := config.AppInfo.UserDataPath + "/" + id + "/" + name + ".json" + data := file.ReadFullFile(filePath) if !gjson.ValidBytes(data) { c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: string(data)}) @@ -469,7 +467,7 @@ func DeleteUserCustomConf(c *gin.Context) { return } filePath := config.AppInfo.UserDataPath + "/" + strconv.Itoa(user.Id) + "/" + name + ".json" - os.Remove(filePath) + os.Remove(filePath) // @tiger - 这里万一无法实际删除,后面仍然有可能返回成功 c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)}) }