Schnelle GET-Anfrage mit Parametern

81

Ich bin sehr neu in Swift, daher werde ich wahrscheinlich viele Fehler in meinem Code haben, aber ich versuche, eine zu senden GET Anfrage mit Parametern an einen localhost-Server zu senden. Mehr noch, ich versuche es zu erreichen, da meine Funktion zwei Parameter annimmt baseURL:string,params:NSDictionary. Ich bin nicht sicher, wie ich diese beiden in der tatsächlichen URLRequest kombinieren soll. Folgendes habe ich bisher versucht

    func sendRequest(url:String,params:NSDictionary){
       let urls: NSURL! = NSURL(string:url)
       var request = NSMutableURLRequest(URL:urls)
       request.HTTPMethod = "GET"
       var data:NSData! =  NSKeyedArchiver.archivedDataWithRootObject(params)
       request.HTTPBody = data
       println(request)
       var session = NSURLSession.sharedSession()
       var task = session.dataTaskWithRequest(request, completionHandler:loadedData)
       task.resume()

    }

}

func loadedData(data:NSData!,response:NSURLResponse!,err:NSError!){
    if(err != nil){
        println(err?.description)
    }else{
        var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
        println(jsonResult)

    }

}
MrSSS16
quelle

Antworten:

156

Beim Erstellen einer GETAnfrage gibt es keinen Textkörper für die Anfrage, sondern alles geht über die URL. Sie können auch eine URL erstellen (und den Prozentsatz, der ihr entgeht) URLComponents.

var url = URLComponents(string: "https://www.google.com/search/")!

url.queryItems = [
    URLQueryItem(name: "q", value: "War & Peace")
]

Der einzige Trick besteht darin, dass für die meisten Webdienste ein +prozentualer Escapezeichen erforderlich ist (da dies als Leerzeichen gemäß der application/x-www-form-urlencodedSpezifikation interpretiert wird ). Aber URLComponentswird nicht Prozent davon entkommen. Apple behauptet, dass dies +ein gültiges Zeichen in einer Abfrage ist und daher nicht maskiert werden sollte. Technisch gesehen sind sie richtig, dass es in einer Abfrage eines URI erlaubt ist, aber es hat eine besondere Bedeutung in application/x-www-form-urlencodedAnfragen und sollte wirklich nicht ohne Flucht übergeben werden.

Apple räumt ein, dass wir den +Zeichen prozentual entkommen müssen , rät jedoch, dies manuell zu tun:

var url = URLComponents(string: "https://www.wolframalpha.com/input/")!

url.queryItems = [
    URLQueryItem(name: "i", value: "1+2")
]

url.percentEncodedQuery = url.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")

Dies ist eine unelegante Problemumgehung, die jedoch funktioniert. Apple empfiehlt, wenn Ihre Abfragen möglicherweise ein +Zeichen enthalten und Sie einen Server haben, der sie als Leerzeichen interpretiert.

Wenn Sie das mit Ihrer sendRequestRoutine kombinieren , erhalten Sie so etwas wie:

func sendRequest(_ url: String, parameters: [String: String], completion: @escaping ([String: Any]?, Error?) -> Void) {
    var components = URLComponents(string: url)!
    components.queryItems = parameters.map { (key, value) in 
        URLQueryItem(name: key, value: value) 
    }
    components.percentEncodedQuery = components.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")
    let request = URLRequest(url: components.url!)

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data,                            // is there data
            let response = response as? HTTPURLResponse,  // is there HTTP response
            (200 ..< 300) ~= response.statusCode,         // is statusCode 2XX
            error == nil else {                           // was there no error, otherwise ...
                completion(nil, error)
                return
        }

        let responseObject = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
        completion(responseObject, nil)
    }
    task.resume()
}

Und du würdest es so nennen:

sendRequest("someurl", parameters: ["foo": "bar"]) { responseObject, error in
    guard let responseObject = responseObject, error == nil else {
        print(error ?? "Unknown error")
        return
    }

    // use `responseObject` here
}

Persönlich würde ich JSONDecoderheutzutage verwenden und einen benutzerdefinierten structanstelle eines Wörterbuchs zurückgeben, aber das ist hier nicht wirklich relevant. Hoffentlich veranschaulicht dies die Grundidee, wie die Parameter in die URL einer GET-Anforderung prozentual codiert werden.


Siehe vorherige Überarbeitung dieser Antwort für Swift 2 und manuelle prozentuale Fluchtversuche.

rauben
quelle
Danke für die fantastische Antwort. Ich habe nur eine Frage, ich bin immer noch ein bisschen verwirrt darüber, was das extension stringmit den Werten macht? Auch wann müsste ich HttpBodydann verwenden?
MrSSS16
Die Zeichenfolgenerweiterung ist ein Prozentsatz, der den Werten gemäß RFC 3986 entgeht. Es gibt bestimmte Zeichen, die in URLs eine besondere Bedeutung haben (z. B. &trennt ein Parameter vom nächsten. Wenn &der Wert also auftritt, können Sie ihn nicht ohne Flucht lassen). In Bezug auf HTTPBodysollten Sie es nicht in GETAnfrage verwenden; Es wird in POSTaber nicht verwendet GET.
Rob
Das sieht so einfach aus. Was sind die Vorteile, wenn Sie stattdessen so etwas wie github.com/xyyc/SwiftSocket verwenden? Es tut mir leid, dass ich neu in all dem bin.
Jigzat
Das ist möglicherweise nicht der richtige Vergleich, da dies eine Sockets-Bibliothek ist und dies HTTP ist. Closer ist so etwas wie Alamofire , aber das ist (a) flexibler (kann JSON-Anforderungen, x-www-form-urlencodierte Anforderungen, mehrteilige usw. verarbeiten); (b) funktionaler (z. B. für Authentifizierungsprobleme); und (c) bringt Sie aus dem Unkraut der HTTP-Programmierung heraus. Wenn überhaupt, ist meine obige Antwort als warnende Geschichte über die Risiken gedacht, nur eine eigene zu erstellen NSMutableURLRequest, und weist darauf hin, dass mehr dahinter steckt, als das OP vorschlägt.
Rob
Dies ist eine schöne Lösung. Danke @Rob. Übrigens gibt es einen Unterschied bei der Bereitstellung solcher Parameter NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: nil)? parameterseine Reihe von Wörterbüchern sein[String: String]
Isuru
94

