使用Rails API的参数发送Angular post请求

Rob*_*ber 20 angularjs ruby-on-rails-4

我无法将Angular前端与Rails后端API完全集成.它们都运行在不同的服务器上,所以我认为我的CORS存在问题.

我的Angular应用程序正在运行一个控制器,该控制器正在调用具有查询(GET)和保存(POST)方法的资源的服务.查询(GET)工作正常,但帖子不起作用.

当我不发送任何参数时,我能够向服务器发送POST请求.像这样:

控制器:

$scope.createBusiness = function() {

        console.log("Business.name=" + $scope.business.name);           
        $scope.business = Business.save(); 
    };
Run Code Online (Sandbox Code Playgroud)

服务:

 .factory('Business', 

  function($resource){          
    var businesses =
     $resource('http://127.0.0.1\\:3000/:business', {business:'businesses'}, {      
        query: {method:'GET', isArray: true},
        save: {method:'POST', isArray: false}
     });        
     return businesses;           
  }
Run Code Online (Sandbox Code Playgroud)

);

但是,我想发布我的模型参数,所以当我尝试发送内容时,我不再发送POST请求,而是发送OPTIONS请求.我收到一个错误.

请在发送不带参数的请求时查看我的请求数据(POST请求):

Request URL:http://127.0.0.1:3000/businesses
Request Method:POST
Status Code:200 OK
Request Headersview source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:es-ES,es;q=0.8
Connection:keep-alive
Content-Length:0
Content-Type:text/plain;charset=UTF-8
Host:127.0.0.1:3000
Origin:http://localhost:1234
Referer:http://localhost:1234/app/index.html
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
Response Headersview source
Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept, Authorization
Access-Control-Allow-Methods:POST, PUT, DELETE, GET, OPTIONS
Access-Control-Allow-Origin:*
Access-Control-Max-Age:1728000
Access-Control-Request-Method:*
Cache-Control:max-age=0, private, must-revalidate
Connection:Keep-Alive
Content-Length:9
Content-Type:application/json; charset=utf-8
Date:Mon, 04 Nov 2013 16:50:33 GMT
Etag:"ccd3d779b6f97e2c24633184cbc8f98c"
Server:WEBrick/1.3.1 (Ruby/2.0.0/2013-06-27)
X-Content-Type-Options:nosniff
X-Frame-Options:SAMEORIGIN
X-Request-Id:e084295e-c7c6-4566-80d1-6e2a8ac2e712
X-Runtime:0.034000
X-Ua-Compatible:chrome=1
X-Xss-Protection:1; mode=block
Run Code Online (Sandbox Code Playgroud)

我到达服务器,执行方法并获得响应!还行吧.

并且,当我发送带有参数的请求(OPTIONS请求)时,请查看我的请求数据:

Request URL:http://127.0.0.1:3000/businesses
Request Method:OPTIONS
Status Code:404 Not Found
Request Headersview source
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:es-ES,es;q=0.8
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:127.0.0.1:3000
Origin:http://localhost:1234
Referer:http://localhost:1234/app/index.html
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
Response Headersview source
Connection:Keep-Alive
Content-Length:131852
Content-Type:text/html; charset=utf-8
Date:Mon, 04 Nov 2013 16:54:04 GMT
Server:WEBrick/1.3.1 (Ruby/2.0.0/2013-06-27)
X-Request-Id:25705159-fbfb-4830-a0f1-6610fa09b70e
X-Runtime:0.371000
Run Code Online (Sandbox Code Playgroud)

UPDATE

添加模型参数时,我忘了添加控制器:

$scope.createBusiness = function() {

        console.log("Business.name=" + $scope.business.name);           
        $scope.business = Business.save($scope.business); 
    };
Run Code Online (Sandbox Code Playgroud)

我有几个视图,有几种形式,所以,我不想只发布一个表单,而是我在范围内的业务对象模型(我填写了所有表单的数据).

UPDATE

这是我的Rails Application_Controller(CORS配置):

class ApplicationController < ActionController::Base


 # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  # OJOJOOJO: Rober: I have commented this line which is provided by default with rails and added all code below in order to 
  # add CSRF protection 
  #protect_from_forgery with: :exception

  protect_from_forgery

  before_filter :cors_preflight_check
  after_filter :cors_set_access_control_headers, :set_csrf_cookie_for_ng

# For all responses in this controller, return the CORS access control headers.

  def cors_set_access_control_headers
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
    headers['Access-Control-Request-Method'] = '*'
    headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
    headers['Access-Control-Max-Age'] = "1728000"
  end

# If this is a preflight OPTIONS request, then short-circuit the
# request, return only the necessary headers and return an empty
# text/plain.

  def cors_preflight_check
    if request.method == :options
      headers['Access-Control-Allow-Origin'] = '*'
      headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
      headers['Access-Control-Request-Method'] = '*'
      headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
      headers['Access-Control-Max-Age'] = '1728000'
      render :text => '', :content_type => 'text/plain'
    end
  end

  def set_csrf_cookie_for_ng
    cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
  end

protected

  def verified_request?
    super || form_authenticity_token == request.headers['X_XSRF_TOKEN']
  end  
end
Run Code Online (Sandbox Code Playgroud)

Rob*_*ber 18

我终于成功了.

我将在Angular和Rails 4中解释我的所有配置,因为我希望阅读它.

  1. app.js:

    angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'myApp.controllers', 'myApp.i18n', 'demo']).
    config(['$routeProvider', '$httpProvider', function($routeProvider, $httpProvider) {
    
    $httpProvider.defaults.useXDomain = true;
    delete $httpProvider.defaults.headers.common["X-Requested-With"];           
    
    Run Code Online (Sandbox Code Playgroud)

    }]);

