替换转义的外壳字符串?



我想用转义的外壳字符串替换 ruby 变量。

这是我的开始,它有效:

<<~`SHELL`
curl -s "#{CANVAS_URI}/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions" 
-X POST 
-H "Content-Type: application/json" 
-d '{ "question": { "question_type": "multiple_choice_question", "question_text": "<p>Is everything clear?</p>", "points_possible": 1, "answers": [ { "text": "I read and understood", "html": "", "weight": 100 }, { "text": "I have questions", "comments_html": "<p>Please post your questions to a discussion (in course navigation).</p>", "weight": 0 } ] } }' 
-H "Authorization: Bearer #{CANVAS_TOKEN}"
SHELL

但是我希望discussion(在-d JSON中(是一个链接。并且无法让它工作:

myJson = %Q|{ "question": { "question_type": "multiple_choice_question", "question_text": "<p>Is everything clear?</p>", "points_possible": 1, "answers": [ { "text": "I read and understood", "html": "", "weight": 100 }, { "text": "I have questions", "comments_html": "<p>Please post your questions to a <a href="#{CANVAS_URI}/courses/#{COURSE_ID}/discussion_topics">discussion</a>.</p>", "weight": 0 } ] } }|
<<~`SHELL`
curl -s "#{CANVAS_URI}/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions" 
-X POST 
-H "Content-Type: application/json" 
-d '#{myJson}' 
-H "Authorization: Bearer #{CANVAS_TOKEN}"
SHELL
<<~`SHELL`
curl -s "#{CANVAS_URI}/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions" 
-X POST 
-H "Content-Type: application/json" 
-d "#{myJson}" 
-H "Authorization: Bearer #{CANVAS_TOKEN}"
SHELL

我被困在逃脱两个郎之间。

整个事情是在 ruby 中发布嵌套 json 的解决方法。平面 jsons 适用于大多数 http gem,但获取嵌套 json 是一种罕见的需求,而且恐怕大多数 gem 都没有针对它进行测试(我记得尝试过 http.rb 来做到这一点,但即使是 json 数组在那里也不是微不足道的。

要解决你的问题,你必须小心地用Shellwords.escape逃避所有内容,然后不要引用它们。

<<~`SHELL`
curl -s "#{CANVAS_URI}/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions" 
-X POST 
-H "Content-Type: application/json" 
-d #{Shellwords.escape(myJson)} 
-H "Authorization: Bearer #{CANVAS_TOKEN}"
SHELL

但这很乏味且容易出错,并不是您的问题。

整个事情是在 ruby 中发布嵌套 json 的解决方法。平面 jsons 在大多数 http gem 中都有效,但获取嵌套 json 是一种罕见的需求,恐怕大多数 gem 都没有针对它进行测试......

嵌套 JSON 并不罕见,无论如何,http 客户端应该没有理由关心 JSON 的内容。它只是传输一根字符串。

您的 JSON 格式不正确。具体在这里:

"comments_html": "<p>Please post your questions to a <a href="#{CANVAS_URI}/courses/#{COURSE_ID}/discussion_topics">discussion</a>.</p>"

你有一个无法逃避的报价。这可能是因为您手动将嵌套的 JSON 放在一起。就像掏出卷曲一样,这很容易出错。


相反,创建一个 Ruby 哈希并将其转换为 JSON。然后你就可以得到Ruby语法检查的所有好处。

payload = {
question: {
question_type: "multiple_choice_question",
question_text: "<p>Is everything clear?</p>",
points_possible: 1,
answers: [
{
text: "I read and understood", 
html: "", 
weight: 100
}, {
text: "I have questions",
comments_html: %Q[<p>Please post your questions to a <a href="#{CANVAS_URI}/courses/#{COURSE_ID}/discussion_topics">discussion</a>.</p>],
weight: 0
}
]
}
}
require "json"
json_payload = JSON.generate(payload)

而不是调用curl,使用 http 库。由于您正在调用 REST API,因此您可以使用 RestClient。

require "rest-client"
response = RestClient.post(
"#{CANVAS_URI}/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions",
json_payload,
{ content_type: :json, authorization: "Bearer #{CANVAS_TOKEN}" }
)

或者,更好的是,使用 canvas-api gem,它负责 JSON 转换,并可以利用分页等 API 功能。

canvas = Canvas::API.new(:host => CANVAS_URI, :token => CANVAS_TOKEN)
response = canvas.post(
"/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions",
payload
)

我认为你采取了错误的方法。将字符串内插与反引号或Kernel#system的单参数版本混合在一起是混乱和危险的。你最好完全绕过 shell 及其引用问题,并使用多参数形式的system

system(
'curl',
'-s', "#{CANVAS_URI}/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions",
'-X', 'POST',
'-H', "Content-Type: application/json",
'-d', myJson,
'-H', "Authorization: Bearer #{CANVAS_TOKEN}"
)

这将使用提供的参数直接执行curl,而无需涉及 shell。如果需要保存curl的响应,请使用标准库中的Open3,并再次避免 shell 及其引用问题。

最新更新