Swift中的HTTP请求体Request Bodies使用示例详解

 更新时间:2023年02月03日 15:20:54   作者:庄周晓梦  
这篇文章主要为大家介绍了Swift中的HTTP请求体Request Bodies使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

正文

在进行HTTPRequest请求发送前,我们稍稍改进一下我们的结构体,最后,我们将会以下面的信息输出:

public struct HTTPRequest {
    private var urlComponents = URLComponents()
    public var method: HTTPMethod = .get
    public var headers: [String: String] = [:]
    public var body: Data?
}

在本节中,我们将着重讨论一下body属性,并对其进行改造。

通用化body

在HTTP简介那一节,我们了解到,一个请求体是原始二进制数据,但是,在与 Web API 通信时,这些数据有多种标准格式,例如 JSON 和表单提交。

我们可以将其概括为一种“给我们数据的东西”的形式,而不是要求此代码的客户手动构造其提交数据的二进制表示。

由于我们不打算对用于构造数据的算法施加任何限制,因此通过协议而不是具体类型来定义此功能是有意义的:

public protocol HTTPBody { }

接下来,我们需要一种方法从其中一个值中获取Data,并在出现问题时选择性地报告错误:

public protocol HTTPBody { 
    func encode() throws -> Data 
}

我们可以在这一点上停下来,但还有另外两条信息值得拥有:

public protocol HTTPBody { 
    var isEmpty: Bool { get }
    var additionalHeaders: [String: String] { get } 
    func encode() throws -> Data 
}

如果我们能快速知道一个body是空的,那么我们就可以省去尝试检索任何编码数据和处理错误或空数据值的麻烦。

此外,某些类型的正文与请求中的header结合使用。 例如,当我们将值编码为 JSON 时,我们希望有一种方法可以自动指定 Content-Type: application/json 的header,而无需在请求中手动指定它。 为此,我们将允许这些类型声明额外的header,这些标头将作为最终请求的一部分结束。 为了进一步简化采用,我们可以为这些提供默认实现:

extension HTTPBody {
    public var isEmpty: Bool { return false }
    public var additionalHeaders: [String: String] { return [:] }
}

最后,我们可以将我们的类型更新到这个新的协议中

public struct HTTPRequest {
    private var urlComponents = URLComponents()
    public var method: HTTPMethod = .get
    public var headers: [String: String] = [:]
    public var body: HTTPBody?
}

空请求体 EmptyBody

最简单的HTTPBody是”无体“。有了这个协议,定义一个空请求体也是很方便的。

public struct EmptyBody: HTTPBody {
    public let isEmpty = true
    public init() { }
    public func encode() throws -> Data { Data() }
}

我们甚至可以将其设置为默认的主体值,从而完全消除对该属性的可选性的需要:

public struct HTTPRequest {
    private var urlComponents = URLComponents()
    public var method: HTTPMethod = .get
    public var headers: [String: String] = [:]
    public var body: HTTPBody = EmptyBody()
}

数据体 DataBody

下一个明显要实现的主体类型是返回给定的任何Data值的主体。 这将用于我们不一定有 HTTPBody 实现但也许我们已经有Data值本身要发送的情况。

具体实现如下:

public struct DataBody: HTTPBody {    
    private let data: Data
    public var isEmpty: Bool { data.isEmpty }
    public var additionalHeaders: [String: String]
    public init(_ data: Data, additionalHeaders: [String: String] = [:]) {
        self.data = data
        self.additionalHeaders = additionalHeaders
    }
    public func encode() throws -> Data { data }    
}

有了这个,我们可以很轻松的将一个Data值封装进HTTPBody里:

let otherData: Data = ...
var request = HTTPRequest()
request.body = DataBody(otherData)

JSON体 JSONBody

在发送网络请求时,将值编码为 JSON 是一项非常常见的任务。 制作一个 HTTPBody 来为我们处理这个现在很容易:

public struct JSONBody: HTTPBody {
    public let isEmpty: Bool = false
    public var additionalHeaders = [
        "Content-Type": "application/json; charset=utf-8"
    ]
    private let encode: () throws -> Data
    public init<T: Encodable>(_ value: T, encoder: JSONEncoder = JSONEncoder()) {
        self.encode = { try encoder.encode(value) }
    }
    public func encode() throws -> Data { return try encode() }
}

