通过Javascript POST请求将JSON对象发送到Rails控制器

时间:2018-04-02 13:36:58

标签: javascript ruby-on-rails rspec vue.js capybara

我试图在使用Vue.js和Dropzone的Rails 5.1应用程序中提交表单。在sendingEvent期间,我在对象上使用JSON.stringify,然后将其发送给控制器。但是,我不觉得这是正确的方法,因为我在控制器中使用强params时遇到了问题。

JS

import Vue from 'vue/dist/vue.esm'
import VueResource from 'vue-resource'
import vue2Dropzone from 'vue2-dropzone'

Vue.use(VueResource)

document.addEventListener('DOMContentLoaded', () => {
  if(document.getElementById('listing-multistep') !== null) {
    Vue.http.headers.common['X-CSRF-Token'] = document.querySelector('input[name="authenticity_token"]').getAttribute('value');
    var listingForm = document.getElementById('listing_form');
    var listing = JSON.parse(listingForm.dataset.listing);
    var locale = document.getElementsByTagName('html')[0].getAttribute('lang');
    const myForm = new Vue({
      el: '#listing-multistep',
      components: {
        vueDropzone: vue2Dropzone
      },
      data: function () {
        return {
          id: listing.id,
          locale: locale,
          slug: listing.slug,
          activeStep: 0,
          // More data
          dropzoneOptions: {
            url: `/${locale}/listings`,
            method: 'post',
            acceptedFiles: 'image/*',
            uploadMultiple: true,
            autoProcessQueue: false,
            parallelUploads: 15,
            maxFiles: 15,
            addRemoveLinks: true,
            thumbnailWidth: 150,
            maxFilesize: 5,
            dictDefaultMessage: "<i class='fa fa-cloud-upload'></i> Drop files here to upload (max. 15 files)",
            headers: { 'X-CSRF-Token': Vue.http.headers.common['X-CSRF-Token'] }
          }
        }
      },
      methods: {
        sendingEvent: function(file, xhr, formData) {
          // This function gets called by Dropzone upon form submission.
          var listingObj = this.setupListingObj()
          formData.append('listing', JSON.stringify(listingObj))
        },
        listingRedirect: function(files, response) {
          window.location = `/${this.locale}/listings/${response.slug}`
        },
        submitListing: function() {
          var numFiles = this.$refs.listingDropzone.getAcceptedFiles().length
          // If there are images to upload, use Dropzone
          // Else submit the form normally.
          if(numFiles > 0) {
            this.$refs.listingDropzone.processQueue()
          } else {
            var listingObj = this.setupListingObj()

            if(this.id === null) {
              // POST if it's a new listing
              this.$http.post(`/${this.locale}/listings`, {listing: listingObj}).then(
                response => {
                  window.location = `/${this.locale}/listings/${response.body.slug}`
              }, response => {
                console.log(response)
              })
            } else {
              // PUT if it's an existing listing
              this.$http.put(`/${this.locale}/listings/${this.slug}`, {listing: listingObj}).then(
                response => {
                  window.location = `/${this.locale}/listings/${response.body.slug}`
              }, response => {
                console.log(response)
              })
            }
          }
        },
        setupListingObj: function() {
          // do some processing...

          var listingObj = {
            id: this.id,
            name: this.name,
            // set more attributes
          }
          return listingObj
        },
      }
  }
});

正如您所看到我在formData.append('listing', JSON.stringify(listingObj))上使用sendingEvent

我的控制器:

class ListingsController < ApplicationController

  def create
    @listing = Listing.new JSON.parse(params[:listing])
    @listing.owner = current_user
    respond_to do |format|
      if @listing.save
        format.html { redirect_to listing_path(@listing), notice: 'Listing was created successfully!' }
        format.json { render :show, status: :created, location: @listing }
      else
        format.html { render :new }
        format.json { render json: @listing.errors, status: :unprocessable_entity }
      end
    end
  end

  private

  def listing_params
    params.require(:listing).permit(
      :name,
      :bedrooms,
      :beds,
      :bathrooms,
      :price_cents,
      :price_currency,
      :property_type,
      :city,
      :state,
      :address,
      :lat,
      :lng,
      :description,
      :amenities => []
    )
  end
end

它似乎在开发中工作,但是当我在RSpec中使用此代码运行测试时,我得到的错误如下:

Internal Server Error no implicit conversion of ActionController::Parameters into String

当我尝试交换@listing = Listing.new JSON.parse(listing_params)时,它无法在开发中工作。

我有一种感觉,我没有正确发送表单数据。通过Javascript将数据发送到我的Rails控制器的正确方法是什么?是否需要进行调整然后发布?我怎样才能通过强对数来访问它?

提前致谢!

更新

这是我的规范:

RSpec.feature 'Listing owners can create new listings' do
  let(:owner) { create(:user, :owner) }

  before do
    login_as owner
    visit new_listing_path
  end

  scenario 'successfully', js: true do
    fill_in 'Property name', with: 'Example property'
    select 'Apartment', from: 'Property type'
    fill_in 'Address', with: 'Somewhere'
    click_button 'Next'

    fill_in 'Property description', with: Faker::Lorem.paragraph(2)
    click_button 'Create Listing'

    expect(page).to have_content 'Listing was created successfully!'
  end
end

我正在使用Chrome无头进行这些测试,以便解析表单上的Vue.js内容。在我的 rails_helper.rb 中,我有:

require 'spec_helper'
require 'rspec/rails'
require 'capybara/rails'
require 'capybara/rspec'
require 'pundit/rspec'
require 'selenium/webdriver'

RSpec.configure do |config|
  # ...
  Capybara.javascript_driver = :headless_chrome
end

我有 support / chrome_driver.rb 文件,其中包含以下内容:

Capybara.register_driver(:headless_chrome) do |app|
  capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
    chromeOptions: { args: %w[headless disable-gpu] }
  )

  Capybara::Selenium::Driver.new(
    app,
    browser: :chrome,
    desired_capabilities: capabilities
  )
end

0 个答案:

没有答案