Rails RoutingError来自约束失败(IP白名单文件)



我正试图通过对特定文件的路由设置约束来对其进行IP白名单访问。

## 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

对于白名单之外的IP,会抛出RoutingError。我试图用一条包罗万象的路线来处理这个错误。

## 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

有趣的是,当我尝试访问一些无意义的路由时,比如"localhost:3000/asdf",会显示正确的404页面(application/errors/with_block.html.haml(。然而,当处理白名单RoutingError时,我会得到一个

ActionView::MissingTemplate(缺少具有{:locale=>[:en]、:formats=>[:text]、:variations=>[]、:handlers=>[:raw、:erb、:html、:builder、:ruby、:haml、:coffee]}的模板应用程序/errors/with_block。搜索位置:*"…/app/views"*"…/.rbenv/versions/2.5.7/lib/ruby/gems/2.5.0/gems/kaminari-0.17.0/app/views"):

tldr:如何处理约束失败导致的RoutingError?

编辑:在调用render时,似乎需要添加formats:[:html],但为什么现在需要这样做,而在调用render_error_status的其他时候却没有?

主要问题是请求类型,当路由约束失败时,Rails试图找到文本格式的文件,请参阅错误:

ActionView::MissingTemplate (Missing template application/errors/with_block with {:locale=>[:en], :formats=>[:text]

这是因为您正在从路由请求一个文本文件,由于路由约束失败,该文件无法返回。因此,当请求类型为text时,错误页面也应为该格式。例如,如果我有一个API,在那里我发送JSON请求,并且如果存在权限问题,并且我提供HTML响应,那么它对API将没有用处。因此,当使用默认约定时,Rails中的请求和响应格式应该是相同的。

你的方法应该有不同的响应类型,比如:

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

还有其他选项可以做到这一点,比如在文本请求的情况下使用HTML进行响应,但标准的Rails方式是这样的。

最新更新