当我尝试在同一有效负载中尝试使用File参数和array参数编辑Game时,我无法弄清JavaScript如何以Rails可接受的格式发送请求。
Rails控制器看起来像这样(显然是简化的):
class GamesController < ApplicationController
def update
@game = Game.find(params[:id])
authorize @game
respond_to do |format|
if @game.update(game_params)
format.html { render html: @game, success: "#{@game.name} was successfully updated." }
format.json { render json: @game, status: :success, location: @game }
else
format.html do
flash.now[:error] = "Unable to update game."
render :edit
end
format.json { render json: @game.errors, status: :unprocessable_entity }
end
end
end
private
def game_params
params.require(:game).permit(
:name,
:cover,
genre_ids: [],
engine_ids: []
)
end
end
所以我有这样的JavaScript:
// this.game.genres and this.game.engines come from
// elsewhere, they're both arrays of objects. These two
// lines turn them into an array of integers representing
// their IDs.
let genre_ids = Array.from(this.game.genres, genre => genre.id);
let engine_ids = Array.from(this.game.engines, engine => engine.id);
let submittableData = new FormData();
submittableData.append('game[name]', this.game.name);
submittableData.append('game[genre_ids]', genre_ids);
submittableData.append('game[engine_ids]', engine_ids);
if (this.game.cover) {
// this.game.cover is a File object
submittableData.append('game[cover]', this.game.cover, this.game.cover.name);
}
fetch("/games/4", {
method: 'PUT',
body: submittableData,
headers: {
'X-CSRF-Token': Rails.csrfToken()
},
credentials: 'same-origin'
}).then(
// success/error handling here
)
当我单击表单中的“提交”按钮时,JavaScript将运行,并且应该将数据转换为Rails后端将接受的格式。不幸的是,我无法使其正常工作。
在没有要提交的图像文件的情况下,我可以使用JSON.stringify()
而不是FormData
来提交数据,例如:
fetch("/games/4", {
method: 'PUT',
body: JSON.stringify({ game: {
name: this.game.name,
genre_ids: genre_ids,
engine_ids: engine_ids
}}),
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': Rails.csrfToken()
},
credentials: 'same-origin'
})
这很好。但是我无法弄清楚在提交File对象时如何使用JSON.stringify
。另外,我可以使用FormData
对象,该对象适用于简单值,例如name
以及File对象,但不适用于ID数组之类的数组值。
在Rails控制台中,仅包含ID数组的成功表单提交(使用JSON.stringify
如下所示:
Parameters: {"game"=>{"name"=>"Pokémon Ruby", "engine_ids"=>[], "genre_ids"=>[13]}, "id"=>"4"}
但是,我当前的代码最终却是这样的:
Parameters: {"game"=>{"name"=>"Pokémon Ruby", "genre_ids"=>"18,2,15", "engine_ids"=>"4,2,10"}, "id"=>"4"}
Unpermitted parameters: :genre_ids, :engine_ids
或者,如果您还在此过程中上传文件,则:
Parameters: {"game"=>{"name"=>"Pokémon Ruby", "genre_ids"=>"13,3", "engine_ids"=>"5", "cover"=>#<ActionDispatch::Http::UploadedFile:0x00007f9a45d11f78 @tempfile=#<Tempfile:/var/folders/2n/6l8d3x457wq9m5fpry0dltb40000gn/T/RackMultipart20190217-31684-1qmtpx2.png>, @original_filename="Screen Shot 2019-01-27 at 5.26.23 PM.png", @content_type="image/png", @headers="Content-Disposition: form-data; name=\"game[cover]\"; filename=\"Screen Shot 2019-01-27 at 5.26.23 PM.png\"\r\nContent-Type: image/png\r\n">}, "id"=>"4"}
Unpermitted parameters: :genre_ids, :engine_ids
TL; DR :我的问题是,如何使用JavaScript将有效载荷(名称字符串,ID数组以及游戏封面图像)发送到Rails?实际会接受哪种格式,我该如何实现?
you can see the repo here如果有帮助,则Rails应用程序是开源的。提到的特定文件是app/controllers/games_controller.rb
和app/javascript/src/components/game-form.vue
,尽管我在此问题上都进行了显着简化。
答案 0 :(得分:1)
我发现可以使用ActiveStorage's Direct Upload feature来做到这一点。
在我的JavaScript中:
// Import DirectUpload from ActiveStorage somewhere above here.
onChange(file) {
this.uploadFile(file);
},
uploadFile(file) {
const url = "/rails/active_storage/direct_uploads";
const upload = new DirectUpload(file, url);
upload.create((error, blob) => {
if (error) {
// TODO: Handle this error.
console.log(error);
} else {
this.game.coverBlob = blob.signed_id;
}
})
},
onSubmit() {
let genre_ids = Array.from(this.game.genres, genre => genre.id);
let engine_ids = Array.from(this.game.engines, engine => engine.id);
let submittableData = { game: {
name: this.game.name,
genre_ids: genre_ids,
engine_ids: engine_ids
}};
if (this.game.coverBlob) {
submittableData['game']['cover'] = this.game.coverBlob;
}
fetch(this.submitPath, {
method: this.create ? 'POST' : 'PUT',
body: JSON.stringify(submittableData),
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': Rails.csrfToken()
},
credentials: 'same-origin'
})
}
然后我发现,通过DirectUpload的工作方式,我可以将coverBlob
变量发送到Rails应用程序,因此它只是一个字符串。超级容易。
答案 1 :(得分:0)
您可以将File
对象转换为data URL
并将该字符串包含在JSON
中,请参见Upload multiple image using AJAX, PHP and jQuery的processFiles
函数或使用{{1} },将其设置为JSON.stringify()
对象的值,而不是将Array
的值传递给FormData
。
Array