当前位置: 首页 > 知识库问答 >
问题:

正确使用AlaMofires的URLRequestConvertible

闻人冷勋
2023-03-14

我读过几本教程,也读过@mattt的自述,但我还是搞不懂几件事。

> 在实际API中的正确用法是什么?看起来,如果我将通过为所有API实现 协议来创建一个路由器,那么它将几乎不可读。是否应该为每个endpoint创建一个路由器?

第二个问题很可能是由于缺乏使用Swift语言的经验引起的。我不明白为什么要用 来构建路由器?为什么我们不将类与静态方法一起使用?这里有一个例子(来自Alamofire's自述文件)

enum Router: URLRequestConvertible {
    static let baseURLString = "http://example.com"
    static let perPage = 50

    case Search(query: String, page: Int)

    // MARK: URLRequestConvertible

    var URLRequest: NSURLRequest {
        let (path: String, parameters: [String: AnyObject]?) = {
            switch self {
            case .Search(let query, let page) where page > 1:
                return ("/search", ["q": query, "offset": Router.perPage * page])
            case .Search(let query, _):
                return ("/search", ["q": query])
            }
        }()

        let URL = NSURL(string: Router.baseURLString)!
        let URLRequest = NSURLRequest(URL: URL.URLByAppendingPathComponent(path))
        let encoding = Alamofire.ParameterEncoding.URL

        return encoding.encode(URLRequest, parameters: parameters).0
    }
}

传递参数有两种方式:

case CreateUser([String: AnyObject])
case ReadUser(String)
case UpdateUser(String, [String: AnyObject])
case DestroyUser(String)

和(假设用户有4个参数)

case CreateUser(String, String, String, String)
case ReadUser(String)
case UpdateUser(String, String, String, String, String)
case DestroyUser(String)

@mattt在示例中使用的是第一个。但这将导致在路由器外部(例如在UIViewControllers中)“硬编码”参数名称。参数名称中的错误可能会导致错误。br>其他人使用的是第二个选项,但在这种情况下,每个参数表示什么根本就不明显。br>正确的方法是什么?

共有3个答案

曹茂材
2023-03-14

你为什么不试着用SweetRouter。它将帮助您删除所有的样板,当您声明一个路由器,它还支持像多个环境这样的东西,您的代码将是真正可读的。

null

struct Api: EndpointType {
    enum Environment: EnvironmentType {
        case localhost
        case test
        case production

        var value: URL.Environment {
            switch self {
            case .localhost: return .localhost(8080)
            case .test: return .init(IP(126, 251, 20, 32))
            case .production: return .init(.https, "myproductionserver.com", 3000)
            }
        }
    }

    enum Route: RouteType {
        case auth, me
        case posts(for: Date)

        var route: URL.Route {
            switch self {
            case .me: return .init(at: "me")
            case .auth: return .init(at: "auth")
            case let .posts(for: date):
                return URL.Route(at: "posts").query(("date", date), ("userId", "someId"))
            }
        }
    }

    static let current: Environment = .localhost
}

下面是你如何使用它:

Alamofire.request(Router<Api>(at: .me))
Alamofire.request(Router<Api>(.test, at: .auth))
Alamofire.request(Router<Api>(.production, at: .posts(for: Date())))
滕令雪
2023-03-14

下面是Swift3中最新的 ,Alamofire's GitHub推荐使用。我希望您在如何使用 正确实现路由器方面发现它很有用。

import Alamofire

enum Router: URLRequestConvertible
{
    case createUser(parameters: Parameters)
    case readUser(username: String)
    case updateUser(username: String, parameters: Parameters)
    case destroyUser(username: String)

    static let baseURLString = "https://example.com"

    var method: HTTPMethod
    {
        switch self {
        case .createUser:
            return .post
        case .readUser:
            return .get
        case .updateUser:
            return .put
        case .destroyUser:
            return .delete
        }
     }

    var path: String
    {
        switch self {
        case .createUser:
            return "/users"
        case .readUser(let username):
            return "/users/\(username)"
        case .updateUser(let username, _):
            return "/users/\(username)"
        case .destroyUser(let username):
            return "/users/\(username)"
        }
    }

    // MARK: URLRequestConvertible

    func asURLRequest() throws -> URLRequest
    {
        let url = try Router.baseURLString.asURL()

        var urlRequest = URLRequest(url: url.appendingPathComponent(path))
        urlRequest.httpMethod = method.rawValue

        switch self {
        case .createUser(let parameters):
            urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters)
        case .updateUser(_, let parameters):
            urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters)
        default:
            break
        }

        return urlRequest
    }
}
干善
2023-03-14

