Я в Ruby недавно, до этого долго писал на Java. Достался внутренний сервис на Sinatra, Ruby 4.0, около тридцати роутов под /api, и в каждом две одинаковые строки проверки ключа:
get "/api/orders" do
token = request.env["HTTP_AUTHORIZATION"].to_s.delete_prefix("Bearer ")
halt 401, json(error: "unauthorized") unless ApiKey.active.exists?(token: token)
json Order.recent.limit(50).as_json
end
post "/api/orders" do
token = request.env["HTTP_AUTHORIZATION"].to_s.delete_prefix("Bearer ")
halt 401, json(error: "unauthorized") unless ApiKey.active.exists?(token: token)
# ...
end
В Spring я бы повесил фильтр, а тут как принято? Rails не предлагать, сервис маленький и пусть таким остаётся)
В Sinatra для этого есть before с шаблоном пути и helpers. Фильтр выполняется до любого роута под /api, а хелпер доступен в самих роутах, если нужно знать, чей это ключ:
require "digest"
helpers do
def current_api_key
@current_api_key ||= begin
token = request.env["HTTP_AUTHORIZATION"].to_s.delete_prefix("Bearer ")
ApiKey.active.find_by(token_digest: Digest::SHA256.hexdigest(token))
end
end
end
before "/api/*" do
halt 401, json(error: "unauthorized") unless current_api_key
end
get "/api/orders" do
json Order.recent.limit(50).as_json
end
Заодно я поменял поиск на хэш ключа. Хранить ключи в базе открытым текстом не стоит: утечёт дамп, утекут и ключи. Если у вас пока так, это отдельная миграция, но сделать её стоит.
Если сервис вырастет, посмотри на Roda: там проверку ставят один раз на ветку r.on "api". Но ради одного фильтра переезжать не стоит
И сразу тест на запрос без заголовка, одна строчка. Если кто-то потом случайно сломает фильтр, это будет видно в CI, а не в проде