Asked  7 Months ago    Answers:  5   Viewed   84 times

I'm trying to run a HTTP Request in Swift, to POST 2 parameters to a URL.

Example:

Link: www.thisismylink.com/postName.php

Params:

id = 13
name = Jack

What is the simplest way to do that?

I don't even want to read the response. I just want to send that to perform changes on my database through a PHP file.

 Answers

77

In Swift 3 and later you can:

let url = URL(string: "http://www.thisismylink.com/postName.php")!
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let parameters: [String: Any] = [
    "id": 13,
    "name": "Jack & Jill"
]
request.httpBody = parameters.percentEncoded()

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    guard let data = data, 
        let response = response as? HTTPURLResponse, 
        error == nil else {                                              // check for fundamental networking error
        print("error", error ?? "Unknown 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: data, encoding: .utf8)
    print("responseString = (responseString)")
}

task.resume()

Where:

extension Dictionary {
    func percentEncoded() -> Data? {
        return map { key, value in
            let escapedKey = "(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
            let escapedValue = "(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
            return escapedKey + "=" + escapedValue
        }
        .joined(separator: "&")
        .data(using: .utf8)
    }
}

extension CharacterSet { 
    static let urlQueryValueAllowed: CharacterSet = {
        let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
        let subDelimitersToEncode = "!$&'()*+,;="

        var allowed = CharacterSet.urlQueryAllowed
        allowed.remove(charactersIn: "(generalDelimitersToEncode)(subDelimitersToEncode)")
        return allowed
    }()
}

This checks for both fundamental networking errors as well as high-level HTTP errors. This also properly percent escapes the parameters of the query.

Note, I used a name of Jack & Jill, to illustrate the proper x-www-form-urlencoded result of name=Jack%20%26%20Jill, which is “percent encoded” (i.e. the space is replaced with %20 and the & in the value is replaced with %26).


See previous revision of this answer for Swift 2 rendition.

Tuesday, June 1, 2021
 
Sauleil
answered 7 Months ago
37

You shouldn't use HttpContext for getting the files in ASP.NET Web API. Take a look at this example written by Microsoft (http://code.msdn.microsoft.com/ASPNET-Web-API-File-Upload-a8c0fb0d/sourcecode?fileId=67087&pathId=565875642).

public class UploadController : ApiController 
{ 
    public async Task<HttpResponseMessage> PostFile() 
    { 
        // Check if the request contains multipart/form-data. 
        if (!Request.Content.IsMimeMultipartContent()) 
        { 
            throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); 
        } 

        string root = HttpContext.Current.Server.MapPath("~/App_Data"); 
        var provider = new MultipartFormDataStreamProvider(root); 

        try 
        { 
            StringBuilder sb = new StringBuilder(); // Holds the response body 

            // Read the form data and return an async task. 
            await Request.Content.ReadAsMultipartAsync(provider); 

            // This illustrates how to get the form data. 
            foreach (var key in provider.FormData.AllKeys) 
            { 
                foreach (var val in provider.FormData.GetValues(key)) 
                { 
                    sb.Append(string.Format("{0}: {1}n", key, val)); 
                } 
            } 

            // This illustrates how to get the file names for uploaded files. 
            foreach (var file in provider.FileData) 
            { 
                FileInfo fileInfo = new FileInfo(file.LocalFileName); 
                sb.Append(string.Format("Uploaded file: {0} ({1} bytes)n", fileInfo.Name, fileInfo.Length)); 
            } 
            return new HttpResponseMessage() 
            { 
                Content = new StringContent(sb.ToString()) 
            }; 
        } 
        catch (System.Exception e) 
        { 
            return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e); 
        } 
    } 

}
Friday, July 23, 2021
 
linjuming
answered 5 Months ago
21

The solution. Thanks to @Rob :

func loginRequest(url:String, withParams params: [String: String?], postCompleted : (succeeded: Bool, msg: String) -> ()){
    var request = NSMutableURLRequest(URL: NSURL(string: url)!)
    var session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"

    var err: NSError?
    var bodyData = ""
    for (key,value) in params{
        if (value == nil){ continue }
        let scapedKey = key.stringByAddingPercentEncodingWithAllowedCharacters(
            .URLHostAllowedCharacterSet())!
        let scapedValue = value!.stringByAddingPercentEncodingWithAllowedCharacters(
            .URLHostAllowedCharacterSet())!
        bodyData += "(scapedKey)=(scapedValue)&"
    }

    request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)

    var task = session.dataTaskWithRequest(request,
        completionHandler: {data, response, error -> Void in
            let dataString = NSString(data: data, encoding: NSUTF8StringEncoding)
            postCompleted(succeeded: true, msg: dataString!)
    })
    task.resume()
}

