- comment.go:commentForm 加 json tag;PostComment 校验分支改 APIError
(404 article_not_found、403 comments_disabled/comments_guests_disabled、
400 校验码、500 article_error),保留 guest 令牌与 flash 机制,成功返回
{ok,redirect:/article/:slug#comment-N,comment_id}
- api.go:新增 APIErrorf(支持 %d/%s 占位符键如 comments_too_long)
- admin_comment.go:approve/reject/delete 改 JSON(parseUintParam 拒绝非
数值 id 400),成功带原 ?saved=1&msg= 查询串 redirect
- main.go:PostComment 迁入 /api;评论审核三操作迁入 /api/admin/comments
- article.html:评论表单改 blogAPI 提交,错误内联 commentError div
- comment_list.html:审核操作改 to commentAct() 委托(confirm 在函数内,
取消不发请求),成功 reload 保持筛选状态
- 测试:security_test env 路由同步 /api;session_upload 评论用例改 JSON
- main_test 冒烟补评论 API 路由断言;go build/vet/test 全绿
71 lines
1.7 KiB
Go
71 lines
1.7 KiB
Go
package handlers
|
||
|
||
import (
|
||
"fmt"
|
||
"net/http"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// APIOK 返回统一成功响应。
|
||
// redirect 非空时附带相对跳转地址(原 302 目标,可含 ?saved=1 等 query);
|
||
// data 额外字段会合并进响应。
|
||
func APIOK(c *gin.Context, redirect string, data gin.H) {
|
||
resp := gin.H{"ok": true}
|
||
if redirect != "" {
|
||
resp["redirect"] = redirect
|
||
}
|
||
for k, v := range data {
|
||
resp[k] = v
|
||
}
|
||
c.JSON(http.StatusOK, resp)
|
||
}
|
||
|
||
// APIError 返回统一错误响应:{ok:false, code, error}。
|
||
// trKey 是 i18n 键,直接作为 code 返回;error 为该键按请求语言的翻译文案。
|
||
// trKey 为空时回退到通用键 "api_error"。
|
||
func APIError(c *gin.Context, status int, trKey string) {
|
||
tr := getTr(c)
|
||
code := trKey
|
||
if code == "" {
|
||
code = "api_error"
|
||
}
|
||
msg := tr[code]
|
||
if msg == "" {
|
||
msg = tr["api_error"]
|
||
}
|
||
c.JSON(status, gin.H{
|
||
"ok": false,
|
||
"code": code,
|
||
"error": msg,
|
||
})
|
||
}
|
||
|
||
// APIErrorf 同 APIError,但 i18n 文案可按 fmt.Sprintf 格式化
|
||
// (针对含 %d/%s 占位符的键,如 comments_too_long)。
|
||
func APIErrorf(c *gin.Context, status int, trKey string, args ...interface{}) {
|
||
tr := getTr(c)
|
||
msg := tr[trKey]
|
||
if msg == "" {
|
||
msg = tr["api_error"]
|
||
} else if len(args) > 0 {
|
||
msg = fmt.Sprintf(msg, args...)
|
||
}
|
||
c.JSON(status, gin.H{
|
||
"ok": false,
|
||
"code": trKey,
|
||
"error": msg,
|
||
})
|
||
}
|
||
|
||
// bindJSON 将 JSON 请求体绑定到 v。
|
||
// 绑定失败时返回 400 + api_invalid_request,并返回 false。
|
||
// 使用前必须保证请求是 JSON(Content-Type: application/json)。
|
||
func bindJSON(c *gin.Context, v interface{}) bool {
|
||
if err := c.ShouldBindJSON(v); err != nil {
|
||
APIError(c, http.StatusBadRequest, "api_invalid_request")
|
||
return false
|
||
}
|
||
return true
|
||
}
|