【问题标题】:How do I make an HTTP request in Swift?如何在 Swift 中发出 HTTP 请求?
【发布时间】:2021-10-26 03:26:00
【问题描述】:

我在 iBooks 中阅读了 Apple 的 The Programming Language Swift,但不知道如何在 Swift 中发出 HTTP 请求(类似于 cURL)。我需要导入 Obj-C 类还是只需要导入默认库?还是不能基于原生 Swift 代码发起 HTTP 请求?

【问题讨论】:

标签: ios objective-c swift iphone nsurlsession


【解决方案1】:

您可以像在 Objective-C 中一样使用 URLURLRequestURLSessionNSURLConnection。请注意,对于 iOS 7.0 及更高版本,URLSession 是首选。

使用URLSession

URLSession 初始化一个URL 对象和一个URLSessionDataTask。然后使用resume() 运行任务。

let url = URL(string: "http://www.stackoverflow.com")!

let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
    guard let data = data else { return }
    print(String(data: data, encoding: .utf8)!)
}

task.resume()

使用NSURLConnection

首先,初始化一个URL和一个URLRequest

let url = URL(string: "http://www.stackoverflow.com")!
var request = URLRequest(url: url)
request.httpMethod = "POST" 

然后,您可以通过以下方式异步加载请求:

NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue.main) {(response, data, error) in
    guard let data = data else { return }
    print(String(data: data, encoding: .utf8)!)
}

或者你可以初始化一个NSURLConnection:

let connection = NSURLConnection(request: request, delegate:nil, startImmediately: true)

只需确保将您的委托设置为 nil 以外的其他值,并使用委托方法处理收到的响应和数据。

更多详情,请查看documentation for the NSURLConnectionDataDelegate protocol

在 Xcode 操场上进行测试

如果您想在 Xcode 操场上试用此代码,请将 import PlaygroundSupport 添加到您的操场,以及以下调用:

PlaygroundPage.current.needsIndefiniteExecution = true

这将允许您在 Playground 中使用异步代码。

【讨论】:

  • 不要在操场上浪费时间在 iOS 8 beta 2 上,因为 XCPlayground 不受支持。如果你想尝试破解,有一个聪明而丑陋的等待方式:stackoverflow.com/a/24058337/700206
  • 如何使用 NSURLSession 发送 POST 数据?
  • 这个例子在 6.1 失败:test.swift:5:57: error: value of optional type 'NSURL?'未拆封;你的意思是用'!'要么 '?'? let task = NSURLSession.sharedSession().dataTaskWithURL(url) {(data, response, error) in
  • 添加感叹号修复@mcuadros的错误信息... .dataTaskWithURL(url!)
  • 你能修复一下代码 sn-ps 吗?以下是 Swift 4 print(String(data: data!, encoding: String.Encoding.utf8)) 的工作原理
【解决方案2】:

检查以下代码:

1.同步请求

Swift 1.2

    let urlPath: String = "YOUR_URL_HERE"
    var url: NSURL = NSURL(string: urlPath)!
    var request1: NSURLRequest = NSURLRequest(URL: url)
    var response: AutoreleasingUnsafeMutablePointer<NSURLResponse?>=nil
    var dataVal: NSData =  NSURLConnection.sendSynchronousRequest(request1, returningResponse: response, error:nil)!
    var err: NSError
    println(response)
    var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(dataVal, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary
    println("Synchronous\(jsonResult)")

Swift 2.0 +

let urlPath: String = "YOUR_URL_HERE"
    let url: NSURL = NSURL(string: urlPath)!
    let request1: NSURLRequest = NSURLRequest(URL: url)
    let response: AutoreleasingUnsafeMutablePointer<NSURLResponse?>=nil


    do{

        let dataVal = try NSURLConnection.sendSynchronousRequest(request1, returningResponse: response)

            print(response)
            do {
                if let jsonResult = try NSJSONSerialization.JSONObjectWithData(dataVal, options: []) as? NSDictionary {
                    print("Synchronous\(jsonResult)")
                }
            } catch let error as NSError {
                print(error.localizedDescription)
            }



    }catch let error as NSError
    {
         print(error.localizedDescription)
    }

2。异步请求

Swift 1.2

let urlPath: String = "YOUR_URL_HERE"
    var url: NSURL = NSURL(string: urlPath)!
    var request1: NSURLRequest = NSURLRequest(URL: url)
    let queue:NSOperationQueue = NSOperationQueue()
    NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
        var err: NSError
        var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
        println("Asynchronous\(jsonResult)")
       })

Swift 2.0 +

let urlPath: String = "YOUR_URL_HERE"
    let url: NSURL = NSURL(string: urlPath)!
    let request1: NSURLRequest = NSURLRequest(URL: url)
    let queue:NSOperationQueue = NSOperationQueue()

    NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in

        do {
            if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
                print("ASynchronous\(jsonResult)")
            }
        } catch let error as NSError {
            print(error.localizedDescription)
        }


    })

3。照常URL连接

Swift 1.2

    var dataVal = NSMutableData()
    let urlPath: String = "YOUR URL HERE"
    var url: NSURL = NSURL(string: urlPath)!
    var request: NSURLRequest = NSURLRequest(URL: url)
    var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: true)!
    connection.start()

然后

 func connection(connection: NSURLConnection!, didReceiveData data: NSData!){
    self.dataVal?.appendData(data)
}


func connectionDidFinishLoading(connection: NSURLConnection!)
{
    var error: NSErrorPointer=nil

    var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(dataVal!, options: NSJSONReadingOptions.MutableContainers, error: error) as NSDictionary

    println(jsonResult)



}

