tgf
tgf

Reputation: 3266

ActiveStorage service_url && rails_blob_path cannot generate full url when not using S3

I have a basic ActiveStorage setup with one model that has_many_attached :file_attachments. In a service elsewhere I'm trying to generate a link to be used outside the main app (email, job etc).

With S3 in production I can do: item.file_attachments.first.service_url and I get an appropriate link to the S3 bucket+object.

I cannot use the method prescribed in the rails guides: Rails.application.routes.url_helpers.rails_blob_path(item.file_attachments.first)

It errors with: ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true I can pass it a host: 'http://....' argument and it's happy although it still doesn't generate the full URL, just the path.

In development I'm using disk backed file storage and I can't use either method:

> Rails.application.routes.url_helpers.rails_blob_path(item.file_attachments.first)
ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true

Setting host here also doesn't generate a full URL.

In production service_url works, however here in development I get the error:

> item.file_attachments.first.service_url
ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true

and specifying a host doesn't help:

item.file_attachments.first.service_url(host:'http://localhost.com')
ArgumentError: unknown keyword: host

I've also tried adding

config.action_mailer.default_url_options = { :host => "localhost:3000" }
config.action_storage.default_url_options = { :host => "localhost:3000" }
Rails.application.routes.default_url_options[:host] = 'localhost:3000'

with no success.

My question is - how can I get the full URL in a manner that works in both development and production? or where do I set the host at?

Upvotes: 25

Views: 22047

Answers (4)

mechnicov
mechnicov

Reputation: 15288

In Rails 7.1 ActiveStorage::Current#host and ActiveStorage::Current#host= methods are removed

But it's possible to use ActiveStorage::SetCurrent module, it sets ActiveStorage::Current.url_options before action, so just enough to include in your controller (for example in ApplicationController)

include ActiveStorage::SetCurrent

This module source:

module ActiveStorage::SetCurrent
  extend ActiveSupport::Concern

  included do
    before_action do
      ActiveStorage::Current.url_options = { protocol: request.protocol, host: request.host, port: request.port }
    end
  end
end

Of course you can set these options yourself

ActiveStorage::Current.url_options = { host: 'my.host' } # and other options

Upvotes: 1

stevec
stevec

Reputation: 52328

I needed the url for an image stored in ActiveStorage.

The image was

Post.first.image

Wrong

Post.first.image.url
Post.first.image.service_url

Right

url_for(Post.first.image)

Upvotes: 5

George Claghorn
George Claghorn

Reputation: 26535

Active Storage’s disk service expects to find a host for URL generation in ActiveStorage::Current.host.

When you call ActiveStorage::Blob#service_url manually, ensure ActiveStorage::Current.host is set. If you call it from a controller, you can subclass ActiveStorage::BaseController. If that’s not an option, set ActiveStorage::Current.host in a before_action hook:

class Items::FilesController < ApplicationController
  before_action do
    ActiveStorage::Current.host = request.base_url
  end
end

Outside of a controller, use ActiveStorage::Current.set to provide a host:

ActiveStorage::Current.set(host: "https://www.example.com") do
  item.file_attachments.first.service_url
end

Upvotes: 47

Motine
Motine

Reputation: 1862

I had a similar problem. I had to force the port to be different due to a container setup. In my case the redirect done by ActiveStorage included the wrong port. For me adjusting the default_url_options worked:

before_action :configure_active_storage_for_docker, if: -> { Rails.env.development? }

def configure_active_storage_for_docker
  Rails.application.routes.default_url_options[:port] = 4000
end

Upvotes: 0

Related Questions