Then, I call the function and I can know if the user is correct:

        self.loginRequest("http:myurl.com",
            withParams: ["email":"email","passw":"passw"])
        {
            (succeeded: Bool, msg: String) -> () in
            if(succeeded) {
                if msg == "0"
                {
                    //Incorrect data...
                }
                else
                {
                    //The login it's ok...
                }
            }
        }
Sunday, August 15, 2021
 
Seankala
answered 4 Months ago
37

I'm trying to see a reason why you are not using the WebClient class:-

var fields = new NameValueCollection();
fields.Add("streetnumber", address.StreetNumber);
fields.Add("streetname", address.StreetName);
fields.Add("city", address.City);
fields.Add("province", address.Province);

var wc = new WebClient();
byte[] resultData = wc.UploadValues(url, fields);
string result = Encoding.Default.GetString(resultData);

You might want to check the encoding used by the server when sending the results, if it uses UTF-8 change the last line to:-

string result = Encoding.UTF8.GetString(resultData);

Some issues I spot in your orginal code:-

  1. The first field is prefixed with &, that shouldn't be there.
  2. You need call use Uri.EscapeDataString on each field value.
  3. You are attempting to construct a memory stream around the result of GetRequestStream, I can't see what that would achieve even if MemoryStream had such a constructor but it doesn't anyway. Just write directly to the stream returned by GetRequestStream

If you have already done so get yourself a copy of fiddler so you can observe what occurs when a standard form requests the data sucessfully and what your code is doing.

Edit: If you have evidence that the lack of a cookie container is what causes WebClient not to work then you could try this approach:-

public class MyWebClient : WebClient
{

    protected override WebRequest GetWebRequest (Uri address)
    {
      WebRequest request = (WebRequest) base.GetWebRequest (address);

      request.Container = new CookieContainer();
      return request;
    }
}

Now use my code above to but instead od instancing WebClient instance MyWebClient instead.

Wednesday, October 20, 2021
 
muued
answered 2 Months ago
30

It sounds like to me that you want to implement the HTTP protocol from scratch on top of the POSIX sockets API. I have done this myself, it was quite fun.

Read about the sockets API here: http://en.wikipedia.org/wiki/Berkeley_sockets

If you want to work on Windows, see here.

This link, posted in the comments, provides a pretty good starting-point example for using the API, although it weirdly includes both the client and the server as serial logic within the same program -- which may allow it to bypass some of the calls (such as waiting for incoming connections) required to implement a client or server as a standalone program.

Assuming you are implementing an HTTP server in C++, you might choose to implement the client as a web page (running on your favorite browser), as the following hack demonstrates...

<html>
<head>
</head>
<body>
This web page sends the entered text back to the server upon a button press.
The server's response is then displayed in the box below.
This is only a hack for learning purposes, and not a demonstration of best-practices.
<br>
<textarea id="INP">
Hello world!
</textarea>
<br>
<button onclick="return make_request('INP','GET','test1')">GET Request</button>
<button onclick="return make_request('INP','POST','test2')">POST Request</button>
<div id="result" style='border-style:solid;'>?</div>
<script>
function make_request( textID, request_type, webfn )
   {
   var url = "" + "?webfn="+webfn // assumes this page was served by the same server
   if ( request_type != 'POST' ) url += "&value="+document.getElementById(textID).value;
   var req = new XMLHttpRequest();
   req.open( request_type, url, /*async*/false );
   req.send( request_type=='POST' ? document.getElementById(textID).value : null );
   if ( req.readyState == 4/*complete*/ && req.status == 200/*OK*/ )
      {
      result = req.responseXML.documentElement.getElementsByTagName('value')[0].firstChild.data;
      document.getElementById('result').innerHTML = req.responseText;
      }
   else alert("HTTP request failed");
   return false;
   }
</script>
</body>
</html>
Thursday, December 2, 2021
 
cusejuice
answered 5 Days ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share