Golang REST API草案反馈与讨论
Golang REST API草案反馈与讨论 我的目标是使用Go创建一个尽可能通用且遵循DRY原则的API。为了实现这一点,我做出了一些不太常见的决定:
- 使用AJAX调用来避免在更新网页时重新加载整个页面(导致闪烁)。而不是使用Go。
- 从API中排除硬编码的查询以减少端点(路由)。这样做的好处是,在更新查询时可以修改和添加查询,而无需重新编译API。
- 使用JSON来创建和更新数据,使其更加通用。
- 使用sqlx驱动来进一步减少代码并避免重复。

package main
import (
//"fmt"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
"net/http"
"os"
"strings"
)
var db *sqlx.DB
func main() {
Connect()
http.HandleFunc("/", handler)
http.Handle("/favicon.ico", http.NotFoundHandler())
http.ListenAndServe(":9998", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT,CREATE,DELETE")
w.Header().Set("Access-Control-Allow-Headers", "*")
w.Header().Set("Content-Type", "application/json")
switch r.Method {
case "DELETE":
Delete(w, r)
case "POST":
Create(w, r)
case "PUT":
Update(w, r)
default: //GET
Get(w, r)
}
}
func Getquery(path string) string {
// get query from lookup db
var query string
err := db.QueryRow("SELECT sql_query FROM sqls WHERE sql_id=$1", path).Scan(&query)
if err != nil {
path = ""
}
return query
}
func getpath(r *http.Request) (string, string, string) {
path := strings.Split(r.URL.String(), "/")
switch len(path) {
case 4:
return path[1], path[2], path[3]
case 3:
return path[1], path[2], ""
case 2:
return path[1], "", ""
default:
return "", "", ""
}
}
我的问题是:
- 您能看到任何安全问题吗?(CORS除外)
- 有什么地方您会采取不同的做法吗?
- 对这种通用方法有什么看法?
更详细的描述在这里:Go REST API
更多关于Golang REST API草案反馈与讨论的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于Golang REST API草案反馈与讨论的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
从安全性和架构角度来看,这个通用API设计存在几个关键问题:
1. 安全问题
SQL注入风险
Getquery函数直接从数据库读取SQL语句并执行,这是严重的安全漏洞:
// 当前代码存在SQL注入风险
func Get(w http.ResponseWriter, r *http.Request) {
table, id, _ := getpath(r)
query := Getquery(table)
// 直接拼接用户输入的ID到查询中
finalQuery := strings.Replace(query, "$1", id, -1)
rows, err := db.Query(finalQuery) // 高危!
}
缺少输入验证
没有对路径参数进行验证:
func getpath(r *http.Request) (string, string, string) {
path := strings.Split(r.URL.String(), "/")
// 缺少对path[1], path[2], path[3]的验证
// 恶意输入如:/users;DROP TABLE users;/1 会导致问题
return path[1], path[2], path[3]
}
权限控制缺失
所有查询都使用同一数据库连接,没有基于角色的访问控制。
2. 架构改进建议
使用参数化查询
func Get(w http.ResponseWriter, r *http.Request) {
table, id, _ := getpath(r)
query := Getquery(table)
// 使用参数化查询防止SQL注入
rows, err := db.Query(query, id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer rows.Close()
// 处理结果...
}
添加输入验证
func validateTableName(table string) bool {
// 只允许字母数字和下划线
matched, _ := regexp.MatchString("^[a-zA-Z0-9_]+$", table)
return matched
}
func validateID(id string) bool {
// 验证ID是否为数字
_, err := strconv.Atoi(id)
return err == nil
}
实现查询缓存
var queryCache = sync.Map{}
func Getquery(path string) (string, error) {
// 检查缓存
if cached, ok := queryCache.Load(path); ok {
return cached.(string), nil
}
// 数据库查询
var query string
err := db.QueryRow("SELECT sql_query FROM sqls WHERE sql_id=$1", path).Scan(&query)
if err != nil {
return "", err
}
// 存储到缓存
queryCache.Store(path, query)
return query, nil
}
3. 通用方法的优缺点
优点:
- 减少重复代码
- 动态查询配置
- 快速原型开发
缺点:
- 类型安全丢失
- 调试困难
- 性能优化受限
- 缺乏编译时检查
改进的通用处理器示例:
type APIHandler struct {
db *sqlx.DB
queryCache *sync.Map
validator *Validator
}
func (h *APIHandler) HandleRequest(w http.ResponseWriter, r *http.Request) {
// 验证输入
table, id, action := getpath(r)
if !h.validator.ValidateTable(table) {
http.Error(w, "Invalid table name", http.StatusBadRequest)
return
}
// 获取查询模板
query, err := h.GetQuery(table, action)
if err != nil {
http.Error(w, "Query not found", http.StatusNotFound)
return
}
// 根据方法处理
switch r.Method {
case http.MethodGet:
h.handleGet(w, r, query, id)
case http.MethodPost:
h.handlePost(w, r, query)
case http.MethodPut:
h.handlePut(w, r, query, id)
case http.MethodDelete:
h.handleDelete(w, r, query, id)
}
}
func (h *APIHandler) handleGet(w http.ResponseWriter, r *http.Request, query, id string) {
var result interface{}
if id == "" {
// 列表查询
err := h.db.Select(&result, query)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} else {
// 单条查询
err := h.db.Get(&result, query, id)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
}
json.NewEncoder(w).Encode(result)
}
主要安全建议:
- 始终使用参数化查询
- 验证所有用户输入
- 实现查询白名单机制
- 添加请求限流和监控
- 使用HTTPS传输数据
这种通用方法适合内部工具或原型开发,但在生产环境中需要更严格的安全控制和类型安全保证。

