httpface

package
v0.0.1-beta.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 28, 2026 License: AGPL-3.0 Imports: 6 Imported by: 0

Documentation

Overview

Package httpface 管理面/用户面 HTTP 边界:响应书写(WriteJSON/WriteErr/ WriteServiceErr,service 错误→HTTP 状态映射表唯一一份)+ 请求参数边界 (ClampLimit——两面包共享的分页上限钳制)。

依赖仅 internal/service/errors 叶子包(零内部依赖),不反向依赖 handler/service/auth/server 任何上层包——依赖图无环。

Index

Constants

View Source
const (
	// HeaderStatsEffectiveFrom 本次读取真正使用的窗口下界(RFC3339,UTC)。
	HeaderStatsEffectiveFrom = "X-Stats-Effective-From"
	// HeaderStatsEffectiveTo 本次读取真正使用的窗口上界(RFC3339,UTC,半开)。
	HeaderStatsEffectiveTo = "X-Stats-Effective-To"
	// HeaderStatsStorage 本次读取实际使用的存储(cube|raw)。
	HeaderStatsStorage = "X-Stats-Storage"
)

统计响应回显头(spec-stats-window-plan-2026-09-25 §4.4(b)):三个 200 体是 **裸数组**(加字段即破契约)⇒ 生效窗口与实际存储只能走响应头。

Variables

This section is empty.

Functions

func ClampLimit

func ClampLimit(l int) int

ClampLimit 分页 limit 统一钳制(上限 200,与 /usage_logs 同语义):超限 裁剪到 200;≤0 原样透传(repo 归一下限 ≤0→20)。管理面误操作 limit=2147483647 不再全表物化(spec 2026-08-17 边界收敛,6 端点共享)。

func ResolveStatsWindow

func ResolveStatsWindow(from, to *time.Time, window *string, now time.Time) (time.Time, time.Time, error)

ResolveStatsWindow 统计端点请求边界的**窗口形态解析**(P3,spec §7.3/§7.3b): 把线上三字段(from/to/window,三态都可选)解码成 domain.Admit 唯一接受的一对 绝对时刻。窗口**恰择一**:

(from != nil && to != nil && window == nil) -> 绝对窗口,原样直透
(from == nil && to == nil && window != nil) -> 相对窗口,服务端自持 now:
                                               to = ceilHour(now)、from = to − d
其余(两态都给 / 只给一端 / 两态都不给)    -> 400 reason=window_ambiguous
window 不是 time.ParseDuration 能解析的时长串 -> 400 reason=window_invalid

**这是解码,不是判定**——本函数不问"这个窗口能不能被服务"(cost / coverage / zone 三条判定全部且只由 domain.Admit 回答,Admit 仍是唯一判定入口)。为什么 落在这里:

  1. 它必须**唯一**。6 个启用 `window` 的端点分居两个包(handler 与 handler/user),httpface 是本仓库既有的"两面包共享的请求参数边界" (包注释里的 ClampLimit 就是同一理由的先例);写两份 = 两处可漂移。
  2. 它必须返回 **400 线缆载体**。只有 httpface 能同时命名 domain(取 StatsRejectWindowAmbiguous)与 serviceerr(取承载类型)而不成环 (httpface 不能 import internal/service)。放在 domain 则每个调用点都要把 `*domain.StatsWindowError` 再包成 `*serviceerr.StatsWindowError`——那层包装 就是本 spec 明令禁止的垫片。
  3. 它**不能**放进 Admit。wire 形态(三个可选字段)在 domain 之外:Admit 收到 的已经是 (from, to)。把可选性灌进 domain 会让 domain 认识 HTTP 形态。

**只收时长,不收日历天**(spec §7.3b,评审 m3):`d` 不是 time.ParseDuration 的单位,`7d` 的拒绝是 ParseDuration 的默认行为,此处只负责把它映射成 400 + 机读 reason(用户裁决:`24h` 在 DST 切换日 ≠ 一个日历天,而本 API 唯一的对齐 基准是固定 1h 网格,另立 AddDate 日历语义会引入第二套窗口长度语义)。

now 由调用方注入(handler 既有的可注入时钟 h.now)——本函数不读墙钟,故 A11/A12 可确定性断言。返回值 error 恒为 nil 或 `*serviceerr.StatsWindowError`(经 WriteServiceErr 出 400 + 机读字段)。

func WriteErr

func WriteErr(w http.ResponseWriter, status int, msg string)

WriteErr 写 {"error": msg} JSON 信封。

func WriteJSON

func WriteJSON(w http.ResponseWriter, status int, v any)

WriteJSON 写 JSON 响应(Content-Type application/json + encoder 编码, 含尾换行——与各包历史副本逐字节一致)。

func WriteServiceErr

func WriteServiceErr(w http.ResponseWriter, err error)

WriteServiceErr 统一把 service 错误映射为 HTTP 状态(映射表唯一一份)。 404 输出 err.Error()(service 层已把缺失 id 详情包装进 ErrNotFound,与 批量 404 同语义);未命中哨兵 → 500 "internal error"(不泄露内部细节)。 400 额外序列化统计窗口拒绝的机读字段(spec §4.4(c))。

func WriteStatsEcho

func WriteStatsEcho(w http.ResponseWriter, exec domain.Exec)

WriteStatsEcho 写三个回显头,值**直接取自 service 判定后回传的 Exec**。 调用方绝不重算窗口判定:判定 owner 只有 domain.Admit 一处(重算即第二份 判定,而两份判定的 now 不同还可能给出不同窗口,回显就会撒谎)。

无跨源部署(管理台/用户台同源由本进程服务),故不需要 Access-Control-Expose-Headers;若将来跨源,这三个头必须先暴露。

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL