33

I have a series of resources that I want only available if accessed via the JS format. Rails' route resources gives me the formats plus the standard HTML. Is there a way to specify that only the JS format routes be created?

Eric M.
  • 5,399
  • 6
  • 41
  • 67

7 Answers7

65

You must wrap those routes in a scope. Constraints unfortunately don't work as expected in this case.

This is an example of such a block...

scope :format => true, :constraints => { :format => 'json' } do
  get '/bar' => "bar#index_with_json"
end

More information can be found here: https://github.com/rails/rails/issues/5548

koonse
  • 1,517
  • 1
  • 14
  • 16
  • 4
    If you're using `resources`, you don't need a scope block, just add the `:format => true` and `:constraints => ...` directly to the `resources` call. – Nathan Nov 26 '14 at 00:11
  • This worked in my case for resourcefull route. `resources :photos, format: true, constraints: 'json'` – maicher Dec 24 '14 at 11:41
  • 2
    Unfortunately, it seems that this requires the url to have the file extension on it – stephen.hanson Jan 31 '15 at 06:55
  • 3
    @steve.hanson to avoid the format requirement in the URL, use a lambda for constraint: `get :foo, constraints: lambda { |req| req.format == :json }`. – RocketR May 17 '17 at 12:00
  • The problem is when I wrap the route in a scope block, other code in my rails fails because the link_to command cant find this wrapped route anymore – Nathan B Jul 21 '21 at 07:06
  • 1
    Reiterating the comment by @RocketR above, **this solution does not work**, and [it's mentioned in the rails routing documentation](https://guides.rubyonrails.org/routing.html). (See the callout starting "There is an exception for the format constraint...") You need to use a lambda for a route format constraint, not a plain hash: `constraints: lambda { |req| req.format == :json }`, or equivalently, `constraints: ->(req) { req.format == :json }` – Tom Lord Feb 23 '23 at 16:20
18

You just add constraints about format :

resources :photos, :constraints => {:format => /(js|json)/}
shingara
  • 46,608
  • 11
  • 99
  • 105
  • Unless I'm doing something wrong, that still allows me to access /photos as :html. I get the missing template message, when I'd expect a missing route exception. Thoughts? – Eric M. Sep 09 '10 at 20:51
  • Shouldn't that be `/(js|json)/`? – Garrett Sep 09 '10 at 20:55
  • Yeah, I caught that and changed it. Still doesn't work for me. I have resources :members, :controller => 'homes/members', :constraints => {:format => /js/} – Eric M. Sep 09 '10 at 21:29
  • 4
    this will not limit requests to those formats, see my answer below for the correct implementation – koonse Feb 06 '13 at 07:32
8

None of the above solutions worked for me. I ended up going with this solution:

post "/test/suggestions", to: "test#suggestions", :constraints => -> (req) { req.xhr? }

Found on https://railsadventures.wordpress.com/2012/10/07/routing-only-ajax-requests-in-ror/#comment-375

user2031423
  • 347
  • 4
  • 7
1

how about

# routes.rb

class OnlyAjaxRequest
  def matches?(request)
    request.xhr?
  end
end

post "/test/suggestions", to: "test#suggestions", :constraints => OnlyAjaxRequest.new

it doesn't get to the controller at all. Taken from railsadventures

Giugrilli
  • 93
  • 1
  • 7
0

If you need not only one or another than json (cant use #xhr?) I offer to you option below

resource :offers, only: :show, format: true, constraints: { format: 'pdf' }

Hope it helps

Neodelf
  • 185
  • 1
  • 14
0

That's how I do it:

class OnlyAjaxRequest  
    def matches?(request)
      request.xhr? and request.format.to_s.match(/(js|json|javascript)/).present?
    end
  end

match 'remote_login', to: 'remote_content#remote_login', via: [:get], :constraints => OnlyAjaxRequest.new

If you only care about the format, leave just the request.format part

Nathan B
  • 1,625
  • 1
  • 17
  • 15
-1

You can use a before_filter that raises a routing error unless the request format is MIME::JS.

app/controllers/application_controller.rb:

class ApplicationController < ActionController::Base
  before_filter :check_js

  private
    def check_js
      raise RoutingError.new('expected application/json') unless request.format == MIME::JS
    end
end

Apply this filter more surgically with :only, :except, and :skip_before_filter as covered in the rails Action Controller Guide

allonhadaya
  • 1,297
  • 7
  • 19