Verwenden Sie NSURLComponents, um Ihre NSURL wie folgt zu erstellen

var urlComponents = NSURLComponents(string: "https://www.google.de/maps/")!

urlComponents.queryItems = [
  NSURLQueryItem(name: "q", value: String(51.500833)+","+String(-0.141944)),
  NSURLQueryItem(name: "z", value: String(6))
]
urlComponents.URL // returns https://www.google.de/maps/?q=51.500833,-0.141944&z=6

Schriftart: https://www.ralfebert.de/snippets/ios/encoding-nsurl-get-parameters/

Ben-Hur Batista
quelle
5
Trotz Robs beeindruckender Antwort ist Ihre einfacher und funktioniert.
Jan ATAC
3
Dies sollte die akzeptierte Antwort sein. Es wird empfohlen, NSURLComponents zusammen mit Abfrageelementen zum Erstellen von URLs zu verwenden. Viel sicherer und weniger fehleranfällig.
John Rogers
4

Ich benutze dies, versuche es auf dem Spielplatz. Definieren Sie die Basis-URLs als Struktur in Konstanten

struct Constants {

    struct APIDetails {
        static let APIScheme = "https"
        static let APIHost = "restcountries.eu"
        static let APIPath = "/rest/v1/alpha/"
    }
}

private func createURLFromParameters(parameters: [String:Any], pathparam: String?) -> URL {

    var components = URLComponents()
    components.scheme = Constants.APIDetails.APIScheme
    components.host   = Constants.APIDetails.APIHost
    components.path   = Constants.APIDetails.APIPath
    if let paramPath = pathparam {
        components.path = Constants.APIDetails.APIPath + "\(paramPath)"
    }
    if !parameters.isEmpty {
        components.queryItems = [URLQueryItem]()
        for (key, value) in parameters {
            let queryItem = URLQueryItem(name: key, value: "\(value)")
            components.queryItems!.append(queryItem)
        }
    }

    return components.url!
}

let url = createURLFromParameters(parameters: ["fullText" : "true"], pathparam: "IN")

//Result url= https://restcountries.eu/rest/v1/alpha/IN?fullText=true
anoop4real
quelle
1

Swift 3 :

extension URL {
    func getQueryItemValueForKey(key: String) -> String? {
        guard let components = NSURLComponents(url: self, resolvingAgainstBaseURL: false) else {
              return nil
        }

        guard let queryItems = components.queryItems else { return nil }
     return queryItems.filter {
                 $0.name.lowercased() == key.lowercased()
                 }.first?.value
    }
}

Ich habe es verwendet, um den Bildnamen für UIImagePickerControllerin zu erhalten func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]):

var originalFilename = ""
if let url = info[UIImagePickerControllerReferenceURL] as? URL, let imageIdentifier = url.getQueryItemValueForKey(key: "id") {
    originalFilename = imageIdentifier + ".png"
    print("file name : \(originalFilename)")
}
Danut Pralea
quelle
Ihre Antwort beantwortet OPs Frage nicht
Ilias Karim
0

Sie können Ihre verlängern Dictionarynur zu liefern , stringFromHttpParameterwenn beide Schlüssel und Wert entsprechen CustomStringConvertablemag diese

extension Dictionary where Key : CustomStringConvertible, Value : CustomStringConvertible {
  func stringFromHttpParameters() -> String {
    var parametersString = ""
    for (key, value) in self {
      parametersString += key.description + "=" + value.description + "&"
    }
    return parametersString
  }
}

Dies ist viel sauberer und verhindert versehentliche Aufrufe stringFromHttpParametersvon Wörterbüchern, die diese Methode nicht aufrufen können

Reza Shirazian
quelle
-1

Diese von @Rob vorgeschlagene Erweiterung funktioniert für Swift 3.0.1

Ich konnte die Version, die er in seinen Beitrag aufgenommen hat, nicht mit Xcode 8.1 (8B62) kompilieren.

extension Dictionary {

    /// Build string representation of HTTP parameter dictionary of keys and objects
    ///
    /// :returns: String representation in the form of key1=value1&key2=value2 where the keys and values are percent escaped

    func stringFromHttpParameters() -> String {

        var parametersString = ""
        for (key, value) in self {
            if let key = key as? String,
               let value = value as? String {
                parametersString = parametersString + key + "=" + value + "&"
            }
        }
        parametersString = parametersString.substring(to: parametersString.index(before: parametersString.endIndex))
        return parametersString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
    }

}
etayluz
quelle
-3

Ich benutze:

let dictionary = ["method":"login_user",
                  "cel":mobile.text!
                  "password":password.text!] as  Dictionary<String,String>

for (key, value) in dictionary {
    data=data+"&"+key+"="+value
    }

request.HTTPBody = data.dataUsingEncoding(NSUTF8StringEncoding);
Fabio Guerra
quelle