4

I am trying to send a Post request from my iOS app, to my rails API. I can make it so that I receive a status code of 201 (succes/created), however, I can not actually send the data through and create the object in rails.

Here is my code in the rails API:

def create
    puts params
    product = current_user.products.build(product_params)
    if product.save
        render json: product, status: 201, location: [:api, product]
    else
        render json: {errors: product.errors}, status: 422
    end
end

private
def product_params
    params.require(:product).permit(:title)
end

With the above code, I receive a status code of 400. However, when I remove the (products_params) from the create action, then I receive a 201 status code. So it reaches the server and creates a new object in my database, but its title is just nil.

The problem is this: When I print out the params, They come through like so:

{"{\"title\":\"First Title\"}"=>nil, "format"=>:json, 
"controller"=>"api/v1/products", "action"=>"create", "user_id"=>"2"}

Whereas, they need to come through like this:

  {"title"=>"First Title", "format"=>:json, 
  "controller"=>"api/v1/products", "action"=>"create", "user_id"=>"2"}

Currently, on the iOS side, I am sending through the params like so:

let params = ["title": "First Title"]

How can I send the params from the iOS side so that they come through to the rails side in the correct format?

Just for a little more info, this is the response I'm getting in my Terminal when I do the post request:

enter image description here

Thanks

2
  • Show us how you send params to Rails. Rails is clearly receiving the params the wrong way. Commented Jan 28, 2016 at 21:56
  • thanks for the reply, but I figured it out below :) I wasn't setting the content type to json. Commented Jan 28, 2016 at 21:58

1 Answer 1

2

I figured this out. The problem was that in my POST request, I was not setting the content type HTTP header to json. So, I simply added this line to my post request:

request.addValue("application/json", forHTTPHeaderField: "Content-Type")

And then rails automatically converted my parameters as a Swift Dictionary to a rails hash. my final code was this:

iOS side, POST request:

 func postRequest2(x: String) {
    let string = "http://localhost:3000/api/users/2/products"
    let url = NSURL(string: string)
    let session = NSURLSession.sharedSession()
    let request = NSMutableURLRequest(URL: url!)
    let params = ["title": "Fifth Title"]
    request.setValue("XXXXXXXXXX", forHTTPHeaderField: "Authorization")
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.HTTPMethod = "POST"
    do {
        request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: NSJSONWritingOptions.PrettyPrinted)
    }
    catch {

    }
    let tache = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
        if let antwort = response as? NSHTTPURLResponse {
            let code = antwort.statusCode
            print(code)
        }
    }
    tache.resume()

}

and rails side, in my ProductsController:

def create

    product = current_user.products.build(product_params)
    if product.save
        render json: product, status: 201, location: [:api, product]
    else
        render json: {errors: product.errors}, status: 422
    end
end
Sign up to request clarification or add additional context in comments.

4 Comments

I'm glad you found the answer. A quick tip: I (it's a matter of personal choice, but here it goes) prefer to use :created and :unprocessable_entity for the statuses. It make the code more readable.
you mean like: render json: product, status: :created, location: [:api, product] and render json: {errors: product.errors}, status: :unprocessable_entity ?
Yeah, you can replace the numbers with the symbolized names. Given how straightforward your API looks I would also recommend you look up the responders gem. It was recently separated from the Rails core , but it responds just like you want, with a simple responds_with @product after @product.save, no conditionals.
FYI, I’m pretty sure this won’t work as written. You call params.require(:product) but don’t include product as a key when you define params = ["title": "Fifth Title"] on the Swift side.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.