首先,我们假设我们得到的任何值都会至少产生一些结果,因为即使是空字符串也会编码为非空 JSON 值。 因此,isEmpty = false

接下来,大多数服务器在接收 JSON 正文时需要 application/jsonContent-Type,因此我们假设这是常见情况,并在 additionalHeaders 中默认该值。 但是,我们会将该属性保留为 var,以防万一出现客户不希望这样的情况。

对于编码,我们需要接受一些通用值(要编码的东西),但最好不要让整个结构对编码类型通用。 我们可以通过将类型的泛型参数限制为初始化器来避免类型的泛型参数,然后在闭包中捕获泛型值。

我们还需要一种方法来提供自定义 JSONEncoder,以便客户有机会摆弄诸如 .keyEncodingStrategy 之类的东西。 但是,我们将提供一个默认编码器来简化使用。

最后,encode() 方法本身只是调用我们创建的闭包,它捕获通用值并通过 JSONEncoder 执行它。

其中一个的使用方法如下:

struct PagingParameters: Encodable {
    let page: Int
    let number: Int
}
let parameters = PagingParameters(page: 0, number: 10)
var request = HTTPRequest()
request.body = JSONBody(parameters)

这样,正文将自动编码为 {"page":0,"number":10},我们的最终请求将具有正确的 Content-Type 标头。

表单 FormBody

我们将在本文中看到的最后一种主体是表示基本表单提交的body。 当我们专门讨论多部分表单上传时,我们将保存文件上传以备将来使用。

表单提交正文最终为粗略的 URL 编码键值对,例如 name=Arthur&age=42

我们将从与我们的 HTTPBody 实现相同的基本结构开始:

public struct FormBody: HTTPBody {
    public var isEmpty: Bool { values.isEmpty }
    public let additionalHeaders = [
        "Content-Type": "application/x-www-form-urlencoded; charset=utf-8"
    ]
    private let values: [URLQueryItem]
    public init(_ values: [URLQueryItem]) {
        self.values = values
    }
    public init(_ values: [String: String]) {
        let queryItems = values.map { URLQueryItem(name: $0.key, value: $0.value) }
        self.init(queryItems)
    }
    public func encode() throws -> Data {
        let pieces = values.map { /* TODO */ }
        let bodyString = pieces.joined(separator: "&")
        return Data(bodyString.utf8)
    }
}

和以前一样,我们有一个自定义的 Content-Type 标头来应用于请求。 我们还公开了几个初始化器,以便客户端可以以对他们有意义的方式描述这些值。 我们还删除了大部分 encode() 方法,省略了 URLQueryItem 值的实际编码。

不幸的是,对名称和值进行编码有点模棱两可。 如果你仔细阅读关于表单提交的古老规范,你会看到提到“换行规范化”和将空格编码为 + 的内容。 我们可以努力挖掘并找出这些东西的含义,但在实践中,Web 服务器往往可以很好地处理任何百分比编码的内容,甚至是空格。 我们将走捷径并假设这是真的。 我们还将全面假设字母数字字符在名称和值中是可以的,并且其他所有内容都应该被编码:

private func urlEncode(_ string: String) -> String {
    let allowedCharacters = CharacterSet.alphanumerics
    return string.addingPercentEncoding(withAllowedCharacters: allowedCharacters) ?? ""
}

使用 = 字符组合名称和值:

private func urlEncode(_ queryItem: URLQueryItem) -> String {
    let name = urlEncode(queryItem.name)
    let value = urlEncode(queryItem.value ?? "")
    return "(name)=(value)"
}

有了这个,我们可以解决 /* TODO */ 评论:

public struct FormBody: HTTPBody {
    public var isEmpty: Bool { values.isEmpty }
    public let additionalHeaders = [
        "Content-Type": "application/x-www-form-urlencoded; charset=utf-8"
    ]
    private let values: [URLQueryItem]
    public init(_ values: [URLQueryItem]) {
        self.values = values
    }
    public init(_ values: [String: String]) {
        let queryItems = values.map { URLQueryItem(name: $0.key, value: $0.value) }
        self.init(queryItems)
    }
    public func encode() throws -> Data {
        let pieces = values.map(self.urlEncode)
        let bodyString = pieces.joined(separator: "&")
        return Data(bodyString.utf8)
    }
    private func urlEncode(_ queryItem: URLQueryItem) -> String {
        let name = urlEncode(queryItem.name)
        let value = urlEncode(queryItem.value ?? "")
        return "(name)=(value)"
    }
    private func urlEncode(_ string: String) -> String {
        let allowedCharacters = CharacterSet.alphanumerics
        return string.addingPercentEncoding(withAllowedCharacters: allowedCharacters) ?? ""
    }
}

