mirror of
https://github.com/infinite-iroha/touka.git
synced 2026-02-03 08:51:11 +08:00
Merge pull request #8 from infinite-iroha/dev
[engine] add StaticFile && [context] Add Cookie Method, port from gin
This commit is contained in:
commit
f1ff1f935f
2 changed files with 167 additions and 0 deletions
55
context.go
55
context.go
|
|
@ -50,6 +50,8 @@ type Context struct {
|
|||
|
||||
// 引用所属的 Engine 实例,方便访问 Engine 的配置(如 HTMLRender)
|
||||
engine *Engine
|
||||
|
||||
sameSite http.SameSite
|
||||
}
|
||||
|
||||
// --- Context 相关方法实现 ---
|
||||
|
|
@ -77,6 +79,7 @@ func (c *Context) reset(w http.ResponseWriter, req *http.Request) {
|
|||
c.queryCache = nil // 清空查询参数缓存
|
||||
c.formCache = nil // 清空表单数据缓存
|
||||
c.ctx = req.Context() // 使用请求的上下文,继承其取消信号和值
|
||||
c.sameSite = http.SameSiteDefaultMode // 默认 SameSite 模式
|
||||
// c.HTTPClient 和 c.engine 保持不变,它们引用 Engine 实例的成员
|
||||
}
|
||||
|
||||
|
|
@ -489,6 +492,58 @@ func (c *Context) GetLogger() *reco.Logger {
|
|||
return c.engine.LogReco
|
||||
}
|
||||
|
||||
// SetSameSite 设置响应的 SameSite cookie 属性。
|
||||
func (c *Context) SetSameSite(samesite http.SameSite) {
|
||||
c.sameSite = samesite
|
||||
}
|
||||
|
||||
// SetCookie 设置一个 HTTP cookie。
|
||||
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {
|
||||
if path == "" {
|
||||
path = "/"
|
||||
}
|
||||
http.SetCookie(c.Writer, &http.Cookie{
|
||||
Name: name,
|
||||
Value: url.QueryEscape(value),
|
||||
MaxAge: maxAge,
|
||||
Path: path,
|
||||
Domain: domain,
|
||||
SameSite: c.sameSite,
|
||||
Secure: secure,
|
||||
HttpOnly: httpOnly,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Context) SetCookieData(cookie *http.Cookie) {
|
||||
if cookie.Path == "" {
|
||||
cookie.Path = "/"
|
||||
}
|
||||
if cookie.SameSite == http.SameSiteDefaultMode {
|
||||
cookie.SameSite = c.sameSite
|
||||
}
|
||||
http.SetCookie(c.Writer, cookie)
|
||||
}
|
||||
|
||||
// GetCookie 获取指定名称的 cookie 值。
|
||||
func (c *Context) GetCookie(name string) (string, error) {
|
||||
cookie, err := c.Request.Cookie(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 对 cookie 值进行 URL 解码
|
||||
value, err := url.QueryUnescape(cookie.Value)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to unescape cookie value: %w", err)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// DeleteCookie 删除指定名称的 cookie。
|
||||
// 通过设置 MaxAge 为 -1 来删除 cookie。
|
||||
func (c *Context) DeleteCookie(name string) {
|
||||
c.SetCookie(name, "", -1, "/", "", false, false) // 设置 MaxAge 为 -1 删除 cookie
|
||||
}
|
||||
|
||||
// === 日志记录 ===
|
||||
func (c *Context) Debugf(format string, args ...any) {
|
||||
c.engine.LogReco.Debugf(format, args...)
|
||||
|
|
|
|||
112
engine.go
112
engine.go
|
|
@ -845,3 +845,115 @@ func (group *RouterGroup) Static(relativePath, rootPath string) {
|
|||
c.Abort()
|
||||
})
|
||||
}
|
||||
|
||||
// Static File 传入一个文件路径, 使用FileServer进行处理
|
||||
func (engine *Engine) StaticFile(relativePath, filePath string) {
|
||||
// 清理路径
|
||||
relativePath = path.Clean(relativePath)
|
||||
filePath = path.Clean(filePath)
|
||||
|
||||
// 创建一个文件系统处理器,指向包含目标文件的目录
|
||||
// http.Dir 需要一个目录路径
|
||||
dir := path.Dir(filePath)
|
||||
fileName := path.Base(filePath)
|
||||
fileServer := http.FileServer(http.Dir(dir))
|
||||
|
||||
FileHandle := func(c *Context) {
|
||||
// 检查是否是 GET 或 HEAD 方法
|
||||
if c.Request.Method != http.MethodGet && c.Request.Method != http.MethodHead {
|
||||
// 如果不是,且启用了 MethodNotAllowed 处理,则继续到 MethodNotAllowed 中间件
|
||||
if engine.HandleMethodNotAllowed {
|
||||
c.Next()
|
||||
} else {
|
||||
// 否则,返回 405 Method Not Allowed
|
||||
engine.errorHandle.handler(c, http.StatusMethodNotAllowed)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
requestPath := c.Request.URL.Path
|
||||
|
||||
// 构造文件服务器需要处理的请求路径
|
||||
// FileServer 会将请求路径与 http.Dir 的根路径结合
|
||||
// 我们需要将请求路径设置为文件名,以便 FileServer 找到正确的文件
|
||||
c.Request.URL.Path = "/" + fileName // FileServer 期望路径以 / 开头
|
||||
|
||||
// 使用自定义的 ResponseWriter 包装器来捕获 FileServer 可能返回的错误状态码
|
||||
ecw := AcquireErrorCapturingResponseWriter(c)
|
||||
defer ReleaseErrorCapturingResponseWriter(ecw)
|
||||
|
||||
// 调用 FileServer 处理请求
|
||||
fileServer.ServeHTTP(ecw, c.Request)
|
||||
|
||||
// 在 FileServer 处理完成后,检查是否捕获到错误状态码,并调用 ErrorHandler
|
||||
ecw.processAfterFileServer()
|
||||
|
||||
// 恢复原始请求路径
|
||||
c.Request.URL.Path = requestPath
|
||||
|
||||
// 中止处理链,因为 FileServer 已经处理了响应
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
// 注册一个精确匹配的路由
|
||||
engine.GET(relativePath, FileHandle)
|
||||
engine.HEAD(relativePath, FileHandle)
|
||||
engine.OPTIONS(relativePath, FileHandle)
|
||||
|
||||
}
|
||||
|
||||
// Group的StaticFile
|
||||
func (group *RouterGroup) StaticFile(relativePath, filePath string) {
|
||||
// 清理路径
|
||||
relativePath = path.Clean(relativePath)
|
||||
filePath = path.Clean(filePath)
|
||||
|
||||
// 创建一个文件系统处理器,指向包含目标文件的目录
|
||||
// http.Dir 需要一个目录路径
|
||||
dir := path.Dir(filePath)
|
||||
fileName := path.Base(filePath)
|
||||
fileServer := http.FileServer(http.Dir(dir))
|
||||
|
||||
FileHandle := func(c *Context) {
|
||||
// 检查是否是 GET 或 HEAD 方法
|
||||
if c.Request.Method != http.MethodGet && c.Request.Method != http.MethodHead {
|
||||
// 如果不是,且启用了 MethodNotAllowed 处理,则继续到 MethodNotAllowed 中间件
|
||||
if group.engine.HandleMethodNotAllowed {
|
||||
c.Next()
|
||||
} else {
|
||||
// 否则,返回 405 Method Not Allowed
|
||||
group.engine.errorHandle.handler(c, http.StatusMethodNotAllowed)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
requestPath := c.Request.URL.Path
|
||||
|
||||
// 构造文件服务器需要处理的请求路径
|
||||
// FileServer 会将请求路径与 http.Dir 的根路径结合
|
||||
// 我们需要将请求路径设置为文件名,以便 FileServer 找到正确的文件
|
||||
c.Request.URL.Path = "/" + fileName // FileServer 期望路径以 / 开头
|
||||
|
||||
// 使用自定义的 ResponseWriter 包装器来捕获 FileServer 可能返回的错误状态码
|
||||
ecw := AcquireErrorCapturingResponseWriter(c)
|
||||
defer ReleaseErrorCapturingResponseWriter(ecw)
|
||||
|
||||
// 调用 FileServer 处理请求
|
||||
fileServer.ServeHTTP(ecw, c.Request)
|
||||
|
||||
// 在 FileServer 处理完成后,检查是否捕获到错误状态码,并调用 ErrorHandler
|
||||
ecw.processAfterFileServer()
|
||||
|
||||
// 恢复原始请求路径
|
||||
c.Request.URL.Path = requestPath
|
||||
|
||||
// 中止处理链,因为 FileServer 已经处理了响应
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
// 注册一个精确匹配的路由
|
||||
group.GET(relativePath, FileHandle)
|
||||
group.HEAD(relativePath, FileHandle)
|
||||
group.OPTIONS(relativePath, FileHandle)
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue