2015-01-09 102 views
2

我在客户端有一个JavaScript代码段,通过来自另一个Rails API应用程序的跨域请求数据。如何使用XMLHttpRequest对象发布发布请求?

var myData = { "event": {"name": "some event name", "property": "some value"} }; 
var request = new XMLHttpRequest(); 
request.open("POST", "http://localhost:3000/api/events", true); 
request.setRequestHeader('Content-Type', 'application/json'); 
request.send(JSON.stringify(myData)); 

不知为什么添加request.setRequestHeader( '内容 - 类型', '应用/ JSON');导致浏览器发送POST请求,但没有此声明,浏览器发送OPTIONS请求而不是?

此外,我得到了一个错误ActionController :: ParameterMissing - param丢失或值为空:事件:在我的Rails应用程序运行上面的代码时。为什么浏览器不发送myData变量中设置的数据?

版本信息:红宝石2.1.5p273来说,Rails 4.1.8

# routes.rb 

Rails.application.routes.draw do 
    match '/api/events' => 'api/v1/events#create', via: :options 
    ... 
end 

# app/controller/api/v1/events_controller.rb 

class API::V1::EventsController < ApplicationController 
    skip_before_action :verify_authenticity_token 
    before_action :set_headers 

    def index 
    @events = Event.all 
    render json: @events, status: :ok 
    end 

    def show 
    @event = Event.find(params[:id]) 
    render json: @event 
    end 

    def create 
    @event = Event.new(event_params) 
    if @event.save 
     render json: @event, status: :created 
    else 
     render @event.errors, status: :unprocessable_entity 
    end 
    end 

    def update 
    @event = Event.find(params[:id]) 
    if @event.update(event_params) 
     raise "#{@event.name}" 
     head :no_content 
    else 
     render @event.errors, status: :unprocessable_entity 
    end 
    end 

    private 

    def event_params 
    params.require(:event).permit(:name, :property) 
    end 

    def set_headers 
    headers['Access-Control-Allow-Origin'] = '*' 
    headers['Access-Control-Expose-Headers'] = 'ETag' 
    headers['Access-Control-Allow-Methods'] = 'OPTIONS' #'GET, POST, PATCH, PUT, DELETE, OPTIONS, HEAD' 
    headers['Access-Control-Allow-Headers'] = '*,x-requested-with,Content-Type,If-Modified-Since,If-None-Match' 
    headers['Access-Control-Max-Age'] = '1728000' 
    end 
end 

# Rails log 

Started OPTIONS "/api/events" for 127.0.0.1 at 2015-01-09 14:54:31 -0600 
    Processing by API::V1::EventsController#create as */* 
    Completed 400 Bad Request in 2ms 

    ActionController::ParameterMissing - param is missing or the value is empty: event: 
     actionpack (4.1.8) lib/action_controller/metal/strong_parameters.rb:187:in `require' 
     app/controllers/api/v1/events_controller.rb:39:in `event_params' 
     app/controllers/api/v1/events_controller.rb:18:in `create' 
     actionpack (4.1.8) lib/action_controller/metal/implicit_render.rb:4:in `send_action' 
     ... 

回答

3

浏览器发送OPTIONS请求给每个跨域的ajax post请求来检查请求是否安全。要阅读更多关于它搜索'cors'的信息。

解决您的问题删除 'set_header' 的before_filter从控制器和

  1. 添加宝石 '机架CORS' 来的Gemfile
  2. 捆绑安装以下到config/application.rb中
  • 添加
    config.middleware.insert_before 0, "Rack::Cors" do 
        allow do 
        origins '*' 
        resource '*', :headers => :any, :methods => [:get, :post, :options, :put, :head, :delete] 
        end 
    end 
    
  • +1

    你苏使用机架宝石来充气? – 2015-01-09 22:21:15

    +0

    是@SambathPrum,更新了答案。谢谢 – 2015-01-10 12:05:44

    1

    执行这个时候,因为它是所有使用Rails默认情况下已经页面我会使用jQuery。你可以做$.post('/api/events', myData)它会为你处理一堆手动工作,并让它在多个浏览器上工作。在附注中,您不需要需要key的引号括起来。

    1

    CORS需要针对非simple请求的预检请求。预检请求使用OPTIONS方法。未指定Content-Type的POST请求并不简单,因为不符合CORS的用户代理通常不会提出这样的请求。

    一个简单的POST请求只能使用某些Content-Type值。粗略地说,这些对应于<form>元件可以发送的类型。 One of these(最近添加的)是application/json

    因此,如果您设置Content-Type: application/json,浏览器将不需要执行预检请求。