注意:当然,您不需要包含我的所有模块.

  1. 在我看来:

    <form novalidate="" name="createBusinessForm" ng-submit="setBusinessInformation()" class="css-form">            
            <label>* {{'BUSINESS_NAME' | translate}}</label>
            <input type="text" name="name" ng-model="business.name" class="input-xxlarge input-height-large" placeholder="{{'BUSINESS_NAME_PLACEHOLDER' | translate}}" required maxlength="80">
            <span ng-show="createBusinessForm.name.$dirty && createBusinessForm.name.$error.required" class="text-error">Mandatory field.</span>
            <label>* {{'ID' | translate}}</label>    
            <input type="text" ng-model="business.cif_nif" class="input-xlarge input-height-large" placeholder="{{'BUSINESS_ID_PLACEHOLDER' | translate}}" required maxlength="60">        
            <label>* {{'ADDRESS' | translate}}</label>    
    
    Run Code Online (Sandbox Code Playgroud)

注意:您可以根据需要定义任意数量的字段,并使用数据bingind将值分配给对象.

  1. 在我的控制器中:

    $scope.createBusiness = function() {
    
            $scope.business.type = $scope.type; 
            $scope.business.plan = $scope.plan;     
            $scope.business = Business.save($scope.business);           
            $location.path('/user-dashboard');                              
        }; 
    
    Run Code Online (Sandbox Code Playgroud)

注意:您需要在post请求中发送的所有属性.除了表单之外,您还可以在发送到Rails API之前为对象分配一些新属性.我们将使用带有资源对象的服务来发送POST请求.

  1. 在我的服务中:

    .factory( '商务',

      function($resource){          
        var businesses =
         $resource('http://127.0.0.1\\:3000/:business', {business:'businesses'}, {      
            query: {method:'GET', isArray: true},
            save: {method:'POST', isArray: false}
         });        
         return businesses;           
      }
    
    Run Code Online (Sandbox Code Playgroud)

    );

注意:我有一个GET请求,要通过Rails API和POST来从DB获取业务.

Rails 4

重要

  1. 的routes.rb

    match "/businesses" => "application#index", via: :options  
    
    Run Code Online (Sandbox Code Playgroud)

注意:与Angular服务器将发送的OPTIONS请求匹配的新条目预先取消发送POST请求的开始.

  1. application_controller.rb

    class ApplicationController < ActionController::Base
    
    
    before_filter :set_headers
    
    def index
      puts "Do nothing."
      render nothing: true
    end
    
    def set_headers
      puts 'ApplicationController.set_headers'
      if request.headers["HTTP_ORIGIN"]     
      # better way check origin
      # if request.headers["HTTP_ORIGIN"] && /^https?:\/\/(.*)\.some\.site\.com$/i.match(request.headers["HTTP_ORIGIN"])
        headers['Access-Control-Allow-Origin'] = request.headers["HTTP_ORIGIN"]
        headers['Access-Control-Expose-Headers'] = 'ETag'
        headers['Access-Control-Allow-Methods'] = 'GET, POST, PATCH, PUT, DELETE, OPTIONS, HEAD'
        headers['Access-Control-Allow-Headers'] = '*,x-requested-with,Content-Type,If-Modified-Since,If-None-Match,Auth-User-Token'
        headers['Access-Control-Max-Age'] = '86400'
        headers['Access-Control-Allow-Credentials'] = 'true'
      end
    end               
    end
    
    Run Code Online (Sandbox Code Playgroud)
  2. 我的Rails控制器

    def create
      puts 'Businesses_controller.create!!!!!'
      puts business_params.inspect
    
      # business_type object is recovered from db
      businessTypeName = params[:type]    
      businessType = BusinessType.where(:name => businessTypeName).first
      ...
    end
    
    Run Code Online (Sandbox Code Playgroud)

注意:这里做任何你需要的......

  • 有一种更简洁的方法可以做到这一点,即使用 rack-cors gem 而不是对标题进行硬编码。查看@matthew-coarr 的最新答案 (2认同)

Mat*_*arr 14

我们遇到了同样的问题,采取了类似但希望更简单/更快/更灵活的方法.

我所做的快速概述是使用ruby库"rack-cors"(https://github.com/cyu/rack-cors)来管理所有CORS头文件.

这意味着我不必在我的代码中粘贴一堆硬编码的头名称/值.

对我来说最大的好处是,它既可以处理简单的CORS请求(GET请求和响应),也可以处理使用OPTION请求的预先请求(OPTIONS请求和响应,然后是POST请求和响应).

以下是我遵循的快速步骤:

  1. 宝石安装机架
  2. 将以下内容添加到Gemfile:

    gem 'rack-cors', :require => 'rack/cors'
    
    Run Code Online (Sandbox Code Playgroud)
  3. 运行"bundle install",它将更新Gemfile.lock

  4. 编辑config/application.rb以添加以下块:

    config.middleware.insert_before "ActionDispatch::Static", "Rack::Cors", :debug => true, :logger => Rails.logger do
      allow do
        origins '*'
    
        resource '*',
          :headers => :any,
          :methods => [:get, :post, :delete, :put, :options],
          :max_age => 0
      end
    end
    
    Run Code Online (Sandbox Code Playgroud)

现在在我的情况下,我只想打开任何主机,但你可能会更严格.您也可以限制标头和http方法.

在github页面上查看readme的更多详细信息:https://github.com/cyu/rack-cors (rack-cors作者在examples/rails3和examples/rails4下有示例rails应用程序)