Mercutio
Mercutio

Reputation: 1296

Trying to replicate C# POST call in Swift

I've got a simple web service that's working with a C# client, but is throwing a 400 status code when I attempt to POST with a Swift client.

So far I can get an array of checklists objects in Swift, that return in the following JSON format:

data - - - Optional(["User": {
    "Display_Name" = "<null>";
    Email = "<null>";
    "First_Name" = "Tester 0";
    "Last_Name" = McTesterson;
    Phone = "<null>";
    "User_ID" = 1;
}, "Checklist_ID": 1, "Description": {
    "Description_ID" = 1;
    Summary = "test summary";
    Title = "Test Title u.u";
}, "Status": {
    State = 1;
    "Status_ID" = 1;
}])

When I go to POST a new checklist, the title is passed in the request's URI after .../checklist/create/ and the http body / content is a single value for the 'Summary' field. It is successfully doing so in C# using this code:

public static void CreateChecklist(string title, string summary = "")
{
    let url = $"/checklist/create/{title}/"
    Post<string, string>(HttpMethod.Post, url, requestContent: summary);
}

private R Post<T, R>(HttpMethod ClientMethod, string methodUrl, object requestContent = default(object))
{
    var httpClient = new HttpClient();
    methodUrl = CHECKLIST_URL + methodUrl;
    var request = new HttpRequestmessage() 
    {
        RequestUri = new Uri(methodUrl),
        Method = ClientMethod
    };

    // When uploading, setup the content here...
    if (ClientMethod == HttpMethod.Post || ClientMethod == HttpMethod.Put)
    {
        string serializedContent = JsonConvert.SerializeObject(requestContent);
        request.Content = new StringContent(serializedContent, Encoding.UTF8, "application/json");
    }

    // Process the response...
    HttpResponseMessage response;
    try 
    {
        response = httpClient.SendAsync(request).Result;
    }
    catch (Exception ex)
    {
        while (ex.InnerException != null) ex = ex.InnerException;
        throw ex;
    }

    if (response.IsSuccessStatusCode) 
    {
        var tempContent = response.Content.ReadAsStringAsync().Result;
        var r = JsonConvert.DeserializeObject<R>(tempContent);
        return r;
    }
    else 
    {
        throw new Exception("HTTP Operation failed");
    }
}

However, when I go to post in Swift a 400 Response is returned and no new checklist is created (see Console output below). Here is the Swift code I'm using (pushed together into a single method):

    func uglyPost<T: RestCompatible>(request: String,
                                     for rec: T,
                                     followUp: OptionalBlock = nil) {

        guard let url = URL(string: request) else { followUp?(); return }
        let g = DispatchGroup()

        var request = URLRequest(url: url)
        request.httpMethod = "POST"

        // This is where the summary field is serialized and injected...
        do {
            let body = ["Summary": ""]
print("   isValid - \(JSONSerialization.isValidJSONObject(body))")
            request.httpBody = try JSONSerialization.data(withJSONObject: body,
                                                          options: [])
            request.setValue("application/json; charset=utf-8",
                             forHTTPHeaderField: "Content-Type")
        } catch {
            print(" Error @ CanSerializeJSONRecord")
        }

        // This is the actual POST request attempt...
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
print(" d - \n\(String(describing: data?.description))")
print(" r - \n\(String(describing: response))")
            g.leave()
            if let error = error {
                print(" Error @ CanMakePostRequest - \(error.localizedDescription)")
                return
            }
        }

        // This is where asyncronous POST reequest is executed...
        g.enter()
        task.resume()

        // Waiting for POST request to conclude before completion block
        g.wait()
        followUp?()
    }

Also, the console output:

 --http://-----.azurewebsites.net/api/-----/checklist/create/SwiftPostTests
   isValid - true
 d - 
Optional("33 bytes")
 r - 
Optional(<NSHTTPURLResponse: 0x7fb549d0e300> { URL: http://-----.azurewebsites.net/api/-----/checklist/create/SwiftPostTests } { Status Code: 400, Headers {
    "Content-Type" =     (
        "application/json; charset=utf-8"
    );
    Date =     (
        "Sat, 08 Dec 2018 22:57:50 GMT"
    );
    Server =     (
        K-----
    );
    "Transfer-Encoding" =     (
        Identity
    );
    "X-Powered-By" =     (
        "ASP.NET"
    );
} })
 fulfilling
/Users/.../SingleSequenceUglyPost.swift:79: error: -[*.SingleSequenceUglyPost testUglyFullSequence] : XCTAssertGreaterThan failed: ("307") is not greater than ("307") - 

My URI is correct, and the server is up because I'm making GET calls successfully and can POST from C# client. Any help on why I'm getting the 400 code or what my next troubleshooting steps should be?

Upvotes: 0

Views: 101

Answers (1)

Mercutio
Mercutio

Reputation: 1296

The issue here was that the web service (built on azure, c#) allowed for values to be sent outside of collections (dictionary, array of dictionary). We had to adjust it to receive Json objects instead of raw strings. Not sure if it's possible to serialize non key-value pairs in Swift, but both languages work with the web api's now.

Upvotes: 0

Related Questions