简体   繁体   English

如何使用XMLHttpRequest对象发出发布请求?

[英]How to make a post request with XMLHttpRequest object?

I have a JavaScript snippet in client side requesting data via cross domain from another Rails API app. 我在客户端有一个JavaScript代码段,该代码段通过跨域从另一个Rails API应用程序请求数据。

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));

I wonder why adding request.setRequestHeader('Content-Type', 'application/json'); 我想知道为什么要添加request.setRequestHeader('Content-Type','application / json'); causes browser to send a POST request , but without this statement, browser sends an OPTIONS request instead? 导致浏览器发送POST请求 ,但是如果没有此语句,浏览器将发送OPTIONS请求

In addition, I got an error ActionController::ParameterMissing - param is missing or the value is empty: event: in my Rails app when running the code above. 另外,在运行上面的代码时,在我的Rails应用程序中,我收到一个错误ActionController :: ParameterMissing-参数丢失或值为空:event :。 Why does browser not send the data set in myData variable? 为什么浏览器不发送myData变量中的数据集?

Version info: ruby 2.1.5p273, Rails 4.1.8 版本信息:ruby 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'
      ...

Browser send OPTIONS request for every cross domain ajax post request to check if request is safe or not. 浏览器针对每个跨域ajax发布请求发送OPTIONS请求,以检查请求是否安全。 To read more about it search about 'cors'. 要了解更多信息,请搜索“ cors”。

To fix your problem remove 'set_header' before_filter from controller and 要解决您的问题,请从控制器中删除'set_header'before_filter

  1. Add gem 'rack-cors' to Gemfile 将gem'rack-cors'添加到Gemfile
  2. bundle install 捆绑安装
  3. Add following to config/application.rb 在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 

I would use jQuery when executing this since it is on all the pages with Rails by default already. 我将在执行此操作时使用jQuery,因为默认情况下它已在所有带有Rails的页面上。 You could do $.post('/api/events', myData) it will handle a bunch of the manual work for you and make it work across multiple browsers. 您可以执行$.post('/api/events', myData) ,它将为您处理大量手动工作,并使它可以在多个浏览器中工作。 On a side note, you don't need to wrap the key 's in quotes. 附带一提,您无需key括在引号中。

CORS requires a preflight request for non- simple requests. 对于非简单请求,CORS需要一个预检请求。 The preflight request uses the OPTIONS method. 预检请求使用OPTIONS方法。 A POST request with unspecified Content-Type is not simple, because a user agent that doesn't conform to CORS will not normally make such a request. 具有未指定Content-Type POST请求并不简单,因为不符合CORS的用户代理通常不会发出这样的请求。

A simple POST request can only use certain Content-Type values. 一个简单的POST请求只能使用某些Content-Type值。 Roughly, these correspond to the types that a <form> element could send. 大致上,这些对应于<form>元素可以发送的类型。 One of these (recently added) is application/json . 其中之一 (最近添加)是application/json

So if you set Content-Type: application/json , the browser won't need to do a preflight request. 因此,如果您设置Content-Type: application/json ,则浏览器将不需要执行预检请求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM