fastapi: 存在httpbasic验证时Json返回需要增加headers

一,例子

当你在 FastAPI 中触发一个 401 异常时,
浏览器到底要不要弹出登录框,只取决于响应头(Headers)里有没有 WWW-Authenticate 字段,
而跟响应体(Body)是 JSON、纯文本还是 HTML 页面没有任何关系。

代码:

    # --- 状况 A:如果是 API 子应用的请求,一律返回 JSON ---
    if path.startswith("/api"):
        status_code = err_data['status_code']
        content = {"data": {}, "msg": err_data['detail'], "code": err_data['status_code']},

        if status_code == 401:
            headers = {"WWW-Authenticate": 'Basic realm="ApiPortal"'}
            return JSONResponse(
                status_code=err_data['status_code'],
                content=content,
                headers=headers,
            )
        else:
            return JSONResponse(
                status_code=err_data['status_code'],
                content=content,
            )

 

二,api子应用和admin都添加了httpbasic验证的情况

两处有httpbasic的情况要添加realm进行区分:

代码:

security = HTTPBasic(realm="ApiPortal")
# 初始化 Basic 认证组件
security = HTTPBasic(realm="AdminPortal")

处理异常时返回的代码:

# 返回异常信息,根据子应用和是否ajax分别进行处理,管理后非ajax请求,跳到错误页面
def response_exception(request: Request,err_data):
    path = request.url.path

    # --- 状况 A:如果是 API 子应用的请求,一律返回 JSON ---
    if path.startswith("/api"):
        status_code = err_data['status_code']
        content = {"data": {}, "msg": err_data['detail'], "code": err_data['status_code']},

        if status_code == 401:
            headers = {"WWW-Authenticate": 'Basic realm="ApiPortal"'}
            return JSONResponse(
                status_code=err_data['status_code'],
                content=content,
                headers=headers,
            )
        else:
            return JSONResponse(
                status_code=err_data['status_code'],
                content=content,
            )

    # --- 状况 B:如果是 Admin 子应用的请求,需要分情况讨论 ---
    elif path.startswith("/admin"):
        accept_header = request.headers.get("accept", "")
        requested_with = request.headers.get("x-requested-with", "")

        # 判断是否为 Ajax 请求:
        # 1. 检查 X-Requested-With 是否为 XMLHttpRequest (传统 Ajax)
        # 2. 或者检查 Accept 头是否更倾向于接收 JSON (现代 fetch/axios 请求)
        is_ajax = (
                requested_with.lower() == "xmlhttprequest" or
                ("application/json" in accept_header and "text/html" not in accept_header)
        )
        if is_ajax:
            content={"data": {}, "msg": err_data['detail'], "code": err_data['status_code']}
            if err_data['status_code'] == 401:
                headers = {"WWW-Authenticate": 'Basic realm="AdminPortal"'}
                return JSONResponse(
                    status_code=err_data['status_code'],
                    content=content,
                    headers=headers,
                )
            else:
                return JSONResponse(
                    status_code=err_data['status_code'],
                    content=content,
                )

        else:
            # 传统的浏览器地址栏直接访问或表单提交出错 -> 渲染精美的错误 HTML 页面
            # 传入 request 和 错误信息 供模板渲染
            return templates.TemplateResponse(
                name="error/error.html",
                context={"request": request, "status_code": err_data['status_code'], "detail": err_data['detail']},
                status_code=err_data['status_code'],
                headers={"WWW-Authenticate": 'Basic realm="AdminPortal"'},
                request=request,
            )

 

posted @ 2026-07-23 22:26  刘宏缔的架构森林  阅读(3)  评论(0)    收藏  举报