将可编码结构编码为 Alamofire POST 参数 - Swift

Posted

技术标签:

【中文标题】将可编码结构编码为 Alamofire POST 参数 - Swift【英文标题】:Encode an encodable struct to Alamofire POST parameters - Swift 【发布时间】:2021-09-10 22:22:35 【问题描述】:

我正在尝试将我的前端 ios 应用 (Swift) 连接到我的 Django 后端。

我尝试创建一个处理任何请求的APIRouter 类。这个类需要符合URLRequestConvertible

我有这个结构:

struct APIAccountID: Codable 
    
    let username: String
    let password: String

这是可编码的。

我正在尝试在 Alamofire POST 请求中传递此结构的数据。

所以我试图像这样对对象进行编码:

var parameters: [String: AnyObject]? 
    switch self 
    case .fetchAccessToken(let accountID):
        if let data = try? JSONEncoder().encode(accountID) 
            return data
         else 
            print("Error: encoding post data")
            return nil
        
    default: break
    

这不起作用,因为在使用 Alamofire URLEncodedFormParameterEncoderJSONParameterEncoder 时,AnyAnyObject 不可编码。

如果我使用这个参数开关:

var parameters: Data? 
        switch self 
        case .fetchAccessToken(let accountID):
            if let data = try? JSONEncoder().encode(accountID) 
                return data
             else 
                print("[fetchAccessToken] Error: encoding post data")
            
        default:
            break
        
        return nil
    

我在发送请求时收到此错误:


    "non_field_errors" =     (
        "Invalid data. Expected a dictionary, but got str."
    );

我的 URLRequestConvertible 函数:

// MARK: - URLRequestConvertible
extension APIRouter: URLRequestConvertible 
    
    func asURLRequest() throws -> URLRequest 
        let url = try baseURL.asURL().appendingPathComponent(path)
        var request = URLRequest(url: url)
        request.method = method
        if method == .get 
            request = try URLEncodedFormParameterEncoder()
                .encode(parameters, into: request)
         else if method == .post 
            request = try JSONParameterEncoder().encode(parameters, into: request)
            request.setValue("application/json", forHTTPHeaderField: "Accept")
        
        return request
    

【问题讨论】:

我没有测试过代码,但很明显错误表明参数应该是字典类型,并且你在参数中传递字符串。您必须创建一个字典,在该字典中映射您的模型并将其作为参数传递 请注意:您不需要显式的 CodingKeys 结构。默认情况下,键与属性名称相同。 @George 是的,这只是因为代码是从我的脚本自动生成的。 @AmaisSheikh 问题是我的结构中可以有多种类型,而 Any 或 AnyObject 不可编码... @PaulBénéteau 如果您手动发送此数据,是否有效? Data(#"["username": "Paul", "password": "Bénéteau"]"#.utf8)。我不确定它想要什么数据,所以值得一试。如果这不起作用,请尝试从键中删除 "。如果我们知道正确的输入,我们就可以解决如何从您的结构中获取该输入。 【参考方案1】:

AlamoFire URLFormEncodedParameterEncoderJSONParameterEncoder 可以接受任何符合Encodable 的内容,因此您无需先使用JSONEncoder;他们会处理这些。

您的根本问题是 parameters 的类型应该是什么,因为您将有不同的参数类型,具体取决于 API 调用。

你不能说

var parameters: APIAccountID

因为这不适用于参数是 updatePortfolio 或其他的情况。

你不能简单地说var parameters: Encodable?,因为你需要一个具体的类型。

您需要使用类型擦除。这是一种在 Swift 中使用的技术,允许将原本不相关的类型用作参数。有多种方法,但常见的一种是“装箱”,其中实际类型存储为属性,当您引用类型擦除类型时,您将该引用或函数调用重定向到装箱或包装类型。

因此,在这种情况下,您需要的是 AnyEncodable - Encodable 的类型擦除实现。幸运的是,FlightSchool 存储库中提供了这样的东西 - https://github.com/Flight-School/AnyCodable

将该包添加到项目后,您可以将parameters 修改为

var parameters: AnyEncodable? 
    switch self 
        case .fetchAccessToken(let accountID):
            return AnyEncodable(accountID)
        case .updatePortfolio(let portfolio):
            return AnyEncodable(portfolio)
    

现在,我们的parameters var 有一个固定类型——AnyEncodable?,它符合Encodable,所以每个人都很高兴。该实现要求您使用实际的Encodable 实例化AnyEncodable 的实例

【讨论】:

以上是关于将可编码结构编码为 Alamofire POST 参数 - Swift的主要内容,如果未能解决你的问题,请参考以下文章

使用 HTTP 将结构 Codable 发布为使用 Alamofire 的 JSON 数组

Alamofire:具有额外属性的可编码对象

如何在可编码结构中使用计算属性(swift)

可编码为 CKRecord

如何以编码结构发布数据?

Swift:如何将可编码类型作为函数输入传递给编码或解码 json