当响应数据不包含可通过Combine解码的对象时,如何抛出错误?

问题描述

我有一个发布者包装结构,可以处理响应状态代码。如果状态码不在200..300范围内,则返回一个对象,否则抛出错误。效果很好。

public func anyPublisher<T:Decodable>(type: T.Type) -> AnyPublisher<T,Error> {
    return URLSession.shared.dataTaskPublisher(for: urlRequest)
        .tryMap { output in
            guard let httpResponse = output.response as? HTTPURLResponse,200..<300 ~= httpResponse.statusCode else {
                throw APIError.unknown
            }
            return output.data
    }
    .decode(type: T.self,decoder: JSONDecoder())
    .receive(on: DispatchQueue.main)
    .eraseToAnyPublisher()
}

使用:

let sendNewUserPublisher = NetworkPublisher(urlRequest: request).anyPublisher(type: User.self)

cancellationToken = sendNewUserPublisher.sink(receiveCompletion: { completion in
    if case let .failure(error) = completion {
        NSLog("error: \(error.localizedDescription)")
    }
},receiveValue: { post in
    self.post = post
})

如上所述,即使响应数据不包含要解码的对象,我也要处理错误。

public func anyPublisher() -> AnyPublisher<URLSession.DataTaskPublisher.Output,URLSession.DataTaskPublisher.Failure> {
    return URLSession.shared.dataTaskPublisher(for: urlRequest)
        // I'd like to handle status code here,and throw an error,if needed
        .receive(on: DispatchQueue.main)
        .eraseToAnyPublisher()
}

在此先感谢您提供的帮助。

解决方法

我建议创建一个处理HTTP响应状态代码验证的Publisher,并将其用于您的其他两个发布者-一个处理空的请求主体,另一个对主体进行解码。

如果即使在验证状态码后仍需要HTTPURLResponse对象,则:

extension URLSession.DataTaskPublisher {
    /// Publisher that throws an error in case the data task finished with an invalid status code,otherwise it simply returns the body and response of the HTTP request
    func httpResponseValidator() -> AnyPublisher<Output,CustomError> {
        tryMap { data,response in
            guard let httpResponse = response as? HTTPURLResponse else { throw CustomError.nonHTTPResponse }
            let statusCode = httpResponse.statusCode
            guard (200..<300).contains(statusCode) else { throw CustomError.incorrectStatusCode(statusCode) }
            return (data,httpResponse)
        }
        .mapError { CustomError.network($0) }
        .eraseToAnyPublisher()
    }
}

或者,如果您不关心响应的任何其他属性,则仅确认其状态码有效:

func httpResponseValidator() -> AnyPublisher<Data,CustomError> {
    tryMap { data,response in
        guard let httpResponse = response as? HTTPURLResponse else { throw CustomError.nonHTTPResponse }
        let statusCode = httpResponse.statusCode
        guard (200..<300).contains(statusCode) else { throw CustomError.incorrectStatusCode(statusCode) }
        return data
    }
    .mapError { CustomError.network($0) }
    .eraseToAnyPublisher()
}

然后,您可以使用它重写anyPublisher函数的两个版本:

extension URLSession.DataTaskPublisher {
    func anyPublisher<T:Decodable>(type: T.Type) -> AnyPublisher<T,Error> {
        httpResponseValidator()
            .decode(type: T.self,decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
    }

    func anyPublisher() -> AnyPublisher<Output,CustomError> {
        httpResponseValidator()
            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
    }
}

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...