Swift 2.0 +

   var dataVal = NSMutableData()
    let urlPath: String = "YOUR URL HERE"
    var url: NSURL = NSURL(string: urlPath)!
    var request: NSURLRequest = NSURLRequest(URL: url)
    var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: true)!
    connection.start()

然后

func connection(connection: NSURLConnection!, didReceiveData data: NSData!){
    dataVal.appendData(data)
}


func connectionDidFinishLoading(connection: NSURLConnection!)
{

    do {
        if let jsonResult = try NSJSONSerialization.JSONObjectWithData(dataVal, options: []) as? NSDictionary {
            print(jsonResult)
        }
    } catch let error as NSError {
        print(error.localizedDescription)
    }

}

4.异步 POST 请求

Swift 1.2

    let urlPath: String = "YOUR URL HERE"
    var url: NSURL = NSURL(string: urlPath)!
    var request1: NSMutableURLRequest = NSMutableURLRequest(URL: url)

    request1.HTTPMethod = "POST"
     var stringPost="deviceToken=123456" // Key and Value

    let data = stringPost.dataUsingEncoding(NSUTF8StringEncoding)

    request1.timeoutInterval = 60
    request1.HTTPBody=data
    request1.HTTPShouldHandleCookies=false

    let queue:NSOperationQueue = NSOperationQueue()

     NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in


        var err: NSError

        var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
        println("AsSynchronous\(jsonResult)")


        })

Swift 2.0 +

let urlPath: String = "YOUR URL HERE"
    let url: NSURL = NSURL(string: urlPath)!
    let request1: NSMutableURLRequest = NSMutableURLRequest(URL: url)

    request1.HTTPMethod = "POST"
    let stringPost="deviceToken=123456" // Key and Value

    let data = stringPost.dataUsingEncoding(NSUTF8StringEncoding)

    request1.timeoutInterval = 60
    request1.HTTPBody=data
    request1.HTTPShouldHandleCookies=false

    let queue:NSOperationQueue = NSOperationQueue()

    NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in

        do {
            if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
                print("ASynchronous\(jsonResult)")
            }
        } catch let error as NSError {
            print(error.localizedDescription)
        }


    })

5.异步 GET 请求

Swift 1.2

    let urlPath: String = "YOUR URL HERE"
    var url: NSURL = NSURL(string: urlPath)!
    var request1: NSMutableURLRequest = NSMutableURLRequest(URL: url)

    request1.HTTPMethod = "GET"
    request1.timeoutInterval = 60
    let queue:NSOperationQueue = NSOperationQueue()

     NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in


        var err: NSError

        var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
        println("AsSynchronous\(jsonResult)")


        })

Swift 2.0 +

let urlPath: String = "YOUR URL HERE"
    let url: NSURL = NSURL(string: urlPath)!
    let request1: NSMutableURLRequest = NSMutableURLRequest(URL: url)

    request1.HTTPMethod = "GET"
    let queue:NSOperationQueue = NSOperationQueue()

    NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in

        do {
            if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
                print("ASynchronous\(jsonResult)")
            }
        } catch let error as NSError {
            print(error.localizedDescription)
        }


    })

6.图片(文件)上传

