You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于通用网络层解码401状态码的服务器响应方案

如何让Swift通用网络层HTTPClient获取401响应中的自定义错误信息

问题背景

我从网上获取了一段Swift通用网络层HTTPClient代码,当前遇到的问题是:服务器返回401状态码时,会附带如下JSON响应:

{ "message": "Username or password is incorrect", "success": false }

但现有HTTPClient仅会抛出NetworkError.unauthorized,无法获取响应里的message字段。需要修改load函数来获取这部分自定义错误信息。

解决方案思路

通用网络层不应耦合业务特定错误(比如LoginError),合理的做法是让NetworkError携带服务器返回的通用错误响应模型,将业务逻辑的处理交给上层调用方。

具体实现步骤

1. 定义通用错误响应模型

先创建可解析服务器错误响应的模型:

struct ErrorResponse: Decodable {
    let message: String
    let success: Bool
}

2. 更新NetworkError枚举,添加关联值携带错误响应

修改原有NetworkError,让unauthorized及其他需要的错误类型可以关联ErrorResponse,同时补充本地化描述:

enum NetworkError: Error {
    case invalidURL
    case invalidResponse
    case unauthorized(ErrorResponse?)
    case serverError(Int, ErrorResponse?)
    case decodingError(Error)
    case unknown
}

extension NetworkError: LocalizedError {
    var errorDescription: String? {
        switch self {
        case .invalidURL:
            return NSLocalizedString("无效的请求地址", comment: "")
        case .invalidResponse:
            return NSLocalizedString("服务器返回无效响应", comment: "")
        case .unauthorized(let response):
            return response?.message ?? NSLocalizedString("未授权访问", comment: "")
        case .serverError(let code, let response):
            return response?.message ?? NSLocalizedString("服务器错误,状态码:\(code)", comment: "")
        case .decodingError(let error):
            return NSLocalizedString("数据解析失败:\(error.localizedDescription)", comment: "")
        case .unknown:
            return NSLocalizedString("未知网络错误", comment: "")
        }
    }
}

3. 修改HTTPClient的load函数,解析401响应并关联到错误中

在状态码处理逻辑里,针对401情况尝试解析服务器返回的ErrorResponse,再抛出携带该响应的NetworkError.unauthorized:

class HTTPClient {
    func load<T: Decodable>(_ request: URLRequest) async throws -> T {
        guard let url = request.url else {
            throw NetworkError.invalidURL
        }
        
        let (data, response) = try await URLSession.shared.data(from: url)
        
        guard let httpResponse = response as? HTTPURLResponse else {
            throw NetworkError.invalidResponse
        }
        
        switch httpResponse.statusCode {
        case 200...299:
            do {
                return try JSONDecoder().decode(T.self, from: data)
            } catch {
                throw NetworkError.decodingError(error)
            }
        case 401:
            // 尝试解析错误响应,解析失败则传nil
            let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: data)
            throw NetworkError.unauthorized(errorResponse)
        case 400...599:
            // 其他服务器错误统一处理
            let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: data)
            throw NetworkError.serverError(httpResponse.statusCode, errorResponse)
        default:
            throw NetworkError.unknown
        }
    }
}

4. 上层业务方(如AuthenticationController)处理具体逻辑

在登录等业务场景中,捕获NetworkError.unauthorized后,取出message做业务处理:

class AuthenticationController {
    private let httpClient = HTTPClient()
    
    func login(username: String, password: String) async {
        guard let url = URL(string: "your-login-api-url") else { return }
        var request = URLRequest(url: url)
        // 构造请求方法、参数等...
        
        do {
            let loginResult = try await httpClient.load(LoginResponse.self, request)
            // 登录成功逻辑
        } catch let error as NetworkError {
            switch error {
            case .unauthorized(let response):
                let errorMessage = response?.message ?? "登录失败,请检查账号密码"
                // 展示错误提示给用户
                print(errorMessage)
            default:
                // 处理其他网络错误
                print(error.localizedDescription)
            }
        } catch {
            print("未知错误:\(error.localizedDescription)")
        }
    }
}

为什么不选择抛出业务特定错误?

第一次尝试中直接抛出LoginError的问题在于,HTTPClient是通用网络层,需要适配所有网络请求场景(如获取用户信息、提交订单等),如果在网络层抛出业务特定错误,会导致网络层与业务逻辑强耦合,违背单一职责原则。而让NetworkError携带通用错误响应,既能保留错误的具体信息,又能让上层业务方根据自身需求处理,更符合架构设计的合理性。

内容的提问来源于stack exchange,提问作者john doe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 21:04:58