和以前一样,使用它变得很简单:

var request = HTTPRequest()
request.body = FormBody(["greeting": "Hello, ", "target": "🌎"])
// the body is encoded as:
// greeting=Hello%2C%20&target=%F0%9F%8C%8E

其他Body Other Bodies

您可以在 HTTP 请求中发送的正文格式多种多样。 我已经提到过,我们将来会更仔细地研究多部分请求,但是这种 HTTPBody 方法几乎适用于您会遇到的每一种请求体。

在下一篇文章中,我们将描述 HTTP 请求加载抽象层并使用 URLSession 实现它。

以上就是Swift中的HTTP请求体Request Bodies使用示例详解的详细内容,更多关于Swift HTTP请求体Request Bodies的资料请关注脚本之家其它相关文章!

相关文章

  • iOS中监听UITextField值改变事件的方法实例

    iOS中监听UITextField值改变事件的方法实例

    UITextField 是一个用来处理文本输入和现实的控件,在我们的开发当中也是经常被用到。下面这篇文章主要给大家介绍了关于iOS中监听UITextField值改变事件的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2018-07-07
  • iOS实现应用内切换语言及字体大小(模仿微信)

    iOS实现应用内切换语言及字体大小(模仿微信)

    这篇文章主要给大家介绍了关于利用iOS如何实现应用内切换语言及字体大小的相关资料,实现的效果类似我们经常在微信中见到的,文中通过示例代码介绍的非常详细,需要的朋友们可以参考借鉴,下面随着小编来一起学习学习吧。
    2018-01-01
  • iOS 基本动画、关键帧动画、利用缓动函数实现物理动画效果

    iOS 基本动画、关键帧动画、利用缓动函数实现物理动画效果

    这篇文章主要介绍了iOS 基本动画、关键帧动画、利用缓动函数实现物理动画效果的相关资料,需要的朋友可以参考下
    2016-10-10
  • iOS开发中音频视频播放的简单实现方法

    iOS开发中音频视频播放的简单实现方法

    视频音频是我们在ios日常开发中经常会遇到的一个需求,所以下面这篇文章主要给大家介绍了关于iOS开发中音频视频播放的简单实现方法,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧。
    2017-10-10
  • iOS中设置网络超时时间+模拟的方法详解

    iOS中设置网络超时时间+模拟的方法详解

    这篇文章主要介绍了在iOS中设置网络超时时间+模拟的方法,文中介绍的非常详细,相信对大家具有一定的参考价值,需要的朋友们下面来跟着小编一起来学习学习吧。
    2017-04-04
  • 简单谈谈c/c++中#import、#include和@class的区别

    简单谈谈c/c++中#import、#include和@class的区别

    对于#import,我想做过iOS开发的人应该都不陌生。在开发过程中,当我们需要声明某一个类时,都需要去引用。而#imclude的话,在我们学习C时就已经知道了,他的作用也是引用声明的意思。在表面上他们的作用似乎都是一样的。但是在具体功能实现方式上,还是有着很大的区别。
    2018-01-01
  • IOS 字符串常用处理详细介绍

    IOS 字符串常用处理详细介绍

    这篇文章主要介绍了IOS 字符串常用处理详细介绍的相关资料,需要的朋友可以参考下
    2017-02-02
  • iOS将相册中图片上传至服务器的方法

    iOS将相册中图片上传至服务器的方法

    这篇文章主要为大家详细介绍了iOS将相册中图片上传至服务器的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05
  • iOS 数据结构之数组的操作方法

    iOS 数据结构之数组的操作方法

    这篇文章主要介绍了iOS 数据结构之数组的操作方法,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧
    2018-07-07
  • iOS项目的开发命名规范教程

    iOS项目的开发命名规范教程

    为了团队各成员之间代码的互通、可读、易维护性,特制订此开发规范。下面这篇文章主要给大家介绍了关于iOS项目的开发命名规范的相关资料,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2017-11-11

最新评论