Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular $http and Rails 4 params

I'm using the rails-api gem to have just a Rails API and using Angular to power my frontend. Whenever I use $http, it will only work if I pass in params instead of data. Here's an example with trying to log in a user and create a new session:

'use strict';

app.controller('LoginCtrl', function($scope, $location, $http, tokenHandler) {
  $scope.login = function() {
    $http({
      url: 'http://localhost:3000/api/admins/sign_in',
      method: 'POST',
      params: $scope.admin
    }).success(function(data) {
      if (data.success) {
        $scope.ngModel = data.data.data;
        tokenHandler.set(data.data.auth_token);
        $location.path('/admin/blog');
      } else {
        $scope.ngModel = data;
        $scope.user.errors = data.info;
      }
    }).error(function(msg) {
      $scope.admin.errors = 'Something is wrong. Please try again.';
    });
  };
});

If instead of params I used data: { admin: $scope.admin }, Rails complains to me that params[:admin] is nil. It seems to not be coming through at all.

However, if I use params, I get this:

Started POST "/api/admins/[email protected]&password=[FILTERED]" for 127.0.0.1 at 2014-09-07 20:08:04 -0400
Processing by Admin::SessionsController#create as HTML
  Parameters: {"email"=>"[email protected]", "password"=>"[FILTERED]"}

Which I can work with. It's just weird that it seems to only work when the request is processed as HTML. When I use data, I get this:

Started OPTIONS "/api/admins/sign_in" for 127.0.0.1 at 2014-09-07 20:36:24 -0400
Processing by Admin::SessionsController#create as */*

Is it suppose to say processing by */*? I'd think it should understand it's supposed to process by json specifically.

My sessions controller looks like this:

class Admin::SessionsController < Devise::SessionsController
  skip_before_filter :verify_authenticity_token

  before_filter :authenticate_user!, except: [:create]
  respond_to :json

  # ...
end

The weird thing is I definitely got it working the first time just using data: { admin: $scope.admin }, but ever since, the params seem to never come through unless I use params: $scope.admin.

ALSO:

I'm using Devise for authentication, and I had to add this to my ApplicationController:

class ApplicationController < ActionController::API
  include ActionController::MimeResponds

  before_filter :set_cors_headers
  before_filter :cors_preflight

  private 

    def set_cors_headers
      headers['Access-Control-Allow-Origin'] = AppConfig.client['origin']
      headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS'
      headers['Access-Control-Allow-Headers'] = '*'
      headers['Access-Control-Max-Age'] = "3628800"
    end

    def cors_preflight
      head(:ok) if request.method == :options
    end
end

Anyone ever dealt with this before?

like image 662
Jared Rader Avatar asked Dec 04 '25 02:12

Jared Rader


1 Answers

I've finally got it working and while I'm still confused, I think I've got somewhere close to what the problem was: My CORS configuration in my Rails API.

From what I've learned, Angular sends data in JSON format by default. This goes through as "Content-Type:application/json;charset=UTF-8", whereas in jQuery AJAX requests, it goes through as "Content-Type:application/x-www-form-urlencoded; charset=UTF-8", and is converted to a query string using $.param(). I'll admit, I've probably heard this before, but haven't truly registered this fact and its effects until now.

In my application controller, I configured my CORS settings like so:

def set_cors_headers
  headers['Access-Control-Allow-Origin'] = AppConfig.client['origin']
  headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS'
  headers['Access-Control-Allow-Headers'] = '*'
  headers['Access-Control-Max-Age'] = "3628800"
end

def cors_preflight
  head(:ok) if request.method == :options
end

AppConfig is just an OpenStruct that tells my Rails API what origin to accept requests from. And then everything else was supposed to simply set the CORS headers.

For some reason of which I'm still not sure, this wasn't working for JSON requests. I got the above code from a tutorial using Angular and Rails, and in the case of the tutorial, they manually stripped out the asset pipeline, leaving everything else about Rails in, whereas rails-api strips out some Rails configuration. This may be why setting the CORS headers in ApplicationController wasn't working.

What did work was to use the rack-cors gem and then add this bit to development.rb:

  config.middleware.use Rack::Cors do
    allow do
      origins 'localhost:9000'
      resource '*', :headers => :any, :methods => [:get, :post, :options, :delete]
    end
  end

This tells my app to accept requests from localhost:9000, and to accept any headers. I thought I was accomplishing that with headers['Access-Control-Allow-Headers'] = '*' in my ApplicationController, but I guess not. Once I specified Rails to use those middleware settings, everything worked perfectly. My Rails API can now accept application/json from my Angular app.

If someone could fill in the gaps where I'm still confused, I'd appreciate it. But I hope this helps others.

like image 79
Jared Rader Avatar answered Dec 06 '25 15:12

Jared Rader



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!