簡體   English   中英

HTTPURLResponse allHeaderFields Swift 3 大寫

[英]HTTPURLResponse allHeaderFields Swift 3 Capitalisation

轉換為 Swift 3 我注意到從 HTTPURLResponse 讀取標頭字段時發生了一個奇怪的錯誤。

let id = httpResponse.allHeaderFields["eTag"] as? String

不再工作。

我打印了所有標題字典,所有標題鍵似乎都在 Sentence 大小寫中。

根據查爾斯代理的說法,我所有的標題都是小寫的。 根據后端團隊的說法,在他們的代碼中,標題在 Title-Case 中。 根據文檔:標題應該不區分大小寫。

所以我不知道該相信哪個。 有沒有其他人在 Swift 3 中發現他們的標題現在被 iOS 變成了 Sentence 案例? 如果是這樣,我們想要這種行為嗎?

我應該用 Apple 記錄一個錯誤,還是應該在 HTTPURLResponse 上創建一個類別,以允許自己不區分大小寫地查找標頭值。

更新:這是一個已知問題


allHeaderFields應該返回一個不區分大小寫的字典,因為這是 HTTP 規范所要求的。 看起來像是 Swift 錯誤,我會在 .

下面是一些簡單地重現問題的示例代碼:

let headerFields = ["ETag" : "12345678"]
let url = URL(string: "http://www.example.com")!
let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: headerFields)!

response.allHeaderFields["eTaG"] // nil (incorrect)
headerFields["eTaG"] // nil (correct)

(改編自Cédric Luthi 的 Gist 。)

基於@Darko 的回答,我制作了一個Swift 3擴展,它可以讓您找到任何不區分大小寫的標題:

import Foundation


extension HTTPURLResponse {

    func find(header: String) -> String? {
        let keyValues = allHeaderFields.map { (String(describing: $0.key).lowercased(), String(describing: $0.value)) }

        if let headerValue = keyValues.filter({ $0.0 == header.lowercased() }).first {
            return headerValue.1
        }
        return nil
    }

}

更有效的解決方法:

(response.allHeaderFields as NSDictionary)["etag"]

作為 Swift 3 的修補程序,您可以執行以下操作:

// Converting to an array of tuples of type (String, String)
// The key is lowercased()
let keyValues = response.allHeaderFields.map { (String(describing: $0.key).lowercased(), String(describing: $0.value)) } 

// Now filter the array, searching for your header-key, also lowercased
if let myHeaderValue = keyValues.filter({ $0.0 == "X-MyHeaderKey".lowercased() }).first {
    print(myHeaderValue.1)
}

更新:這個問題似乎仍然沒有在 Swift 4 中得到解決。

我點擊了這個並使用Dictionary上的擴展來解決它來創建自定義下標。

extension Dictionary {
    subscript(key: String) -> Value? {
        get {
            let anyKey = key as! Key
            if let value = self[anyKey] {
                return value // 1213ns
            }
            if let value = self[key.lowercased() as! Key] {
                return value // 2511ns
            }
            if let value = self[key.capitalized as! Key] {
                return value // 8928ns
            }
            for (storedKey, storedValue) in self {
                if let stringKey = storedKey as? String {
                    if stringKey.caseInsensitiveCompare(key) == .orderedSame {
                        return storedValue // 22317ns
                    }
                }
            }

            return nil
        }
        set {
            self[key] = newValue
        }
    }
}

評論中的時間來自對不同場景的基准測試(優化構建, -Os ,平均超過 1,000,000 次迭代)。 標准字典的等效訪問時間為 1257ns。 必須進行兩次檢查才能有效地增加一倍,2412ns。

在我的特定情況下,我看到從服務器返回的標頭是駝峰式或小寫,具體取決於我連接的網絡(其他需要調查的情況)。 這樣做的好處是,如果它得到修復,我可以刪除擴展名而無需更改其他任何內容。 此外,使用該代碼的任何其他人都不需要記住任何解決方法 - 他們可以免費獲得這個。

我檢查並沒有看到ETagHTTPURLResponse修改 - 如果我傳遞了它ETagEtag我在allHeaderFields得到了那些。 如果性能是一個問題,並且您遇到此問題,您可以創建第二個下標,該下標采用包含數組的Hashable結構。 然后將它傳遞給字典,並帶有您要處理的標簽。

struct DictionaryKey: Hashable {
    let keys: [String]
    var hashValue: Int { return 0 } // Don't care what is returned, not going to use it
}
func ==(lhs: DictionaryKey, rhs: DictionaryKey) -> Bool {
    return lhs.keys == rhs.keys // Just filling expectations
}

extension Dictionary {
    subscript(key: DictionaryKey) -> Value? {
        get {
            for string in key.keys {
                if let value = self[string as! Key] {
                    return value
                }
            }

            return nil
        }
    }
}

print("\(allHeaderFields[DictionaryKey(keys: ["ETag", "Etag"])])"

正如您所期望的,這幾乎等同於進行單獨的字典查找。

由於Swift 中錯誤iOS 13 中的新解決方案,我做了一個擴展:

這是gist鏈接

public extension HTTPURLResponse {
    func valueForHeaderField(_ headerField: String) -> String? {
        if #available(iOS 13.0, *) {
            return value(forHTTPHeaderField: headerField)
        } else {
            return (allHeaderFields as NSDictionary)[headerField] as? String
        }
    }
}

對於 swift 3.0,有一個比 Darko 短一點的版本。

由於 iOS8 和 iOS10 中的標題名稱大小寫可能不同,因此最好的方法是使用不區分大小寫的比較。

response.allHeaderFields.keys.contains(where: {$0.description.caseInsensitiveCompare("CaSe-InSeNsItIvE-HeAdEr") == .orderedSame})

所以現在支持所有類型:

  • 不區分大小寫的標頭
  • 不區分大小寫的標題
  • 不區分大小寫的標題

這是我的。 我沒有弄亂字典的工作方式,而是在NSHTTPURLResponseNSHTTPURLResponse了一個 obj-c 類別。 Obj-C allHeaderFields字典仍然不區分大小寫。

@import Foundation;

@implementation NSHTTPURLResponse (CaseInsensitive)

- (nullable NSString *)allHeaderFieldsValueForCaseInsensitiveKey:(nonnull NSString *)key {
    NSString *value = self.allHeaderFields[key];
    if ([value isKindOfClass:[NSString class]]) {
        return value;
    } else {
        return nil;
    }

}

@end

對於簡單的案例使用

if let ix = headers.index(where: {$0.key.caseInsensitiveCompare("eTag") == .orderedSame}){
    let tag = headers[ix].value
}

swift 4.1它對我有用。

if let headers = httpResponse.allHeaderFields as? [String: String], let value = headers["key"] {
                print("value: \(value)")
            }

如果不能像下面那樣工作,您也可以使用.lowercased().capitalized值(根據您的情況)

httpResponse.allHeaderFields["eTag".capitalized] as? String
httpResponse.allHeaderFields["eTag". lowercased()] as? String

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM