接口文档
后端一跑起来,浏览器打开 /scalar 就是一份能点开每个接口、能就地发请求的文档,不用再装任何东西。它渲染的正是前端 npm run gen:api 取数的那份 /openapi/v1.json,文档和契约对不上这件事不会发生。生产环境默认连这两个端点都不挂。
开关与配置
{
"SmartAdmin": {
"Scalar": {
"EnabledInProduction": true // 默认 false,开发环境不受它影响
}
}
}关着的时候,生产环境里 /scalar 和 /openapi/{documentName}.json 两个端点压根不映射,请求它们拿到 404 而不是 401。开发环境不看这个开关,两个端点一直在,而且都匿名:契约源本来就是给 npm run gen:api 用的,开发机上再挡一道,只会让代码生成多一步登录。
路由固定是 /scalar,没有前缀配置项。它挂在后端那一侧:开发态由前端模板的 Vite 代理原样转发过去,前后端分开部署时,要打开的是后端地址,不是前端站点地址。
鉴权:壳是壳,数据是数据
生产环境开启后,两个端点的待遇不一样:
| 端点 | 里面有什么 | 生产开启后 |
|---|---|---|
/scalar | 纯静态渲染器,不含任何接口信息 | 始终匿名 |
/openapi/{documentName}.json | 完整 API 契约 | 要权限码 GET:/openapi/{documentname}.json |
一份完整契约匿名可取,等于把侦察面直接送出去,所以收紧的是它。壳页面收紧没有意义,它自己什么都不知道;真收紧了反而打不开,浏览器新开的标签页不带 SPA 里存着的 Bearer 令牌,一进去就是 401。
令牌进 Scalar 是手动的一步:点右上角 Authentication,选 Bearer,粘贴。内核不做 ?access_token= 那种 query 兜底,它会把长效 JWT 明文留在浏览器历史和网关访问日志里。实时通知的 Hub 付了这笔代价,是因为 WebSocket 握手在协议上带不了请求头,文档 UI 有别的办法。
授权:这个码发给谁
权限码落在内置菜单「系统运维 → 接口文档」下的「查看契约」按钮上,页面那一行只管可见性,照本仓库「能力挂按钮」的通例。在角色管理里把这颗按钮勾给某个角色,该角色的人在生产环境就取得到契约 JSON;超管不受权限码约束,一直取得到。只勾了页面、没勾这颗按钮的人照样打得开 /scalar,只是拉不到契约,页面是空的。
未登录是 401,登录了但没这个码是 403。两个状态码分得开,排查时不用猜是令牌没粘上还是权限没配。
后台入口页
「系统运维 → 接口文档」是一个薄入口页,进去就在新标签页打开 /scalar,正文只留两样东西:弹窗被拦时用的手动链接,和一个「复制我的接口令牌」按钮。复制出来的就是当前登录态的 access token,粘进 Scalar 的 Authentication 面板就能开始试接口。
这个页面不感知后端的环境开关。生产没开启时,新标签页拿到的是 404,按钮空跑一次也没有副作用。让前端去猜后端配了什么,维护成本反而更高。
依赖:核心包唯一的第三方例外
Scalar.AspNetCore 是核心四包「只依赖 SqlSugarCore 加 Microsoft.*」这条红线上唯一的具名例外。它零传递依赖、UI 资源内嵌在包里,只做端点映射和页面渲染,不碰序列化、存储、鉴权任何一条链路。
做成可选包的方案被否掉了。SmartAdmin.Excel、SmartAdmin.Caching.Redis 那批背后是多数消费方用不上的业务能力,「不装」本身就是收益;文档 UI 是开发期工具,设计意图是每个消费方默认都有,做成可选包等于把「默认都有」改成「知道它存在、还愿意再装一个包的人才有」。