Merge pull request #32 from infinite-iroha/dev

0.2.8
This commit is contained in:
WJQSERVER 2025-06-25 17:50:00 +08:00 committed by GitHub
commit 87fc425dc4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 71 additions and 1 deletions

View file

@ -9,10 +9,13 @@ import (
"html/template"
"io"
"math"
"mime"
"net"
"net/http"
"net/netip"
"net/url"
"os"
"path"
"strings"
"sync"
"time"
@ -649,6 +652,56 @@ func (c *Context) GetRequestURIPath() string {
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 ===
// SetSameSite 设置响应的 SameSite cookie 属性

View file

@ -121,6 +121,21 @@ func defaultErrorWarp(handler ErrorHandler) ErrorHandler {
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)
}
}

2
go.mod
View file

@ -4,7 +4,7 @@ go 1.24.4
require (
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/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/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.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/go.mod h1:mDkGLHte5udWTIcjQTxrABRcf56SSdxBOCLgrRDwI/Y=
github.com/go-json-experiment/json v0.0.0-20250517221953-25912455fbc8 h1:o8UqXPI6SVwQt04RGsqKp3qqmbOfTNMqDrWsc4O47kk=