下载 JSON 数据在 NSHTTPURLResponse 失败

Downloading JSON data fails at NSHTTPURLResponse

我正在尝试使用 Swift 从 URL 和 return 的内容中获取 JSON 文件。但是,代码在以下代码中的 let httpResponse = response as! NSHTTPURLResponse 行失败。我在这一行遇到异常,Xcode 进入调试模式。

class func downloadJSONFile()->AnyObject
    {
        let requestURL: NSURL = NSURL(string: "http://www.learnswiftonline.com/Samples/subway.json")!
        let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
        let session = NSURLSession.sharedSession()
        var json:AnyObject = ""
        let task = session.dataTaskWithRequest(urlRequest) {
            (data, response, error) -> Void in

            let httpResponse = response as! NSHTTPURLResponse
            let statusCode = httpResponse.statusCode

            if (statusCode == 200) {

                do{
                    json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)

                }catch {
                    print("Error with Json: \(error)")

                }

            }

        }

        task.resume()

        return json
    }

我该如何解决这个问题?

有几个问题:

  1. 如果请求中有任何错误,response 将是 nil,因此您尝试强制转换它会导致致命错误。处理网络响应时不要使用强制unwrapping/casting。

  2. 这里有一个更深层次的问题,您正试图从一个异步运行的方法中 return 数据。您应该将您的方法更改为不 return 任何东西,本身,而是提供一个完成处理程序,您可以通过它异步传回相关数据:

     class func downloadJSONFile(completionHandler: @escaping (Any?) -> Void) {
         let requestURL = URL(string: "http://www.learnswiftonline.com/Samples/subway.json")!
         let urlRequest = URLRequest(url: requestURL)
         let session = URLSession.shared
         let task = session.dataTask(with: urlRequest) { data, response, error in
             // check for fundamental networking errors
    
             guard
                 error == nil,
                 let data = data
             else {
                 print(error ?? "Other error")
                 completionHandler(nil)
                 return
             }
    
             guard
                 let httpResponse = response as? HTTPURLResponse,
                 (200 ..< 300) ~= httpResponse.statusCode
             else {
                 print("Invalid status code")
                 completionHandler(nil)
                 return
             }
    
             do {
                 let json = try JSONSerialization.jsonObject(with: data)
                 completionHandler(json)
             } catch let parseError {
                 print("Error parsing: \(parseError)")
                 completionHandler(nil)
             }
         }
    
         task.resume()
     }
    

    然后使用完成处理程序调用它(或使用尾随闭包语法,如下所示):

     APIClass.downloadJSONFile() { json in
         guard json != nil else {
             print("there was some problem")
             return
         }
    
         // now you can use `json` here
    
         dispatch_async(dispatch_get_main_queue()) {
             // and if you're doing any model or UI updates, dispatch that back to the main queue
         }
     }
    
     // but do not use `json` here, as the above runs asynchronously
    
  3. 注意,如果您想将错误信息返回给调用例程,您可以将其更改为也 return 错误信息,例如:

    enum DownloadError: Error {
        case networkError(Error)
        case notHTTPResponse
        case invalidHTTPResponse(Int)
        case parseError(Error)
    }
    
    class func downloadJSONFile(completionHandler: @escaping (Result<Any, Error>) -> Void) {
        let requestURL = URL(string: "http://www.learnswiftonline.com/Samples/subway.json")!
        let urlRequest = URLRequest(url: requestURL)
        let session = URLSession.shared
        let task = session.dataTask(with: urlRequest) { data, response, error in
            if let error = error {
                completionHandler(.failure(DownloadError.networkError(error)))
                return
            }
    
            guard let httpResponse = response as? HTTPURLResponse, let data = data else {
                completionHandler(.failure(DownloadError.notHTTPResponse))
                return
            }
    
            guard 200 ..< 300 ~= httpResponse.statusCode else {
                completionHandler(.failure(DownloadError.invalidHTTPResponse(httpResponse.statusCode)))
                return
            }
    
            do {
                let json = try JSONSerialization.jsonObject(with: data)
                completionHandler(.success(json))
            } catch let parseError {
                completionHandler(.failure(DownloadError.parseError(parseError)))
            }
        }
    
        task.resume()
    }
    

    而且,显然,调用将更改为采用两个参数:

    APIClass.downloadJSONFile() { result in
        switch result {
        case .failure(let error):
            print(error)
    
        case .success(let value):
            // and then it would be like before ...
        }
    }
    
  4. 在 iOS 9 及更高版本中使用 URLSession 时,它将不允许明文请求(即不允许“http”,默认情况下仅允许“https” ).您可以通过将以下内容添加到 info.plist 来强制该应用允许非 https 请求。有关详细信息,请参阅

     <key>NSAppTransportSecurity</key>
     <dict>
         <key>NSExceptionDomains</key>
         <dict>
             <key>learnswiftonline.com</key>
             <dict>
                 <!--Include to allow subdomains-->
                 <key>NSIncludesSubdomains</key>
                 <true/>
                 <!--Include to allow HTTP requests-->
                 <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                 <true/>
                 <!--Include to specify minimum TLS version-->
                 <key>NSTemporaryExceptionMinimumTLSVersion</key>
                 <string>TLSv1.1</string>
             </dict>
         </dict>
     </dict>