iosruby-on-rails-3cocoa-touchasiformdatarequest

iOS POST to Rails - params does not contain value for :modelname


I have a rails 3 controller which responds to both HTML and JSON client requests. This controller has a 'create' method that creates a new 'user' model object with values posted in params[:user]. This works as intended when the user params values are posted from an HTML form. However when posting to this action from JSON client (iOS in my case) params[:user] is nil; I do notice that the params hash does contain the correct values if they are accessed directly i.e. params[:username] => "someusername"

Is there something I need to add to the HTTP request so I can use the params[:model_name] syntax in my controller? I am using ASIFormDataRequest.m to POST to my Rails application.

My client POST looks like this:

-(void)createUser:(NSString *)first
     lastName:(NSString *)last
     userName:(NSString *)user
        email:(NSString *)email
     password:(NSString *)password{

NSString *stringFormat = [NSString stringWithFormat:@"%@users.json", serviceBaseURL];
NSURL *url = [NSURL URLWithString:stringFormat];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
User *requestedUser = [[User alloc]initWithId:nil firstName:first lastName:last userName:user email:email password:password];
request.tag = CREATE_USER;
request.userInfo = [NSDictionary dictionaryWithObject:requestedUser forKey:@"requestedUser"];
[request setPostValue:first forKey:@"first_name"];
[request setPostValue:last forKey:@"last_name"];
[request setPostValue:user forKey:@"username"];
[request setPostValue:email forKey:@"email"];
[request setPostValue:password forKey:@"password"];
request.Delegate = self;
[request startAsynchronous];

}

The controller 'create' methods looks like so:

 def create
  @user = User.new(params[:user])      
  if @user.save  #This will fail because params[:user] is nil when posted from iOS!
    flash[:notice] = {:success => "User Created."}
    session[:user_id] = @user.id
        session[:username] = @user.username
         respond_to do |format|
            format.json {render :json => @user}
            format.html{redirect_to(:controller => 'lists', :action => 'index')}
         end

  else
     flash[:notice] = {:error => @user.errors}
      respond_to do |format|
         #format.json {render :json => @user.errors}
         format.json {render :json => params[:username]} #This has a value. So the form parameters are sent.
         format.html{render('new')}
      end
  end
end

Solution

  • The problem was I needed to POST my JSON in a format that RoR was expecting. The POST data was formatted like so:

    {first_name: "somename", last_name: "somelastname", username: "someusername", etc...}
    

    My RoR controller was expecting the attributes to be wrapped in a model object like so:

    {user:{first_name: "somename", last_name: "somelastname", username: "someusername", etc...}}
    

    Using ASIFormDataRequest this can be accomplished by formatting your post parameters like this:

    [request setPostValue:first forKey:@"user[first_name]"];
    [request setPostValue:last forKey:@"user[last_name]"];
    [request setPostValue:user forKey:@"user[username]"];
    [request setPostValue:email forKey:@"user[email]"];
    [request setPostValue:password forKey:@"user[password]"];
    

    Notice the 'model_name[parameter]' format.