12-给SPA友好的401和403响应

给 SPA 友好的 401 和 403 响应

浏览器里的单页应用往往统一拦截 JSON 里的业务码。认证失败也应返回与业务接口一致的错误信封,前端才能用同一套逻辑弹窗或跳登录。

背景与问题

SPA 拦截器常见约定是:读 code 和 message,再决定弹窗或跳登录。ASP.NET Core 默认的 Challenge 和 Forbid 不一定走同一套序列化管道。未登录时可能拿到空 body 或重定向 HTML,前端当成网络错误;已登录无权限时,文案又和业务失败混在一起。

历史项目里还有「业务失败仍返回 HTTP 200,由信封里的码区分」的约定。网关按状态码做监控和限流时,这种约定会把认证失败统计成成功请求,告警和容量判断都会偏。

做法

自定义认证事件,重写 Challenge 和 Forbid:写出统一错误信封,文案走请求语言参数做本地化。未登录与无权限返回不同文案。OnChallenge 里要调用 HandleResponse(),否则框架可能继续写默认头或二次写 body。

csharp 复制代码
options.Events = new JwtBearerEvents
{
    OnChallenge = async ctx =>
    {
        ctx.HandleResponse();
        ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
        ctx.Response.ContentType = "application/json";
        await ctx.Response.WriteAsJsonAsync(new
        {
            code = 401,
            message = Localize(ctx.HttpContext, "未登录或令牌已失效"),
            fields = Array.Empty<string>()
        });
    },
    OnForbidden = async ctx =>
    {
        ctx.Response.StatusCode = StatusCodes.Status403Forbidden;
        ctx.Response.ContentType = "application/json";
        await ctx.Response.WriteAsJsonAsync(new
        {
            code = 403,
            message = Localize(ctx.HttpContext, "无权限执行此操作"),
            fields = Array.Empty<string>()
        });
    }
};

若必须兼容旧前端,把「是否用真实 HTTP 错误码」做成配置:允许业务失败仍返回 200、由信封区分。这个行为应是显式开关,写在配置里,便于网关和前端同时知晓。

取舍

一律返回 200 会削弱网关按状态码做限流和监控的能力。新项目更适合返回真实的 401 和 403,由前端拦截器统一弹消息。本地化键缺失时要有兜底文案,避免把资源键原样甩给用户。

匿名对象在这里只用于错误信封的形状示例。若这条路径日志量很大,应换成具名错误类型,避免信息日志被编译器生成的类型名刷屏。

结语

给 SPA 的 401 和 403 应返回与业务接口同构的 JSON 信封,并区分未登录与无权限。

相关推荐
caimouse7 小时前
MonoDevelop 编译用户应用程序时调用的是哪个编译器
c#
will_net9 小时前
apollo 站点部署&C#接入
开发语言·c#·apollo
sunlifenger13 小时前
嵌入式部署,解锁张家港智慧灯杆的“极简组网”密码
科技·状态模式
吾与谁归in16 小时前
C#脚本工具,支持智能提示自动补全(WPF + Roslyn + AvalonEdit)
开发语言·c#·wpf
昂好的day16 小时前
WPF 从零入门(上):WPF 是什么、XAML 语法、布局与样式全解
开发语言·c#·wpf
吃一碗粉只付一碗钱1 天前
10-令牌里的用户主键不要明文
服务器·数据库·sql·sqlserver·c#
胖纸不争2 天前
EnergyStarZ:让 Windows 后台进程给前台让路的能效管理器(v1.2 发布)
windows·性能优化·c#·win32·ef core
上火的金鱼妹2 天前
关于多进程sse连接通知的实战
javascript·状态模式·sse