NSURLSession GET request with Body

Hi,


I am wondering if its possible to do a GET request with a JSON payload.


According to the documentation for NSURLSession, the following is stated: "Your app can provide the request body content for an HTTP POST request in three ways: as an

NSData
object, as a file, or as a stream."


However, this does not mention GET requests. I have tried to put a body in HTTP POST request but it seemed like nothing was actually sent over. I am wondering how to do this properly.. and if it can't be done, are their lower level APIs that I can use to perform this task anyway?

Replies

Sure it's possible, but most servers will ignore the body, per HTTP specs.

I'm also wondering the same thing.


I ran into this by chance. It looks like the body isn't sent if httpMethod is GET.

Content-Length is sent in the header, but the body isn't sent. The server then waits for the body content, but the client will never send it, so after a minute or so, the client will see the error "The request timed out." Here is sample code that shows this issue:


let session = URLSession(configuration: URLSessionConfiguration.default, delegate: nil, delegateQueue: nil)
var request = URLRequest(url: URL(string:"https://www.example.com")!)
request.httpMethod = "GET"
request.httpBody = try! JSONSerialization.data(withJSONObject: [], options: .prettyPrinted)
let task = session.dataTask(with: request) { (data, response, error) in
    print("Got response")
    print(data)
    print(response)
    print(error)
}
task.resume()


If httpMethod is POST/PUT/DELETE, this comes back fine. Alternatively, if httpBody is nil, it will also work.


This seems like an issue in Foundation. I would not have expected this code to send a request with the Content-Length set but not send the body.

Many RESTful APIs rely on GET requests with httpBody data. Here is an example of one:


https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html

This is definitely bugworthy IMO. There are three reasonable choices for

NSURLSession
to make here:
  • It could support this (as a compatibility sop to servers that don’t follow the standard [1]).

  • It could ignore the body completely.

  • It could treat this as a programmer error (either failing immediately or trapping).

The current behaviour, where it sends the

Content-Length
header but not the message body, is hard to justify.

Given that, I encourage you to file a bug report about this. In there you can make your own recommendation as to how you think it should behave. If you have concrete information on the number and type of servers that expect this non-standard behaviour to work, that’d be helpful.

Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] Section 4.3.1 of RFC 7231 clearly calls this out as bogus:

A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.

Radar # 46025234

I found that making the GET request with body data with CFHTTPMessage had the same results. I was able to make the request in my code using libcurl. When you add body data to a libcurl request the request type changes to POST automatically, but you can change it back to GET and then it works and sends the body data.

I'm hitting the same issue (no body allowed with GET) in Swift with URLSession. The URLSession throws an error when it finds a body length for the GET message.

It's maddening that one team's viewpoint on the use of the GET verb is codified on the client end, so that it blocks compatibility with so many existing APIs.