Files
blog-server/internal/pkg/httputil/request.go
2026-08-19 22:05:49 +08:00

137 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package httputil
import (
"encoding/json"
"errors"
"io"
"net"
"net/http"
"net/netip"
"net/url"
"server/internal/config"
"server/internal/model/common"
"server/internal/pkg/errs"
"server/internal/pkg/validator"
"strconv"
"strings"
"github.com/go-chi/chi/v5"
)
// Pagination 分页请求参数
func Pagination(r *http.Request) *common.Pagination {
q := r.URL.Query()
// page 默认 1
page := int32(1)
if p := q.Get("page"); p != "" {
if v, err := strconv.Atoi(p); err == nil && v >= 1 {
page = int32(v)
}
}
// pageSize 默认 10
pageSize := int32(10)
if ps := q.Get("page_size"); ps != "" {
if v, err := strconv.Atoi(ps); err == nil && v >= 1 && v <= 100 {
pageSize = int32(v)
}
}
return &common.Pagination{
Page: page,
PageSize: pageSize,
}
}
func URLParamInt32(r *http.Request, key string) (int32, error) {
v := chi.URLParam(r, key)
if v == "" {
return 0, errs.ErrIDRequired
}
n, err := strconv.ParseInt(v, 10, 32)
if err != nil {
return 0, errs.ErrInvalidID
}
return int32(n), nil
}
// maxJSONBodySize JSON 请求体上限1MB
const maxJSONBodySize = 1 << 20
func BindJson(w http.ResponseWriter, r *http.Request, dest any) error {
// 超限时 Decode 返回 *http.MaxBytesError → 413
r.Body = http.MaxBytesReader(w, r.Body, maxJSONBodySize)
if err := json.NewDecoder(r.Body).Decode(dest); err != nil {
var maxBytesErr *http.MaxBytesError
if errors.As(err, &maxBytesErr) {
return errs.ErrBodyTooLarge
}
if errors.Is(err, io.EOF) {
return errs.ErrEmptyBody
}
return errs.ErrInvalidJSON
}
if err := validator.Struct(dest); err != nil {
return err
}
return nil
}
// ClientIP 获取客户端 IPtrust_proxy=true 时信任代理头,否则用 RemoteAddr防伪造
func ClientIP(r *http.Request) netip.Addr {
addr, _ := netip.ParseAddr(clientIPString(r))
return addr
}
func clientIPString(r *http.Request) string {
// 只在 trust_proxy=true 时信任代理头(防伪造)
if config.GetBool("server.trust_proxy") {
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
parts := strings.Split(xff, ",")
for _, v := range parts {
if ip := strings.TrimSpace(v); ip != "" {
return ip
}
}
}
// 再取 X-Real-IPnginx 单层代理覆盖时最准)
if realIP := strings.TrimSpace(r.Header.Get("X-Real-IP")); realIP != "" {
return realIP
}
}
// RemoteAddr: IP:port
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err == nil {
return host
}
return r.RemoteAddr
}
func BuildFileUrl(path *string) string {
if path == nil || *path == "" {
return ""
}
baseURL := config.GetString("file.base_url")
if baseURL == "" {
return ""
}
result, err := url.JoinPath(baseURL, *path)
if err != nil {
return ""
}
return result
}