Content-Type校验中间件不能仅用c.Get("Content-Type")字符串匹配,因HTTP头可能存在空格、大小写混用、字符集参数或被代理改写;需用c.Request().Header.ContentType()获取标准化类型,并注意GET无body时的空值绕过风险。

Content-Type校验中间件为什么不能只靠c.Get("Content-Type")
直接用c.Get("Content-Type")拿头,再字符串匹配,看似简单,但会漏掉关键细节:HTTP头值可能带空格、大小写混用(如application/JSON)、字符集参数(如application/json; charset=utf-8),甚至被代理层改写。Fiber的c.Get()返回原始字符串,不做标准化处理,硬比对容易失败。
更危险的是,有些客户端不发Content-Type头,Fiber默认当application/octet-stream处理——此时c.Get("Content-Type")返回空,但c.Body()仍可能有数据,导致校验绕过。
- 必须用
c.Request().Header.ContentType()获取标准化后的类型(自动忽略空格、转小写、剥离参数) - 若需支持多类型(如同时接受
application/json和application/xml),建议用strings.HasPrefix()而非== - 对
multipart/form-data,别只看头——它后面跟着boundary,实际解析依赖c.FormValue()或c.FormFile(),校验中间件里只需确认头存在且以multipart/开头即可
如何写一个可复用的Content-Type校验中间件
中间件本质是func(*fiber.Ctx) error,核心逻辑就三步:取标准化类型 → 判断是否在白名单 → 不通过则立即响应并return。别调c.Next(),否则后续handler还会执行。
示例中间件,只允许application/json和text/xml:
func ContentTypeValidator(allowedTypes ...string) fiber.Handler {
return func(c *fiber.Ctx) error {
ct := string(c.Request().Header.ContentType())
for _, t := range allowedTypes {
if strings.HasPrefix(ct, t) {
return c.Next()
}
}
return c.Status(fiber.StatusUnsupportedMediaType).
JSON(fiber.Map{"error": "unsupported Content-Type", "received": ct})
}
}
- 注册时传白名单:
app.Use("/api", ContentTypeValidator("application/json", "text/xml")) - 注意:这个中间件对
GET请求也生效,但GET通常无body,所以一般只挂到POST/PUT等路径前缀下 - 别在中间件里调
c.Body()或c.FormValue()——这会提前消费请求体,导致下游handler读不到数据
为什么XML校验要特别处理text/xml和application/xml
Fiber对text/xml和application/xml都不自动解析,但两者语义不同:text/xml按文本处理(可能含BOM、换行敏感),application/xml是二进制安全格式。校验中间件只需确认类型存在,但下游handler必须区分对待。
- 若中间件放行了
text/xml,handler里必须用c.Request().Body()读原始字节,再交给defusedxml解析,不能用c.Body() - 若放行
application/xml,同样不能用c.Body()——它对非form类型返回空,必须走c.Request().Body() - 常见错误:中间件只校验
application/xml,但前端发的是text/xml,结果415直接返回,连日志都看不到body内容
Content-Type校验和CSRF、CORS中间件的顺序问题
校验中间件必须放在csurf.New()和cors.New()之后,否则CSRF Token验证可能失败,或CORS预检请求(OPTIONS)被误拦。
-
OPTIONS请求没有Content-Type头,但CORS中间件需要它来判断是否放行;若你把校验中间件挂全局(app.Use(ContentTypeValidator(...))),OPTIONS会被直接415拦截 - 正确做法:只对具体业务路径挂载,比如
app.Post("/user", ContentTypeValidator("application/json"), userHandler) - CSRF中间件要求
POST必须带Content-Type,但不校验值——所以你的校验中间件不能影响CSRF Token的读取(它从header或form里取,不依赖body解析)
真正容易被忽略的是:校验中间件本身不解决编码问题。比如Content-Type: application/json; charset=GBK,Fiber不会帮你转UTF-8,c.BodyParser()可能直接panic。这时候得在中间件里加charset检测,或者让上游统一用UTF-8。


















