ruby on rails 3.1 - ¿Cómo uso Rack:: Proxy en Rails para enviar solicitudes a una ruta específica a otra aplicación?
ruby-on-rails-3.1 reverse-proxy (4)
Encontré esta excelente publicación en el blog sobre cómo usar Rack::Proxy
como una aplicación proxy separada. El artículo explica cómo usa Rack::Proxy
para enviar solicitudes a http://localhost:3000
a una aplicación en el puerto 3001
y solicitudes a http://localhost:3000/api
a una aplicación en el puerto 3002
. Quiero hacer lo mismo, pero no quiero crear una aplicación proxy separada. En su lugar, quiero que mi aplicación Rails principal envíe solicitudes de proxy a /blog
para una aplicación diferente.
Publicación en el blog: http://livsey.org/blog/2012/02/23/using-rack-proxy-to-serve-multiple-rails-apps-from-the-same-domain-and-port/
A continuación se muestra un código aún más sencillo para representar una api cuando desea que http://localhost:3000/api/users/1 (por ejemplo) vaya al espacio de nombres de la api definido en route.rb sin utilizar un programa de servidor proxy.
En producción sería algo así como http://api.sample.com/users/1 .
lib / proxy.rb
require ''rack-proxy''
class Proxy < Rack::Proxy
def perform_request(env)
request = Rack::Request.new(env)
if request.path =~ %r{^/api}
#do nothing
else
@app.call(env)
end
end
end
config / application.rb
config.middleware.use "Proxy"
config / route.rb
namespace :api, defaults: { format: :json },
constraints: { subdomain: ''api'' }, path: ''/'' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :users, :only => [:show, :create, :update, :destroy]
end
lib / api_constraints.rb
class ApiConstraints
def initialize(options)
@version = options[:version]
@default = options[:default]
end
def matches?(req)
@default || req.headers[''Accept''].include?("application/vnd.sample.v#{@version}")
end
end
Este es un ligero cambio en la solución de steve que usa un poco menos de comprensión interna de Rack::Proxy
:
require ''rack/proxy''
class MyProxy < Rack::Proxy
def initialize(app)
@app = app
end
def call(env)
# call super if we want to proxy, otherwise just handle regularly via call
(proxy?(env) && super) || @app.call(env)
end
def proxy?(env)
# do not alter env here, but return true if you want to proxy for this request.
return true
end
def rewrite_env(env)
# change the env here
env["HTTP_HOST"] = "some.other.host"
env
end
end
FWIW, también acabo de abordar este problema. Algunos pueden encontrar el código completo útil, ya que necesitaba más de lo que usted publicó:
# lib/proxy_to_other.rb
class ProxyToOther < Rack::Proxy
def initialize(app)
@app = app
end
def call(env)
original_host = env["HTTP_HOST"]
rewrite_env(env)
if env["HTTP_HOST"] != original_host
perform_request(env)
else
# just regular
@app.call(env)
end
end
def rewrite_env(env)
request = Rack::Request.new(env)
if request.path =~ %r{^/prefix|^/other_prefix}
# do nothing
else
env["HTTP_HOST"] = "localhost:3000"
end
env
end
end
También:
# config/application.rb
# ...snip ...
module MyApplication
class Application < Rails::Application
# Custom Rack middlewares
config.middleware.use "ProxyToOther" if ["development", "test"].include? Rails.env
#...snip....
Esto supone que su aplicación a la que desea enviar proxy se está ejecutando en el puerto 3001. Me parece que la aplicación que está golpeando puede ejecutarse en cualquier puerto. Esto también supone que solo desea realizar el proxy en entornos de desarrollo y prueba, porque tendrá una solución "real" en producción y puesta en escena (por ejemplo, nginx o un equilibrador de carga que haga lo correcto).
Lo averigué.
lib / proxy.rbrequire ''rack-proxy''
class Proxy < Rack::Proxy
def initialize(app)
@app = app
end
def rewrite_env(env)
# do magic in here
end
end
config / application.rb
config.middleware.use "Proxy"