Как создать действительный запрос прямой загрузки ActiveStorage в GCS? - PullRequest
0 голосов
/ 14 декабря 2018

Речь идет об использовании ActiveStorage с GCS для случая использования клиента API.(Rails 5.2.1, 5.2.2)

Я пишу тест, чтобы изучить, как создать запрос, имитирующий прямую загрузку в GCS, подготовленный универсальным DirectUploadsController. Этот универсальный контроллер является частью ActiveStorage.Идея состоит в том, чтобы позже реплицировать код в мобильном приложении, используя тот же бэкэнд.

Конфигурация AS хорошо работает в среде разработки, используя как загрузку через контроллеры, так и прямую загрузку с использованием интеграции JS, которая поставляетсяс AS.Вот почему я предполагаю, что конфиг должен быть в порядке.(env 'test' и 'development' используют точно такую ​​же настройку на этом этапе.)

Тестовый код находится в поле ниже.

В конечном итоге он вызывает 403 Запрещенный ответ отRestClient.put call.

В ответном сообщении содержится жалоба на несоответствие подписи, подробности ниже.Сначала тестовый код:

require 'test_helper'

class UploadControllerTest < ActionDispatch::IntegrationTest
  test "direct upload from controller prepared blob" do
    pathname = file_fixture('cube.png')
    data = pathname.binread
    content_type = "image/png"

    post rails_direct_uploads_path, params: {
      blob: {
        filename: pathname.basename,
        byte_size: pathname.size,
        checksum: Digest::MD5.base64digest(data),
        content_type: content_type
      }
    }

    assert_equal 27195, pathname.size
    assert_response :success

    json = response.parsed_body
    direct_upload = json["direct_upload"]
    signed_url    = direct_upload["url"]
    headers       = direct_upload["headers"]

    assert_equal({ "Content-MD5" => "tmBHZQCm+qBzGFEaDwmpnA==" }, headers)

    assert_match /&Signature=/, signed_url
    assert_match /&Expires=/, signed_url
    assert_match %r{^https://storage.googleapis.com}, signed_url

    response = RestClient.put(
      signed_url,
      data,
      headers.merge("Content-Type" => content_type)
    )

    assert_response :success
  rescue RestClient::Forbidden => e
    pp e.response.body
    fail "Failing with 403 Forbidden" # always ends up here
  end
end

В результате тело ответа будет следующим: XML:

  <?xml version='1.0' encoding='UTF-8'?>
  <Error>
    <Code>SignatureDoesNotMatch</Code>
    <Message>
      The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.
    </Message>
    <StringToSign>PUT\n" +
    "tmBHZQCm+qBzGFEaDwmpnA==\n" +
    "image/png\n" +
    "1544517548\n" +
  "/planprop-test-bucket/gVn9zVCumGJxiu2kU6mFWUVV</StringToSign>
  </Error>

Код ошибки:

SignatureDoesNotMatch

и сопровождающее сообщение:

Рассчитанная нами подпись запроса не соответствует предоставленной вами подписи.Проверьте свой секретный ключ Google и метод подписи.

Перечисленными частями строки подписи являются контрольная сумма (как указано выше), срок действия (часть URL-адреса), тип контента (как указано выше) иобъект (имя и ключ группы, часть URL).Так что я не вижу места, где могло бы проскальзывать несоответствие.

Что не так?

1 Ответ

0 голосов
/ 30 декабря 2018

Проблема с кодом выше в том, что отправляется заголовок Content-Type, равный application/x-www-form-urlencoded, в то время как при подписании ни один не используется.

Чтобы это работало, измените код запроса PUT на

response = RestClient.put(
  signed_url,
  data,
  headers.merge(content_type: "")
)

Использование nil все равно заставит это значение по умолчанию отображаться в запросе.

Это поведение не уникально для RestClient, кстати.но то же самое для ряда клиентов Ruby http, которые я тестировал (Фарадей, net / http, httpclient).Excon здесь исключение, он не отправляет заголовок типа контента по умолчанию без соответствующего указания.

...