Swift 2.0 +

  let mainURL = "YOUR_URL_HERE"

    let url = NSURL(string: mainURL)
    let request = NSMutableURLRequest(URL: url!)
    let boundary = "78876565564454554547676"
    request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")


    request.HTTPMethod = "POST" // POST OR PUT What you want
    let session = NSURLSession(configuration:NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: nil, delegateQueue: nil)

    let imageData = UIImageJPEGRepresentation(UIImage(named: "Test.jpeg")!, 1)





    var body = NSMutableData()

    body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

    // Append your parameters

    body.appendData("Content-Disposition: form-data; name=\"name\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("PREMKUMAR\r\n".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
    body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

    body.appendData("Content-Disposition: form-data; name=\"description\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("IOS_DEVELOPER\r\n".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
    body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)


    // Append your Image/File Data

    var imageNameval = "HELLO.jpg"

    body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("Content-Disposition: form-data; name=\"profile_photo\"; filename=\"\(imageNameval)\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData("Content-Type: image/jpeg\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData(imageData!)
    body.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

    body.appendData("--\(boundary)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)

    request.HTTPBody = body




    let dataTask = session.dataTaskWithRequest(request) { (data, response, error) -> Void in

        if error != nil {

            //handle error


        }
        else {




            let outputString : NSString = NSString(data:data!, encoding:NSUTF8StringEncoding)!
            print("Response:\(outputString)")


        }
    }
    dataTask.resume()

【讨论】:

    【解决方案3】:

    另一个选项是 Alamofire 库,它提供可链接的请求/响应方法。

    https://github.com/Alamofire/Alamofire

    提出请求

    import Alamofire
    
    Alamofire.request(.GET, "http://httpbin.org/get")
    

    响应处理

    Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
             .response { request, response, data, error in
                  print(request)
                  print(response)
                  print(error)
              }
    

    【讨论】:

    • 我认为通过建议外部库来回复,而功能是由框架提供的,这不是正确的做法。
    • @turkishweb 是对的。 Cezar 的答案更完整,不使用外部库。这应该是公认的答案。
    • Cezar 的答案已经过时(从 Swift 3 开始),Alamofire 的界面比URLSession 和配偶的界面要好得多。此外,接受度不是衡量某种抽象的善的衡量标准(更不用说你的了!;)),而是衡量对 OP 帮助最大的因素。 (对于“纯”解决方案,有 SO 文档!)如果这是这个答案,那就完美了——它没有使用第一原则,但这并不总是“正确”的解决方案,不是吗?
    • 请不要提供第三方库,除非根据 SO 指南明确要求
    • @AlecO 你能指点我写这些指南吗?
    【解决方案4】:

    Swift 4 及更高版本:使用 URLSession API 的数据请求

       //create the url with NSURL
       let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")! //change the url
    
       //create the session object
       let session = URLSession.shared
    
       //now create the URLRequest object using the url object
       let request = URLRequest(url: url)
    
       //create dataTask using the session object to send data to the server
       let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
    
           guard error == nil else {
               return
           }
    
           guard let data = data else {
               return
           }
    
          do {
             //create json object from data
             if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
                print(json)
             }
          } catch let error {
            print(error.localizedDescription)
          }
       })
    
       task.resume()
    

    Swift 4 及更高版本,可解码和结果枚举

    //APPError enum which shows all possible errors
    enum APPError: Error {
        case networkError(Error)
        case dataNotFound
        case jsonParsingError(Error)
        case invalidStatusCode(Int)
    }
    
    //Result enum to show success or failure
    enum Result<T> {
        case success(T)
        case failure(APPError)
    }
    
    //dataRequest which sends request to given URL and convert to Decodable Object
    func dataRequest<T: Decodable>(with url: String, objectType: T.Type, completion: @escaping (Result<T>) -> Void) {
    
        //create the url with NSURL
        let dataURL = URL(string: url)! //change the url
    
        //create the session object
        let session = URLSession.shared
    
        //now create the URLRequest object using the url object
        let request = URLRequest(url: dataURL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 60)
    
        //create dataTask using the session object to send data to the server
        let task = session.dataTask(with: request, completionHandler: { data, response, error in
    
            guard error == nil else {
                completion(Result.failure(AppError.networkError(error!)))
                return
            }
    
            guard let data = data else {
                completion(Result.failure(APPError.dataNotFound))
                return
            }
    
            do {
                //create decodable object from data
                let decodedObject = try JSONDecoder().decode(objectType.self, from: data)
                completion(Result.success(decodedObject))
            } catch let error {
                completion(Result.failure(APPError.jsonParsingError(error as! DecodingError)))
            }
        })
    
        task.resume()
    }
    

    示例:

    //如果我们想从占位符 API 中获取 todo,那么我们定义 ToDo 结构并调用 dataRequest 并传递“https://jsonplaceholder.typicode.com/todos/1”字符串 url。

    struct ToDo: Decodable {
        let id: Int
        let userId: Int
        let title: String
        let completed: Bool
    
    }
    
    dataRequest(with: "https://jsonplaceholder.typicode.com/todos/1", objectType: ToDo.self) { (result: Result) in
        switch result {
        case .success(let object):
            print(object)
        case .failure(let error):
            print(error)
        }
    }
    

    //这会打印结果:

    ToDo(id: 1, userId: 1, title: "delectus aut autem", completed: false)
    

    【讨论】:

    • 如何调整它以接受对象数组?
    • @kirqe 如果您想接受数组作为响应,则在此处传递 `objectType: [T].self` T 确认可解码协议
    【解决方案5】:

    基本的 Swift 3+ 解决方案

    guard let url = URL(string: "http://www.stackoverflow.com") else { return }
    
    let task = URLSession.shared.dataTask(with: url) { data, response, error in
    
      guard let data = data, error == nil else { return }
    
      print(NSString(data: data, encoding: String.Encoding.utf8.rawValue))
    }
    
    task.resume()
    

    【讨论】:

    • 您的示例适用于 Swift 3,因此您应该使用 URL(string: "http://www.stackoverflow.com") 而不是 NSURL
    • 从 URL 投射到 URL 没有意义 URLSession.shared.dataTask(with: url!)
    • 我收到此错误:App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
    • @YumYumYum 你有没有按照这篇文章配置应用传输安全性stackoverflow.com/questions/30731785/…
    • Leo Dabus 变量 url 是 URL 类型?
    【解决方案6】:

    Swift 3.0

    通过一个小的抽象 https://github.com/daltoniam/swiftHTTP

    例子

        do {
            let opt = try HTTP.GET("https://google.com")
            opt.start { response in
                if let err = response.error {
                    print("error: \(err.localizedDescription)")
                    return //also notify app of failure as needed
                }
                print("opt finished: \(response.description)")
                //print("data is: \(response.data)") access the response of the data with response.data
            }
        } catch let error {
            print("got an error creating the request: \(error)")
        }
    

    【讨论】:

    • 您是否找到了一种方法来等待请求完成后再显示视图? @AlexanderN
    【解决方案7】:

    使用 URLSession + Swift 5

    只需添加到cezar's answer,如果你想使用苹果的 URLSession 类发出 web 请求,有多种方法可以完成任务

    1. 带有 URL 的简单 GET 请求
    2. 带有 URL 和参数的简单 GET 请求
    3. 带有错误处理的 URL 的简单 GET 请求
    4. 带有 URL 的简单 POST 请求,带有错误处理的参数

    1.带有 URL 的简单 GET 请求

    func simpleGetUrlRequest()
        {
            let url = URL(string: "https://httpbin.org/get")!
    
            let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
                guard let data = data else { return }
                print("The response is : ",String(data: data, encoding: .utf8)!)
                //print(NSString(data: data, encoding: String.Encoding.utf8.rawValue) as Any)
            }
            task.resume()
        }
    

    注意确保您必须在 pList 中为 http 请求添加“NSAppTransportSecurity”键

    <key>NSAppTransportSecurity</key>
    <dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
    </dict>
    

    2。带有 URL 和参数的简单 GET 请求

    func simpleGetUrlWithParamRequest()
        {
            let url = URL(string: "https://www.google.com/search?q=peace")!
            
            let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
                
                if error != nil || data == nil {
                    print("Client error!")
                    return
                }
                guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
                    print("Server error!")
                    return
                }
                print("The Response is : ",response)
            }
            task.resume()
        }
    

    3.带有错误处理的 URL 的简单 GET 请求

    func simpleGetUrlRequestWithErrorHandling()
        {
            let session = URLSession.shared
            let url = URL(string: "https://httpbin.org/get")!
            
            let task = session.dataTask(with: url) { data, response, error in
                
                if error != nil || data == nil {
                    print("Client error!")
                    return
                }
                
                guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
                    print("Server error!")
                    return
                }
                
                guard let mime = response.mimeType, mime == "application/json" else {
                    print("Wrong MIME type!")
                    return
                }
                
                do {
                    let json = try JSONSerialization.jsonObject(with: data!, options: [])
                    print("The Response is : ",json)
                } catch {
                    print("JSON error: \(error.localizedDescription)")
                }
                
            }
            task.resume()
        }
    

    4.带有 URL 的简单 POST 请求,带有错误处理的参数。

    func simplePostRequestWithParamsAndErrorHandling(){
            let configuration = URLSessionConfiguration.default
            configuration.timeoutIntervalForRequest = 30
            configuration.timeoutIntervalForResource = 30
            let session = URLSession(configuration: configuration)
            
            let url = URL(string: "https://httpbin.org/post")!
            
            var request = URLRequest(url: url)
            request.httpMethod = "POST"
            request.setValue("application/json", forHTTPHeaderField: "Content-Type")
            request.addValue("application/json", forHTTPHeaderField: "Accept")
            
            let parameters = ["username": "foo", "password": "123456"]
            
            do {
                request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
            } catch let error {
                print(error.localizedDescription)
            }
            
            let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
                
                if error != nil || data == nil {
                    print("Client error!")
                    return
                }
                
                guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
                    print("Oops!! there is server error!")
                    return
                }
                
                guard let mime = response.mimeType, mime == "application/json" else {
                    print("response is not json")
                    return
                }
                
                do {
                    let json = try JSONSerialization.jsonObject(with: data!, options: [])
                    print("The Response is : ",json)
                } catch {
                    print("JSON error: \(error.localizedDescription)")
                }
                
            })
            
            task.resume()
        }
    

    感谢您的建议!

    【讨论】:

    • 为什么不使用var session = URLSession.shared,而只是覆盖变量呢?需要吗?
    【解决方案8】:

    详情

    • Xcode 9.2,Swift 4
    • Xcode 10.2.1 (10E1001)、Swift 5

    Info.plist

    NSAppTransportSecurity

    添加到信息列表:

    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
    

    Alamofire 示例

    Alamofire

    import Alamofire
    
    class AlamofireDataManager {
        fileprivate let queue: DispatchQueue
        init(queue: DispatchQueue) { self.queue = queue }
    
        private func createError(message: String, code: Int) -> Error {
            return NSError(domain: "dataManager", code: code, userInfo: ["message": message ])
        }
    
        private func make(session: URLSession = URLSession.shared, request: URLRequest, closure: ((Result<[String: Any]>) -> Void)?) {
            Alamofire.request(request).responseJSON { response in
                let complete: (Result<[String: Any]>) ->() = { result in DispatchQueue.main.async { closure?(result) } }
                switch response.result {
                    case .success(let value): complete(.success(value as! [String: Any]))
                    case .failure(let error): complete(.failure(error))
                }
            }
        }
    
        func searchRequest(term: String, closure: ((Result<[String: Any]>) -> Void)?) {
            guard let url = URL(string: "https://itunes.apple.com/search?term=\(term.replacingOccurrences(of: " ", with: "+"))") else { return }
            let request = URLRequest(url: url)
            make(request: request) { response in closure?(response) }
        }
    }
    

    Alamofire 示例的使用

    private lazy var alamofireDataManager = AlamofireDataManager(queue: DispatchQueue(label: "DataManager.queue", qos: .utility))
    //.........
    
    alamofireDataManager.searchRequest(term: "jack johnson") { result in
          print(result.value ?? "no data")
          print(result.error ?? "no error")
    }
    

    URLSession 示例

    import Foundation
    
    class DataManager {
    
        fileprivate let queue: DispatchQueue
            init(queue: DispatchQueue) { self.queue = queue }
    
        private func createError(message: String, code: Int) -> Error {
            return NSError(domain: "dataManager", code: code, userInfo: ["message": message ])
        }
    
        private func make(session: URLSession = URLSession.shared, request: URLRequest, closure: ((_ json: [String: Any]?, _ error: Error?)->Void)?) {
            let task = session.dataTask(with: request) { [weak self] data, response, error in
                self?.queue.async {
                    let complete: (_ json: [String: Any]?, _ error: Error?) ->() = { json, error in DispatchQueue.main.async { closure?(json, error) } }
    
                    guard let self = self, error == nil else { complete(nil, error); return }
                    guard let data = data else { complete(nil, self.createError(message: "No data", code: 999)); return }
    
                    do {
                        if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
                            complete(json, nil)
                        }
                    } catch let error { complete(nil, error); return }
                }
            }
    
            task.resume()
        }
    
        func searchRequest(term: String, closure: ((_ json: [String: Any]?, _ error: Error?)->Void)?) {
            let url = URL(string: "https://itunes.apple.com/search?term=\(term.replacingOccurrences(of: " ", with: "+"))")
            let request = URLRequest(url: url!)
            make(request: request) { json, error in closure?(json, error) }
        }
    }
    

    URLSession 示例的使用

    private lazy var dataManager = DataManager(queue: DispatchQueue(label: "DataManager.queue", qos: .utility))
    // .......
    dataManager.searchRequest(term: "jack johnson") { json, error  in
          print(error ?? "nil")
          print(json ?? "nil")
          print("Update views")
    }
    

    结果

    【讨论】:

    • 搜索请求?学期?他们来自哪里?
    • 这是请求的完整示例。 class func searchRequest(term: String, ... 是从 itunes.apple.com 下载数据的样本。此示例将搜索,因此,我选择/发明了 searchRequest 函数名称。 term 是“itunes.apple.com/search?”的一部分要求。您可以选择任何描述逻辑模型的函数名称。
    • 不错的解决方案。我已更改 closure 以返回包含 successerror 的枚举。
    【解决方案9】:

    我已经使用 JSON Parsing 完成了 HTTP 请求 Both 方法 GETPOST > 这样:

    关于 viewDidLoad()

    override func viewDidLoad() {
    super.viewDidLoad()
    
        makeGetRequest()
        makePostRequest()
    
    }
    
    func makePostRequest(){
    
        let urlPath: String = "http://www.swiftdeveloperblog.com/http-post-example-script/"
        var url: NSURL = NSURL(string: urlPath)!
        var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
    
        request.HTTPMethod = "POST"
        var stringPost="firstName=James&lastName=Bond" // Key and Value
    
        let data = stringPost.dataUsingEncoding(NSUTF8StringEncoding)
    
        request.timeoutInterval = 60
        request.HTTPBody=data
        request.HTTPShouldHandleCookies=false
    
        let queue:NSOperationQueue = NSOperationQueue()
    
        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in
            var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
            let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: error) as? NSDictionary
    
             if (jsonResult != nil) {
                // Success
               println(jsonResult)
    
               let message = jsonResult["Message"] as! NSString
    
               println(message)
             }else {
                // Failed
                println("Failed")
            }
    
        })
    
    }
    
    func makeGetRequest(){
        var url : String = "http://api.androidhive.info/contacts/"
        var request : NSMutableURLRequest = NSMutableURLRequest()
        request.URL = NSURL(string: url)
        request.HTTPMethod = "GET"
        request.timeoutInterval = 60
    
        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in
            var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
            let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: error) as? NSDictionary
    
            if (jsonResult != nil) {
                // Success
                println(jsonResult)
    
                let dataArray = jsonResult["contacts"] as! NSArray;
    
                for item in dataArray { // loop through data items
    
                    let obj = item as! NSDictionary
    
                    for (key, value) in obj {
    
                        println("Key: \(key) - Value: \(value)")
    
                        let phone = obj["phone"] as! NSDictionary;
    
                        let mobile = phone["mobile"] as! NSString
                        println(mobile)
                        let home = phone["home"] as! NSString
                        println(home)
                        let office = phone["office"] as! NSString
                        println(office)
                    }
                }
    
            } else {
                // Failed
                println("Failed")
            }
    
        })
    }
    

    完成

    【讨论】:

    • 无法使用类型为“(NSMutableURLRequest, queue: NSOperationQueue, completionHandler: (NSURLResponse!, NSData!, NSError!) -> Void)'的参数列表调用'sendAsynchronousRequest'
    【解决方案10】:

    您可以使用Just,一个python-requests 样式的HTTP 库。

    使用 Just 发送 HTTP 请求的一些示例:

    // synchronous GET request with URL query a=1
    let r = Just.get("https://httpbin.org/get", params:["a":1])
    
    // asynchronous POST request with form value and file uploads
    Just.post(
        "http://justiceleauge.org/member/register",
        data: ["username": "barryallen", "password":"ReverseF1ashSucks"],
        files: ["profile_photo": .URL(fileURLWithPath:"flash.jpeg", nil)]
    ) { (r)
        if (r.ok) { /* success! */ }
    }
    

    在这两种情况下,r 请求的结果都可以通过类似于python-request 的方式访问:

    r.ok            // is the response successful?
    r.statusCode    // status code of response
    r.content       // response body as NSData?
    r.text          // response body as text?
    r.json          // response body parsed by NSJSONSerielization
    

    您可以在playground 中找到更多示例

    在 Playground 中以同步模式使用这个库是在 Swift 中最接近 cURL 的东西。

    【讨论】:

    • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。
    • 这个库似乎已被弃用。没有 Swift 4 支持,也没有人回应未解决的问题。
    【解决方案11】:

    我在登录按钮点击时调用 json

    @IBAction func loginClicked(sender : AnyObject) {
    
        var request = NSMutableURLRequest(URL: NSURL(string: kLoginURL)) // Here, kLogin contains the Login API.
    
        var session = NSURLSession.sharedSession()
    
        request.HTTPMethod = "POST"
    
        var err: NSError?
        request.HTTPBody = NSJSONSerialization.dataWithJSONObject(self.criteriaDic(), options: nil, error: &err) // This Line fills the web service with required parameters.
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json", forHTTPHeaderField: "Accept")
    
        var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
            var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
            var err1: NSError?
            var json2 = NSJSONSerialization.JSONObjectWithData(strData.dataUsingEncoding(NSUTF8StringEncoding), options: .MutableLeaves, error:&err1 ) as NSDictionary
    
            println("json2 :\(json2)")
    
            if(err) {
                println(err!.localizedDescription)
            }
            else {
                var success = json2["success"] as? Int
                println("Success: \(success)")
            }
        })
    
        task.resume()
    }
    

    在这里,我为参数制作了一个单独的字典。

    var params = ["format":"json", "MobileType":"IOS","MIN":"f8d16d98ad12acdbbe1de647414495ec","UserName":emailTxtField.text,"PWD":passwordTxtField.text,"SigninVia":"SH"]as NSDictionary
        return params
    }
    
    // You can add your own sets of parameter here.
    

    【讨论】:

      【解决方案12】:

      在 Swift 4.1 和 Xcode 9.4.1 中。

      JSON POST 方法示例。要检查互联网连接,请添加来自https://developer.apple.com/library/archive/samplecode/Reachability/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007324-Intro-DontLinkElementID_2 的 Reachability.h 和 .m 文件

      func yourFunctionName {
          //Check internet connection
          let networkReachability = Reachability.forInternetConnection()
          let networkStatus:Int = (networkReachability?.currentReachabilityStatus())!.rawValue
          print(networkStatus)
          if networkStatus == NotReachable.rawValue {
              let msg = SharedClass.sharedInstance.noNetMsg//Message
              //Call alert from shared class
              SharedClass.sharedInstance.alert(view: self, title: "", message: msg)
          } else {
              //Call spinner from shared class
              SharedClass.sharedInstance.activityIndicator(view: self.view)//Play spinner
      
              let parameters = "Your parameters here"
              var request = URLRequest(url: URL(string: url)!)
      
              request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
              request.httpMethod = "POST"
      
              print("URL : \(request)")
      
              request.httpBody = parameters.data(using: .utf8)
      
              let task = URLSession.shared.dataTask(with: request) { data, response, error in guard let data = data, error == nil else { // check for fundamental networking error
                  //Stop spinner
                  SharedClass.sharedInstance.stopActivityIndicator() //Stop spinner
                  //Print error in alert
                  SharedClass.sharedInstance.alert(view: self, title: "", message: "\(String(describing: error!.localizedDescription))")
                  return
                  }
      
                  SharedClass.sharedInstance.stopActivityIndicator() //Stop spinner
      
                  if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
                      print("statusCode should be 200, but is \(httpStatus.statusCode)")
                      print("response = \(String(describing: response))")
                  }
      
                  do {
                      let response = try JSONSerialization.jsonObject(with: data, options: []) as? [String: AnyObject]
                      print(response!)
                      //Your code here                    
                  } catch let error as NSError {
                      print(error)
                  }
              }
      
              task.resume()
      
          }
      
      }
      

      如果你有兴趣在SharedClass中使用这个功能

      //My shared class
      import UIKit
      class SharedClass: NSObject {
      
      static let sharedInstance = SharedClass()
      
      func postRequestFunction(apiName: String , parameters: String, onCompletion: @escaping (_ success: Bool, _ error: Error?, _ result: [String: Any]?)->()) {
      
          var URL =  "your URL here/index.php/***?"
      
          URL = URL.replacingOccurrences(of: "***", with: apiName)
      
          var request = URLRequest(url: URL(string: URL)!)
          request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
          request.httpMethod = "POST"
          print("shared URL : \(request)")
          request.httpBody = parameters.data(using: .utf8)
      
          var returnRes:[String:Any] = [:]
          let task = URLSession.shared.dataTask(with: request) { data, response, error in
      
              if let error = error {
                  onCompletion(false, error, nil)
              } else {
                  guard let data = data else {
                      onCompletion(false, error, nil)
                      return
                  }
      
                  if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode == 200 {
                      do {
                         returnRes = try JSONSerialization.jsonObject(with: data, options: []) as! [String : Any]
                          onCompletion(true, nil, returnRes)
      
                      } catch let error as NSError {
                         onCompletion(false, error, nil)
                      }
                  } else {
                      onCompletion(false, error, nil)
                  }
              }
          }
          task.resume()
      }
      
      
      private override init() {
      
      }
      

      最后像这样调用这个函数......

      SharedClass.sharedInstance.postRequestFunction(apiName: "Your API name", parameters: parameters) { (success, error, result) in
          print(result!)
          if success {
              //Your code here
          } else {
              print(error?.localizedDescription ?? "")
          }
      }
      

      【讨论】:

        【解决方案13】:

        亲吻答案:

        URLSession.shared.dataTask(with: URL(string: "https://google.com")!) {(data, response, error) in
            print(String(data: data!, encoding: .utf8))
        }.resume()
        

        【讨论】:

          【解决方案14】:

          发出 HTTP GET 请求的简单 Swift 2.0 方法

          HTTP 请求是异步的,因此您需要一种从 HTTP 请求中获取返回值的方法。这种方法使用通知程序并分布在两个类中。

          示例是使用http://www.example.com/handler.php?do=CheckUserJson&json= 网站检查标识符令牌的用户名和密码,即文件名为 handler.php 并在 do 参数上有一个 switch 语句以获取 RESTful 方法。

          在 viewDidLoad 我们设置 NotifierObserver,设置 json 并调用 getHTTPRequest 函数。它将使用http请求返回的参数返回到函数checkedUsernameAndPassword。

          override func viewDidLoad() {
              super.viewDidLoad()
              // setup the Notification observer to catch the result of check username and password
              NSNotificationCenter.defaultCenter().addObserver(self, selector: "checkedUsernameAndPassword:", name: CHECK_USERNAME_AND_PASSWORD, object: nil)        
              let username = GlobalVariables.USER_NAME
              let password = GlobalVariables.PASSWORD
              // check username and password
              if let jsonString = Utility.checkUsernameAndPasswordJson(username, password:password){
                  print("json string returned = \(jsonString)")
                  let url = CHECKUSERJSON+jsonString
                  // CHECKUSERJSON = http://www.example.com/handler.php?do=CheckUserJson&json=
                  // jsonString = {\"username\":\"demo\",\"password\":\"demo\"}"
                  // the php script handles a json request and returns a string identifier           
                  Utility.getHTTPRequest(url,notifierId: CHECK_USERNAME_AND_PASSWORD)
                  // the returned identifier is sent to the checkedUsernaeAndPassword function when it becomes availabel.
              }
          }
          

          Utility.swift 中有两个静态函数,首先对 json 进行编码,然后进行 HTTP 调用。

              static func checkUsernameAndPasswordJson(username: String, password: String) -> String?{
              let para:NSMutableDictionary = NSMutableDictionary()
                  para.setValue("demo", forKey: "username")
                  para.setValue("demo", forKey: "password")
              let jsonData: NSData
              do{
                  jsonData = try NSJSONSerialization.dataWithJSONObject(para, options: NSJSONWritingOptions())
                  let jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding) as! String
                  return jsonString
              } catch _ {
                  print ("UH OOO")
                  return nil
              }
          }
          

          和Http请求

              static func getHTTPRequest (url:String , notifierId: String) -> Void{
              let urlString = url
              let config = NSURLSessionConfiguration.defaultSessionConfiguration()
              let session = NSURLSession(configuration: config, delegate: nil, delegateQueue: nil)
              let safeURL = urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
              if let url = NSURL(string: safeURL){
                  let request  = NSMutableURLRequest(URL: url)
                  request.HTTPMethod = "GET"
                  request.timeoutInterval = 60
                  let taskData = session.dataTaskWithRequest(request, completionHandler: {
                      (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
                      if (data != nil) {
                          let result = NSString(data: data! , encoding: NSUTF8StringEncoding)
                          sendNotification (notifierId, message: String(result), num: 0)
                      }else{
                            sendNotification (notifierId, message: String(UTF8String: nil), num: -1)                    }
                  })
              taskData.resume()
              }else{
                  print("bad urlString = \(urlString)")
              }
          }
          

          sendNotification 函数完成了循环。请注意,在 Observer 中,选择器字符串的末尾有一个“:”。这允许通知在 userInfo 中携带有效负载。我给它一个字符串和一个整数。

              static func sendNotification (key: String, message:String?, num: Int?){
              NSNotificationCenter.defaultCenter().postNotificationName(
                  key,
                  object: nil,
                  userInfo:   (["message": message!,
                                "num": "\(num!)"])
              )
          }
          

          请注意,使用 HTTP 是老式的,更喜欢 HTTPS 参见How do I load an HTTP URL with App Transport Security enabled in iOS 9?

          【讨论】:

            【解决方案15】:
             var post:NSString = "api=myposts&userid=\(uid)&page_no=0&limit_no=10"
            
                NSLog("PostData: %@",post);
            
                var url1:NSURL = NSURL(string: url)!
            
                var postData:NSData = post.dataUsingEncoding(NSASCIIStringEncoding)!
            
                var postLength:NSString = String( postData.length )
            
                var request:NSMutableURLRequest = NSMutableURLRequest(URL: url1)
                request.HTTPMethod = "POST"
                request.HTTPBody = postData
                request.setValue(postLength, forHTTPHeaderField: "Content-Length")
                request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
                request.setValue("application/json", forHTTPHeaderField: "Accept")
            
                var reponseError: NSError?
                var response: NSURLResponse?
            
                var urlData: NSData? = NSURLConnection.sendSynchronousRequest(request, returningResponse:&response, error:&reponseError)
            
                if ( urlData != nil ) {
                    let res = response as NSHTTPURLResponse!;
            
                    NSLog("Response code: %ld", res.statusCode);
            
                    if (res.statusCode >= 200 && res.statusCode < 300)
                    {
                        var responseData:NSString  = NSString(data:urlData!, encoding:NSUTF8StringEncoding)!
            
                        NSLog("Response ==> %@", responseData);
            
                        var error: NSError?
            
                        let jsonData:NSDictionary = NSJSONSerialization.JSONObjectWithData(urlData!, options:NSJSONReadingOptions.MutableContainers , error: &error) as NSDictionary
            
                        let success:NSInteger = jsonData.valueForKey("error") as NSInteger
            
                        //[jsonData[@"success"] integerValue];
            
                        NSLog("Success: %ld", success);
            
                        if(success == 0)
                        {
                            NSLog("Login SUCCESS");
            
                            self.dataArr = jsonData.valueForKey("data") as NSMutableArray
                            self.table.reloadData()
            
                        } else {
            
                            NSLog("Login failed1");
                            ZAActivityBar.showErrorWithStatus("error", forAction: "Action2")
                        }
            
                    } else {
            
                        NSLog("Login failed2");
                        ZAActivityBar.showErrorWithStatus("error", forAction: "Action2")
            
                    }
                } else {
            
                    NSLog("Login failed3");
                    ZAActivityBar.showErrorWithStatus("error", forAction: "Action2")
            }
            

            一定会帮到你

            【讨论】:

              【解决方案16】:

              //这是一个对我有用的例子

              //使用键值向服务器发布请求的Swift函数

              func insertRecords()
              {
              
              
              
                  let usrID = txtID.text
                  let checkin = lblInOut.text
                  let comment = txtComment.text
              
              
              
                  // The address of the web service
                  let urlString = "http://your_url/checkInOut_post.php"
              
                  // These are the keys that your are sending as part of the post request
                  let keyValues = "id=\(usrID)&inout=\(checkin)&comment=\(comment)"
              
              
              
              
                  // 1 - Create the session by getting the configuration and then
                  //     creating the session
              
                  let config = NSURLSessionConfiguration.defaultSessionConfiguration()
                  let session = NSURLSession(configuration: config, delegate: nil, delegateQueue: nil)
              
              
                  // 2 - Create the URL Object
              
                  if let url = NSURL(string: urlString){
              
              
                      // 3 - Create the Request Object
              
                      var request  = NSMutableURLRequest(URL: url)
                      request.HTTPMethod = "POST"
              
                      // set the key values
                      request.HTTPBody = keyValues.dataUsingEncoding(NSUTF8StringEncoding);
              
              
                      // 4 - execute the request
              
                      let taskData = session.dataTaskWithRequest(request, completionHandler: {
              
                          (data:NSData!, response:NSURLResponse!, error:NSError!) -> Void in
              
                          println("\(data)")
              
                          // 5 - Do something with the Data back
              
                          if (data != nil) {
              
                              // we got some data back
                              println("\(data)")
              
                              let result = NSString(data: data , encoding: NSUTF8StringEncoding)
                              println("\(result)")
              
                              if result == "OK" {
              
                                  let a = UIAlertView(title: "OK", message: "Attendece has been recorded", delegate: nil, cancelButtonTitle: "OK")
              
                                  println("\(result)")
              
                                  dispatch_async(dispatch_get_main_queue()) {
              
              
                                  a.show()
              
              
                                  }
              
              
                              } else {
                                // display error and do something else
              
                              }
              
              
                          } else
              
                          {   // we got an error
                              println("Error getting stores :\(error.localizedDescription)")
              
                          }
              
              
                      })
              
                      taskData.resume()
              
              
              
                  }
              
              
              }
              

              获取键值的PHP代码

              $empID = $_POST['id'];

              $inOut = $_POST['inout'];

              $comment = $_POST['comment'];

              【讨论】:

                【解决方案17】:

                这是一个非常简单的 Swift 4 在游乐场中的示例:

                import UIKit
                // run asynchronously in a playground
                import PlaygroundSupport
                PlaygroundPage.current.needsIndefiniteExecution = true
                
                // create a url
                let url = URL(string: "http://www.stackoverflow.com")
                
                // create a data task
                let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
                    if error != nil {
                        print("there's a problem")
                    }
                    print(String(data: data!, encoding: String.Encoding.utf8) ?? "")
                }
                
                //running the task w/ resume
                task.resume()
                

                【讨论】:

                  【解决方案18】:

                  要让 XCUITest 在异步请求完成之前停止测试完成,请使用这个(可能会减少 100 超时):

                  func test_api() {
                      let url = URL(string: "https://jsonplaceholder.typicode.com/posts/42")!
                      let exp = expectation(description: "Waiting for data")
                      let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
                          guard let data = data else { return }
                          print(String(data: data, encoding: .utf8)!)
                          exp.fulfill()
                      }
                      task.resume()
                      XCTWaiter.wait(for: [exp], timeout: 100)
                  }
                  

                  【讨论】:

                    【解决方案19】:

                    更新:- Xcode 13.0 和 Swift 5+

                    获取 HTTP 请求

                    let url = URL(string: "URL HERE")! //PUT Your URL
                            var request = URLRequest(url: url)
                            request.httpMethod = "GET"
                            let task = URLSession.shared.dataTask(with: request) { data, response, error in
                                guard let safeData = data,
                                      let response = response as? HTTPURLResponse,
                                      error == nil else {                                              // check for fundamental networking error
                                          print("error", error ?? "Unknown error")
                                          delegate?.onError(error!)
                                          return
                                      }
                                
                                guard (200 ... 299) ~= response.statusCode else {                    // check for http errors
                                    print("statusCode should be 2xx, but is \(response.statusCode)")
                                    print("response = \(response)")
                                    return
                                }
                                
                                let responseString = String(data: safeData, encoding: .utf8)
                                print("Response String = \(responseString)")
                            }
                            
                            task.resume()
                    

                    【讨论】:

                      【解决方案20】:

                      下面给出了示例“GET”请求的示例。

                      let urlString = "YOUR_GET_URL"
                      let yourURL = URL(string: urlstring)
                      let dataTask = URLSession.shared.dataTask(with: yourURL) { (data, response, error) in
                      do {
                          let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
                          print("json --- \(json)")
                          }catch let err {
                          print("err---\(err.localizedDescription)")
                          }
                         }
                      dataTask.resume()
                      

                      【讨论】:

                        猜你喜欢
                        • 2016-09-24
                        • 2020-05-17
                        • 1970-01-01
                        • 1970-01-01
                        • 2014-08-14
                        • 2015-11-03
                        • 1970-01-01
                        相关资源
                        最近更新 更多