4. 使用进行身份认证

任务说明

本案例将实现一个带认证的天气数据客户端。目标是通过请求头(X-)携带密钥访问受保护的API端点,正确处理401 Unauthorized认证失败响应,并在认证成功后解析并展示返回的天气数据。

核心实现

import requests
import sys

# 配置区
API_BASE_URL = "https://api.example-weather.com/v1"
 = "your--here"  # 替换为真实密钥
CITY = "Beijing"

def fetch_weather(city: str, : str) -> dict:
    """携带请求天气数据,处理认证失败场景"""
    url = f"{API_BASE_URL}/weather"
    headers = {
        "X-": ,
        "Accept": "application/json"
    }
    params = {"city": city}

    try:
        resp = requests.get(url, headers=headers, params=params, timeout=10)
    except requests.exceptions.RequestException as e:
        raise SystemExit(f"[网络错误] 请求失败: {e}")

    # 处理认证失败
    if resp.status_code == 401:
        raise PermissionError(
            f"[认证失败] 状态码401 - 无效或已过期。"
            f"响应内容: {resp.text[:200]}"
        )

    # 处理其他HTTP错误
    if resp.status_code != 200:
        raise RuntimeError(
            f"[HTTP {resp.status_code}] 请求未成功: {resp.text[:200]}"
        )

    # 解析成功响应
    try:
        data = resp.json()
    except ValueError:
        raise ValueError("响应不是有效的JSON格式")

    return data

def display_weather(data: dict) -> None:
    """格式化输出天气数据"""
    if "location" not in data or "current" not in data:
        raise ValueError("响应缺少必要字段: location/current")

    loc = data["location"]
    cur = data["current"]
    print(f"城市: {loc.get('name', '未知')}")
    print(f"温度: {cur.get('temperature_c', 'N/A')}°C")
    print(f"天气: {cur.get('condition', '未知')}")
    print(f"湿度: {cur.get('humidity', 'N/A')}%")
    print(f"风速: {cur.get('wind_kph', 'N/A')} km/h")

if __name__ == "__main__":
    # 演示两种密钥传递方式:请求头(默认)与URL参数(备用)
    # 方式一:请求头认证(推荐)
    try:
        weather_data = fetch_weather(CITY, )
        print("=== 认证成功,天气数据如下 ===")
        display_weather(weather_data)
    except PermissionError as e:
        print(e)
        print("\n[提示] 请检查是否正确,或访问API提供商控制台重新生成密钥。")
        sys.exit(1)
    except (RuntimeError, ValueError) as e:
        print(f"[错误] {e}")
        sys.exit(1)

    # 方式二:URL参数认证(备用演示,仅展示请求构造差异)
    # 实际使用时取消注释以下代码,并注释掉方式一
    """
    url = f"{API_BASE_URL}/weather"
    params = {"city": CITY, "": }
    resp = requests.get(url, params=params, timeout=10)
    if resp.status_code == 401:
        print("URL参数认证失败:密钥无效")
    elif resp.status_code == 200:
        print("URL参数认证成功")
        display_weather(resp.json())
    """

关键设计说明

上述代码实现了完整的认证闭环:请求头携带密钥是主用方式,符合大多数API网关的推荐实践;URL参数方式以注释形式保留,便于理解两种传递方式的差异。代码对401状态码做了专门捕获并抛出PermissionError,与网络错误、其他HTTP错误、JSON解析错误分别处理,确保认证失败时能给出明确提示而非笼统报错。

落地与验证

# 安装依赖(仅需requests库)
pip install requests

# 运行脚本(需先替换为真实密钥)
python weather_client.py

验证方法:

  1. 认证失败路径:将设为任意无效字符串(如"invalid-key-123"),运行脚本,预期输出包含[认证失败] 状态码401的错误信息,程序以退出码1结束。
  2. 认证成功路径:填入有效,运行脚本,预期输出以=== 认证成功,天气数据如下 ===开头,随后显示城市、温度、天气、湿度、风速等字段。
  3. 判定标准:认证失败时能捕获401并给出明确错误;认证成功时能正确解析并打印JSON中的location和current字段;网络异常时能捕获并提示。
认证请求流程 Python客户端 weather_client.py 携带X- 天气API服务器 /v1/weather 校验 ① GET请求 + 请求头 X-: your-key ② 200 OK + JSON数据 401 Unauthorized 密钥无效/过期 ③ 401 认证失败 异常处理 捕获401 → PermissionError 提示检查密钥 退出码1 解析并打印天气数据

验证判定标准:当使用无效密钥时,程序必须输出[认证失败] 状态码401并退出;使用有效密钥时,程序必须输出天气字段且无异常。若API返回非200状态码(如403、500),程序应抛出对应RuntimeError并显示响应片段,便于排查问题。