跳转到内容

静态资源服务

Feat 内置了一个静态资源处理器 HttpStaticResourceHandler,对标 nginx 的静态文件能力:支持文件目录和 classpath 两种资源来源、If-Modified-Since 缓存协商、目录列表、内置路径穿越防护。Feat Cloud 的静态资源能力也是基于它实现的。

创建处理器时通过 baseDir 指定资源位置,支持两种前缀:

FileServerDemo.java
import tech.smartboot.feat.Feat;
import tech.smartboot.feat.core.server.handler.HttpStaticResourceHandler;
Feat.httpServer()
.httpHandler(new HttpStaticResourceHandler(opt -> opt.baseDir("./static")))
.listen(8080);

从磁盘目录读取文件。启动时会校验目录存在性,不存在直接启动失败,避免带病上线。

两种模式下的请求规则一致:

  • GET / → 返回根目录下的 index.html
  • GET /docs/ → 返回 docs/index.html
  • GET /about(无扩展名)→ 依次尝试 /about、/about/index.html、/about.html,适配”目录和同名 html 并存”的站点结构

默认情况下访问目录返回 index.html(不存在则 404)。开发期想浏览目录内容,可以打开 autoIndex:

new HttpStaticResourceHandler(opt -> opt
.baseDir("./static")
.autoIndex(true))

打开后会渲染一个简单的目录列表页。生产环境保持默认关闭——目录列表会把服务器上的文件名全部暴露给访问者。

处理器实现了基于 Last-Modified / If-Modified-Since 的缓存协商:

  • 首次响应携带 Last-Modified 头
  • 浏览器再次请求时带上 If-Modified-Since,资源未变化则返回 304 Not Modified,不传文件体

注意 classpath 模式下资源来自 jar 包,Last-Modified 取的是服务启动时间——jar 内容在运行期不会变,这是合理的简化。文件目录模式取的是文件真实的修改时间。

路径穿越攻击在处理器内被直接拦截:

内置防护
GET /../../etc/passwd → 403 Forbidden

../ 与 ..\ 两种写法都会被拒绝,请求不会触达文件系统。这是处理器自带的兜底,但不要因此放松对自己代码的要求:自研的文件下载接口不走这条管道,需要自己校验(见安全实践)。

其他两个细节也和资源安全有关:

  • favicon.ico 缺失时会尝试从 classpath 提供内置图标,避免大量 404 噪音
  • 404 时优先渲染内置的 feat-404.html 错误页

Feat Cloud 默认就启用了静态资源处理,指向 classpath:static。你可以在 CloudOptions 里改:

CloudOptions 定制静态资源
import tech.smartboot.feat.cloud.FeatCloud;
FeatCloud.cloudServer(options -> {
// 改成磁盘目录
options.setStaticLocations("./static");
// 或换成 jar 内其他资源目录
// options.setStaticLocations("classpath:webapp");
}).listen();

静态资源配置只能通过 CloudOptions 的 Java API 完成(不走 feat.yml),编译期即确定。

静态资源处理器覆盖的是”文件映射”场景。以下需求需要自己扩展:

  • 带 ETag、Cache-Control: max-age 等更精细的缓存策略
  • 断点续传(Range 请求)
  • 上传(上传请用 Multipart 处理,见 HTTP 请求处理)

这些场景可以在处理器外再包一层 HttpHandler 自行实现。