null

在真实世界的API中,URLRequestConvertible的正确用法是什么?

协议是一种轻量级方法,可以确保给定对象创建有效的 。实际上并没有一套严格的规则或指导方针迫使您以任何特定的方式使用此协议。它只是一个方便的协议,允许其他对象存储正确创建 所需的状态。有关Alamofire的更多信息可以在这里找到。

是否应该为每个端点创建一个路由器?

绝对不能。这将破坏使用 的整个目的。Swift枚举对象非常强大,允许您共享大量公共状态,并打开实际上不同的部分。能够用下面这么简单的东西创建一个 ,真是太强大了!

let URLRequest: NSURLRequest = Router.ReadUser("cnoon")

我想不明白为什么要用枚举来构建路由器?为什么我们不将类与静态方法一起使用?

之所以使用枚举,是因为它是在公共接口下表达多个相关对象的一种更为简洁的方式。所有的方法在所有的案例之间共享。如果您使用静态方法,那么您必须为每个方法的每种情况都有一个静态方法。或者必须在对象内部使用Obj-C样式的枚举。这里有一个简单的例子来说明我的意思。

enum Router: URLRequestConvertible {
    static let baseURLString = "http://example.com"

    case CreateUser([String: AnyObject])
    case ReadUser(String)
    case UpdateUser(String, [String: AnyObject])
    case DestroyUser(String)

    var method: Alamofire.HTTPMethod {
        switch self {
        case .CreateUser:
            return .post
        case .ReadUser:
            return .get
        case .UpdateUser:
            return .put
        case .DestroyUser:
            return .delete
        }
    }

    var path: String {
        switch self {
        case .CreateUser:
            return "/users"
        case .ReadUser(let username):
            return "/users/\(username)"
        case .UpdateUser(let username, _):
            return "/users/\(username)"
        case .DestroyUser(let username):
            return "/users/\(username)"
        }
    }
}

要获得任何不同端点的方法,您可以调用相同的方法,而不必传递任何参数来定义您要查找的端点类型,它已经由您选择的案例处理了。

let createUserMethod = Router.CreateUser.method
let updateUserMethod = Router.UpdateUser.method

或者如果要获取路径,则使用相同类型的调用。

let updateUserPath = Router.UpdateUser.path
let destroyUserPath = Router.DestroyUser.path

现在让我们使用静态方法来尝试相同的方法。

struct Router: URLRequestConvertible {
    static let baseURLString = "http://example.com"

    static var method: Method {
        // how do I pick which endpoint?
    }

    static func methodForEndpoint(endpoint: String) -> Method {
        // but then I have to pass in the endpoint each time
        // what if I use the wrong key?
        // possible solution...use an Obj-C style enum without functions?
        // best solution, merge both concepts and bingo, Swift enums emerge
    }

    static var path: String {
        // bummer...I have the same problem in this method too.
    }

    static func pathForEndpoint(endpoint: String) -> String {
        // I guess I could pass the endpoint key again?
    }

    static var pathForCreateUser: String {
        // I've got it, let's just create individual properties for each type
        return "/create/user/path"
    }

    static var pathForUpdateUser: String {
        // this is going to get really repetitive for each case for each method
        return "/update/user/path"
    }

    // This approach gets sloppy pretty quickly
}

注意:如果没有很多属性或函数可以打开事例,那么枚举并不比结构具有太多优势。这只是一种具有不同句法糖的替代方法。

枚举可以最大化状态和代码重用。相关联的值还允许您做一些非常强大的事情,比如对有些相似,但有着难以置信的不同需求的对象进行分组。。。。例如 创建。

