Search code examples
rubyroutesportal

Add a prefix to generated links, but not to incoming routes


Our Rails 4 application needs to be accessible over an archaic portal. This portal works by adding (from the perspective of the browser) a prefix to each URL; this prefix is removed by the portal before forwarding the request to my application.

So the browser calls https://portal.company.com/portal/prefix/xyzzy/myapp/mymodel/new; the portal does its thing and requests https://myserver.company.com/myapp/mymodel/new (passing along the stripped prefix in some irrelevant way). The prefix is dynamic and can change between requests.

The problem is that the portal is not able to rewrite the HTML pages served by my application. That is, it does not put in the prefix. It expects applications to either only emit relative URLs, or to add the portal prefix themselves.

So:

  • A regular URL /myapp/mymodel/new, for example, must stay as is for when the application is accessed directly (for certain users which do not use the portal).
  • When accessed over the portal, our application must still understand /myapp/mymodel/new as usual, but when using mymodel_new_path or link_to @mymodel or form_for @my_model or whatever other magic URL generators there are, it has to add the portal prefix. So, any URL emitted by the application must look like /portal/prefix/xyzzy/myapp/mymodel/new where the per-request string /portal/prefix/xyzzy is given by some method defined by us (and the part xyzzy can change between requests).

How can I achieve that? My routes.rb looks like this today:

MyApp::application.routes.draw do
  scope ' /myapp' do
    get ...

This probably has to stay as is, because URLs in incoming requests do not change when coming from the portal. But how do I influence the outgoing URLs?


Solution

  • This suggestion will allow you to easily prefix the urls produced by the Rails path helpers as your require. Do note, however, it will also make these extended paths valid requests for your application - they shoud just route where expected but you'll get get some extra values in the params hash that you can ignore, so I suspect this is possibly acceptable.


    First, add all the prefix bits as optional parameters to your routes' base scope:

    scope '(:portal/)(:prefixA/)(:prefixB)/myapp' do
      # routes
    end
    

    Note that the those optional params cannot include the / char without it being escaped by the path helpers, so if you have a few levels in the prefix (which it appears you do in the question) you'll need a few different params, all but the last followed by a slash, as above.

    With that done, you should define default_url_options in your ApplicationController, it should return a hash of the values you need in your routes:

    def default_url_options(_options={})
      {
        portal:  'portal',
        prefixA: 'whatevertheprefixis',
        prefixB: 'nextbitoftheprefix'
      }
    end
    

    And that should do it, path helpers (along with link_to @object etc) should all now include those values every time you use them.

    Note that since the portal bit at the start is also an optional parameter, you can simply add additional logic to default_url_options and have it return an empty hash whenever you do not want this prefixing behaviour.