Reputation: 60
I am trying to IP whitelist access to a particular file by placing a constraint on the file's route.
## config/routes.rb
get 'SOME FILE',
to: 'main#(function that renders the file as plain)',
constraints: <IP CONSTRAINT CLASS>.new
## (ip constraint class).rb
require 'ipaddr'
WHITELIST = %w(8.8.8.8/27).freeze
def matches?(request)
request && APPLE_IP_WHITELIST.reduce(false) do |match, range|
match || IPAddr.new(range).include?(request.remote_addr)
end
end
For IPs outside of the whitelist, a RoutingError is thrown. I try to handle the error by using a catch all route.
## config/routes.rb
get '*unmatched_route', :to => 'application#show_not_found', via: :all
## app/controllers/application_controller.rb
def show_not_found
render_error_status(404)
end
def render_error_status(status)
@status = status
template = case @status
when 400, 401, 403, 405
'application/errors/standard'
when 404, 422
'application/errors/with_block'
when 500
'application/errors/custom_500'
end
render template: template, layout: 'error', status: @status
end
Interestingly, when I try to access some nonsense route, like 'localhost:3000/asdf', the proper 404 page (application/errors/with_block.html.haml) is shown. However, when the whitelist RoutingError is handled, I get a
ActionView::MissingTemplate (Missing template application/errors/with_block with {:locale=>[:en], :formats=>[:text], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :haml, :coffee]}. Searched in: * "... /app/views" * "... /.rbenv/versions/2.5.7/lib/ruby/gems/2.5.0/gems/kaminari-0.17.0/app/views" ):
tldr: how do I handle RoutingError resulting from a constraint failing?
Edit: it appears that formats: [:html] needs to added when calling render - but why is this necessary now and not the other times when render_error_status is called?
Upvotes: 0
Views: 134
Reputation: 6418
The main issue is with the request type, when the routing constraint fails Rails tries to find file with text format, see the error:
ActionView::MissingTemplate (Missing template application/errors/with_block with {:locale=>[:en], :formats=>[:text]
This is because you are requesting a text file from the route which cannot be returned because your routing constraint failed. So when the request type is text
the error page should also be in that format. For example if I have an API where I send a JSON
request and if there is an issue of permission and I provide HTML
response then it will be of no use to the API. So the request and response format should be same in Rails when using the default conventions.
Your method should have different response types like this:
def render_error_status(status)
status = status
template = case status
when 400, 401, 403, 405
'application/errors/standard'
when 404, 422
'application/errors/with_block'
when 500
'application/errors/custom_500'
end
respond_to do |format|
format.html { render template, status: status }
format.text { render text: 'Not found', status: :not_found }
end
end
There are other options too available to do this like responding with HTML in case of text request but the standard Rails way is like this.
Upvotes: 1