为枚举事例构造参数以提高可读性的正确方法是什么?(不得不把这个捣碎)

这是个很棒的问题。你已经提出了两个可能的选择。让我补充第三条,也许更适合你的需要。

case CreateUser(username: String, firstName: String, lastName: String, email: String)
case ReadUser(username: String)
case UpdateUser(username: String, firstName: String, lastName: String, email: String)
case DestroyUser(username: String)

在有关联值的情况下,我认为为元组中的所有值添加显式名称会很有帮助。这确实有助于构建上下文。缺点是必须在switch语句中重新声明这些值,就像这样。

static var method: String {
    switch self {
    case let CreateUser(username: username, firstName: firstName, lastName: lastName, email: email):
        return "POST"
    default:
        return "GET"
    }
}

虽然这为您提供了一个良好的,一致的上下文,但它变得非常冗长。这是你目前在Swift中的三个选择,哪一个是正确的选择取决于你的用例。

随着Alamofire 4.0的发布, 现在变得更加智能,并且还可以抛出。我们在Alamofire中添加了完全支持,通过响应处理程序来处理无效请求和生成合理的错误。我们的自述文件中详细介绍了这个新系统。

 类似资料:
  • 问题内容: 我试图了解Java 8中API 的方法。 我有简单的逻辑: 但这会导致编译错误: 我当然可以做这样的事情: 但这就像混乱的支票一样。 如果我将代码更改为此: 代码变得越来越脏,这让我想到了回到旧支票。 有任何想法吗? 问题答案: 需要作为参数。您正在向其传递类型为void的表达式。因此,它不会编译。 使用者应被实现为lambda表达式: 甚至更简单,使用方法参考: 这基本上与 想法是仅

  • 问题内容: 我正在尝试了解的语义,以及实体管理器对未保存的瞬态实例的确切含义。我要实现的只是向会话添加一个新的临时实例,并在刷新会话时让Hibernate执行一个。 我发现如果持久保存一个新实例,然后在同一会话中对其进行修改,则实体管理器将同时生成和语句,这可能会导致约束冲突。 例如,假设我有一个带有列 栏 和以下服务方法的实体关系 Foo 。 __ 尽管我们为提供了一个值,但是执行此代码将违反数

  • 问题内容: 什么时候应该从而不是从中导出例外? 不必在方法的子句中声明A ,因为它不一定要专门列出,否则可能是 好 方法,也可能是 不好的, 因为显式声明方法的异常是一种好习惯。 有什么想法吗? 问题答案: 来自未经检查的异常- 争议 : 如果可以合理预期客户端会从异常中恢复,请将其设置为已检查的异常。如果客户端无法采取任何措施来从异常中恢复,请将其设置为未经检查的异常。 请注意,未检查的异常是从

  • 问题内容: 我现在正在开发应用程序,并进行全局切换。我想包装起来以方便使用。 然后,我在Firefox控制台中得到此结果。 如果我想使用被呼叫的行号登录该怎么办? 问题答案: 这是一个古老的问题,提供的所有答案都太过分了,存在跨浏览器的重大问题,并且没有提供任何超级有用的东西。该解决方案可在每种浏览器中使用,并完全按需报告所有控制台数据。无需黑客,只需一行代码即可签出codepen。 像这样创建开

  • 问题内容: 这个想法是使用更少的连接和更好的性能。连接是否随时终止? 对于另一个问题,是否打开新连接? 问题答案: 不,多路复用器不会过期。没有GetDatabase不会打开新连接。basics.md涵盖了所有内容 -特别是: 从GetDatabase返回的对象是便宜的直通对象,不需要存储。

  • 我有一个使用jquery mobile的应用程序,它由几个html页面组成,每个页面中都有几个jquery页面元素。在桌面浏览器上,一切正常,但当我把它加载到我的android设备(运行2.3)上时,第一个页面看起来很好,但只要你点击一个链接(比如从index.html)- 那么,是否有正确的方法在不同的html页面之间移动呢?我没有得到任何浏览器错误,所以一切似乎都工作正常,但没有jqm的样式或