代码拉取完成,页面将自动刷新
package hwf
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httputil"
"net/url"
"os"
"path/filepath"
"reflect"
"strings"
"text/template"
)
// ReqCtx provides request-scoped helpers.
type ReqCtx struct {
*HwfCtx // framework context
W http.ResponseWriter
R *http.Request
aborted bool
handlers []HandlerFunc
index int
params map[string]string // path parameters
}
func newReqCtx(w http.ResponseWriter, r *http.Request, params map[string]string) *ReqCtx {
return &ReqCtx{
HwfCtx: NewCtx(),
W: w,
R: r,
index: -1,
params: params,
}
}
func (c *ReqCtx) JSON(code int, v any) {
c.W.Header().Set("Content-Type", "application/json")
c.W.WriteHeader(code)
_ = json.NewEncoder(c.W).Encode(v)
}
// JSONF 使用 App.FormatJSON 对输出进行统一包装;未配置时回退到默认 JSON 输出。
// 如果应用未实现 AppJSONFormatter 接口,将使用默认的 JSON 格式输出。
func (c *ReqCtx) JSONF(v ...any) {
if c.eng != nil {
if d, ok := c.eng.app.(AppJSONFormatter); ok {
d.JSONFormat(c, v...)
return
}
}
}
func (c *ReqCtx) Ctx() *HwfCtx {
return c.HwfCtx
}
// String serves a string with the specified status code.
func (c *ReqCtx) String(code int, s string) {
c.W.WriteHeader(code)
_, _ = c.W.Write([]byte(s))
}
// Bytes serves a byte array with the specified status code.
func (c *ReqCtx) Bytes(code int, b []byte) {
c.W.Header().Set("Content-Type", "application/octet-stream")
c.W.WriteHeader(code)
_, _ = c.W.Write(b)
}
// File helpers
func (c *ReqCtx) ServeFile(filePath string) {
http.ServeFile(c.W, c.R, filePath)
}
// ServeFileDownload serves a file for download with the specified name.
func (c *ReqCtx) ServeFileDownload(filePath, downloadName string) {
if downloadName == "" {
downloadName = filepath.Base(filePath)
}
c.W.Header().Set("Content-Disposition", "attachment; filename=\""+downloadName+"\"")
http.ServeFile(c.W, c.R, filePath)
}
// BindJSON binds JSON body to struct with type conversion and validates.
func (c *ReqCtx) BindJSON(dst any) error {
defer c.R.Body.Close()
// Decode to temporary map
var tempMap map[string]interface{}
err := json.NewDecoder(c.R.Body).Decode(&tempMap)
if err != nil {
return fmt.Errorf("failed to decode JSON: %w", err)
}
// Perform type conversion and binding using the enhanced function
err = bindWithMap(dst, tempMap)
if err != nil {
return fmt.Errorf("failed to bind JSON: %w", err)
}
return ValidateStruct(dst)
}
// BindQuery binds URL query parameters with enhanced type conversion and slice support.
func (c *ReqCtx) BindQuery(dst any) error {
q := c.R.URL.Query()
// Use the enhanced bindByTags which now internally uses bindWithConversion
err := bindWithGetter(dst, "query", func(key string) string { return q.Get(key) })
if err != nil {
return err
}
return ValidateStruct(dst)
}
// BindHeader binds headers with enhanced type conversion and slice support.
func (c *ReqCtx) BindHeader(dst any) error {
// Use the enhanced bindByTags which now internally uses bindWithConversion
err := bindWithGetter(dst, "header", func(key string) string { return c.R.Header.Get(key) })
if err != nil {
return err
}
return ValidateStruct(dst)
}
// BindForm binds form (for POST with application/x-www-form-urlencoded) with enhanced type conversion and slice support.
func (c *ReqCtx) BindForm(dst any) error {
c.R.ParseForm()
// Use the enhanced bindByTags which now internally uses bindWithConversion
err := bindWithGetter(dst, "form", func(key string) string { return c.R.Form.Get(key) })
if err != nil {
return err
}
return ValidateStruct(dst)
}
// SaveUploadedFile saves the uploaded file from a multipart form to the specified destination path.
func (c *ReqCtx) SaveUploadedFile(field, dst string) error {
// ensure multipart parsed
if err := c.R.ParseMultipartForm(32 << 20); err != nil {
return err
}
file, _, err := c.R.FormFile(field)
if err != nil {
return err
}
defer file.Close()
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
return err
}
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, file)
return err
}
// Redirect helper
func (c *ReqCtx) Redirect(code int, url string) {
c.W.Header().Set("Location", url)
c.W.WriteHeader(code)
}
// Header helper
func (c *ReqCtx) SetHeader(key, value string) { c.W.Header().Set(key, value) }
// GetHeader helper
func (c *ReqCtx) GetHeader(key string) string { return c.R.Header.Get(key) }
// ClientIP helper
func (c *ReqCtx) ClientIP() string {
xff := c.R.Header.Get("X-Forwarded-For")
if xff != "" {
return strings.Split(xff, ",")[0]
}
return strings.Split(c.R.RemoteAddr, ":")[0]
}
func (c *ReqCtx) HTML(code int, html string) {
c.W.Header().Set("Content-Type", "text/html; charset=utf-8")
c.W.WriteHeader(code)
_, _ = c.W.Write([]byte(html))
}
// RenderHtml renders the specified HTML template with the given data and status code.
func (c *ReqCtx) RenderHtml(code int, templateFile string, data any) {
tmpl, err := template.ParseFiles(templateFile)
if err != nil {
c.Log().Errorf("RenderHtml: %v", err)
return
}
c.W.Header().Set("Content-Type", "text/html; charset=utf-8")
c.W.WriteHeader(code)
tmpl.Execute(c.W, data)
}
// SetCookie sets a cookie with the specified name, value, max age, path, domain, secure, and httpOnly.
func (c *ReqCtx) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {
if path == "" {
path = "/"
}
ck := &http.Cookie{Name: name, Value: value, Path: path, Domain: domain, MaxAge: maxAge, Secure: secure, HttpOnly: httpOnly, SameSite: http.SameSiteLaxMode}
http.SetCookie(c.W, ck)
}
// Cookie gets the cookie value for the given name.
func (c *ReqCtx) Cookie(name string) (string, error) {
ck, err := c.R.Cookie(name)
if err != nil {
return "", err
}
return ck.Value, nil
}
// Request returns the underlying *http.Request.
func (c *ReqCtx) Request() *http.Request { return c.R }
// ResponseWriter returns the underlying http.ResponseWriter.
func (c *ReqCtx) ResponseWriter() http.ResponseWriter { return c.W }
// Abort stops the execution of subsequent handlers in the chain.
func (c *ReqCtx) Abort() {
c.aborted = true
}
// Aborted returns whether the request has been aborted.
func (c *ReqCtx) Aborted() bool { return c.aborted }
// AbortWithStatus aborts the request and sets the HTTP status code.
func (c *ReqCtx) AbortWithStatus(code int) { c.W.WriteHeader(code); c.aborted = true }
// AbortWithStatusJSON aborts the request, sets the HTTP status code, and sends a JSON response.
func (c *ReqCtx) AbortWithStatusJSON(code int, v any) { c.JSON(code, v); c.aborted = true }
// Proxy forwards the current request to the given target using ReverseProxy.
// Example: c.Proxy("http://localhost:9000/")
// If you need to strip a prefix, adjust c.R.URL.Path before calling.
func (c *ReqCtx) Proxy(target string, setfn func(*httputil.ReverseProxy)) error {
u, err := url.Parse(target)
if err != nil {
return err
}
proxy := httputil.NewSingleHostReverseProxy(u)
if setfn != nil {
setfn(proxy)
}
proxy.ServeHTTP(c.W, c.R)
c.aborted = true
return nil
}
// Next executes the next handler in the middleware chain.
// It should be called within middleware functions to continue processing the request.
func (c *ReqCtx) Next() {
c.index++
for c.index < len(c.handlers) {
c.handlers[c.index](c)
if c.aborted {
return
}
c.index++
}
}
// Query returns the query parameter value for the given key.
func (c *ReqCtx) Query(key string) string {
return c.R.URL.Query().Get(key)
}
// Form returns the form parameter value for the given key.
func (c *ReqCtx) Form(key string) string {
c.R.ParseMultipartForm(32 << 20)
return c.R.FormValue(key)
}
// Param gets path parameter by key and assigns it to the provided pointer.
// Supported types: string, int, int64, uint, uint64, float32, float64, bool
// Example: c.Param("id", &id)
func (c *ReqCtx) Param(key string, dest any) error {
if c.params == nil {
return fmt.Errorf("no path parameters available")
}
value, ok := c.params[key]
if !ok {
return fmt.Errorf("parameter %s not found", key)
}
// Check if dest is a pointer
v := reflect.ValueOf(dest)
if v.Kind() != reflect.Pointer {
return fmt.Errorf("dest must be a pointer")
}
if v.IsNil() {
return fmt.Errorf("dest pointer cannot be nil")
}
// Convert string value to the desired type using setFieldValue
return setFieldValue(v.Elem(), value)
}
// GetRawData reads the raw request body data.
func (c *ReqCtx) GetRawData() ([]byte, error) {
defer c.R.Body.Close()
b, err := io.ReadAll(c.R.Body)
return b, err
}
// ==================== Test Helpers ====================
// NewTestReqCtx creates a ReqCtx for testing purposes
func NewTestReqCtx(w http.ResponseWriter, r *http.Request) *ReqCtx {
return newReqCtx(w, r, nil)
}
// SetHandlers sets the handler chain for testing
func (c *ReqCtx) SetHandlers(handlers []HandlerFunc) {
c.handlers = handlers
c.index = -1
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。