Compare commits

..

3 commits

Author SHA1 Message Date
WJQSERVER
87fc425dc4
Merge pull request #32 from infinite-iroha/dev
0.2.8
2025-06-25 17:50:00 +08:00
wjqserver
76d07364ae optimize defaulterrorhandle && add SetRespBodyFile 2025-06-25 17:49:03 +08:00
wjqserver
9ec1d1f2c6 update deps 2025-06-22 18:12:29 +08:00
4 changed files with 71 additions and 1 deletions

View file

@ -9,10 +9,13 @@ import (
"html/template" "html/template"
"io" "io"
"math" "math"
"mime"
"net" "net"
"net/http" "net/http"
"net/netip" "net/netip"
"net/url" "net/url"
"os"
"path"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -649,6 +652,56 @@ func (c *Context) GetRequestURIPath() string {
return c.Request.URL.Path return c.Request.URL.Path
} }
// === 文件操作 ===
// 将文件内容作为响应body
func (c *Context) SetRespBodyFile(code int, filePath string) {
// 清理path
cleanPath := path.Clean(filePath)
// 打开文件
file, err := os.Open(cleanPath)
if err != nil {
c.AddError(fmt.Errorf("failed to open file %s: %w", cleanPath, err))
c.ErrorUseHandle(http.StatusInternalServerError, fmt.Errorf("failed to open file: %w", err))
return
}
defer file.Close()
// 获取文件信息以获取文件大小和MIME类型
fileInfo, err := file.Stat()
if err != nil {
c.AddError(fmt.Errorf("failed to get file info for %s: %w", cleanPath, err))
c.ErrorUseHandle(http.StatusInternalServerError, fmt.Errorf("failed to get file info: %w", err))
return
}
// 尝试根据文件扩展名猜测 Content-Type
contentType := mime.TypeByExtension(path.Ext(cleanPath))
if contentType == "" {
// 如果无法猜测,则使用默认的二进制流类型
contentType = "application/octet-stream"
}
// 设置响应头
c.Writer.Header().Set("Content-Type", contentType)
c.Writer.Header().Set("Content-Length", fmt.Sprintf("%d", fileInfo.Size()))
// 还可以设置 Content-Disposition 来控制浏览器是下载还是直接显示
// c.Writer.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, path.Base(cleanPath)))
// 设置状态码
c.Writer.WriteHeader(code)
// 将文件内容写入响应体
_, err = copyb.Copy(c.Writer, file)
if err != nil {
c.AddError(fmt.Errorf("failed to write file %s to response: %w", cleanPath, err))
// 注意:这里可能无法设置错误状态码,因为头部可能已经发送
// 可以在调用 SetRespBodyFile 之前检查错误,或者在中间件中处理 Context.Errors
}
c.Abort() // 文件发送后中止后续处理
}
// == cookie === // == cookie ===
// SetSameSite 设置响应的 SameSite cookie 属性 // SetSameSite 设置响应的 SameSite cookie 属性

View file

@ -121,6 +121,21 @@ func defaultErrorWarp(handler ErrorHandler) ErrorHandler {
return return
} }
} }
// 查看context内有没有收集到error
if len(c.Errors) > 0 {
c.Errorf("errpage: context errors: %v, current error: %v", errors.Join(c.Errors...), err)
if err == nil {
err = errors.Join(c.Errors...)
}
}
// 如果客户端已经断开连接,则不尝试写入响应
// 避免在客户端已关闭连接后写入响应导致的问题
// 检查 context.Context 是否已取消
if errors.Is(c.Request.Context().Err(), context.Canceled) {
log.Printf("errpage: client disconnected, skipping error page rendering for status %d, err: %v", code, err)
return
}
handler(c, code, err) handler(c, code, err)
} }
} }

2
go.mod
View file

@ -4,7 +4,7 @@ go 1.24.4
require ( require (
github.com/WJQSERVER-STUDIO/go-utils/copyb v0.0.4 github.com/WJQSERVER-STUDIO/go-utils/copyb v0.0.4
github.com/WJQSERVER-STUDIO/httpc v0.7.0 github.com/WJQSERVER-STUDIO/httpc v0.7.1
github.com/fenthope/reco v0.0.3 github.com/fenthope/reco v0.0.3
github.com/go-json-experiment/json v0.0.0-20250517221953-25912455fbc8 github.com/go-json-experiment/json v0.0.0-20250517221953-25912455fbc8
) )

2
go.sum
View file

@ -2,6 +2,8 @@ github.com/WJQSERVER-STUDIO/go-utils/copyb v0.0.4 h1:JLtFd00AdFg/TP+dtvIzLkdHwKU
github.com/WJQSERVER-STUDIO/go-utils/copyb v0.0.4/go.mod h1:FZ6XE+4TKy4MOfX1xWKe6Rwsg0ucYFCdNh1KLvyKTfc= github.com/WJQSERVER-STUDIO/go-utils/copyb v0.0.4/go.mod h1:FZ6XE+4TKy4MOfX1xWKe6Rwsg0ucYFCdNh1KLvyKTfc=
github.com/WJQSERVER-STUDIO/httpc v0.7.0 h1:iHhqlxppJBjlmvsIjvLZKRbWXqSdbeSGGofjHGmqGJc= github.com/WJQSERVER-STUDIO/httpc v0.7.0 h1:iHhqlxppJBjlmvsIjvLZKRbWXqSdbeSGGofjHGmqGJc=
github.com/WJQSERVER-STUDIO/httpc v0.7.0/go.mod h1:M7KNUZjjhCkzzcg9lBPs9YfkImI+7vqjAyjdA19+joE= github.com/WJQSERVER-STUDIO/httpc v0.7.0/go.mod h1:M7KNUZjjhCkzzcg9lBPs9YfkImI+7vqjAyjdA19+joE=
github.com/WJQSERVER-STUDIO/httpc v0.7.1 h1:D3NlfY52pwKIOSzkdRrLinUynyKELrcPZEO8QjlBq2M=
github.com/WJQSERVER-STUDIO/httpc v0.7.1/go.mod h1:M7KNUZjjhCkzzcg9lBPs9YfkImI+7vqjAyjdA19+joE=
github.com/fenthope/reco v0.0.3 h1:RmnQ0D9a8PWtwOODawitTe4BztTnS9wYwrDbipISNq4= github.com/fenthope/reco v0.0.3 h1:RmnQ0D9a8PWtwOODawitTe4BztTnS9wYwrDbipISNq4=
github.com/fenthope/reco v0.0.3/go.mod h1:mDkGLHte5udWTIcjQTxrABRcf56SSdxBOCLgrRDwI/Y= github.com/fenthope/reco v0.0.3/go.mod h1:mDkGLHte5udWTIcjQTxrABRcf56SSdxBOCLgrRDwI/Y=
github.com/go-json-experiment/json v0.0.0-20250517221953-25912455fbc8 h1:o8UqXPI6SVwQt04RGsqKp3qqmbOfTNMqDrWsc4O47kk= github.com/go-json-experiment/json v0.0.0-20250517221953-25912455fbc8 h1:o8UqXPI6SVwQt04RGsqKp3qqmbOfTNMqDrWsc4O47kk=