Sorry if this question is a duplication of another question, but i haven't found it yet.
I have some Grape APIs (which are Rack apps) and one of them (the user API) uses a middleware for authentication.
In my config.ru file i combined all APIs to one app via Rack::Cascade. Here's the code:
user_management = Rack::Builder.new {
use Middleware
run UserAPI.new
}
app = Rack::Cascade.new [
user_management,
ExampleAPI1,
ExampleAPI2,
ExampleAPI3
]
The problem is that the middleware is called every time when any of the APIs gets a request.
Does anybody have any advice on how to use the middleware only when the user API gets a request?
The answer to this question is that I had to remove the resources (e.g. resource :user) from the APIs and then use Rack::Builder as follows:
app = Rack::Builder.new {
map '/user' do
use Middleware
run ExampleAPI1
end
map '/items' do
run ExampleAPI2
end
map '/locations' do
run ExampleAPI3
end
map '/reports' do
run ExampleAPI4
end
}
The problem with Rack::Cascade was that it tries every app from top to bottom until it finds the a suitable endpoint
Related
I have a modular Sinatra app that runs fine under rackup, that is with a config.ru that has three 'use' statements and one run statement. I am trying to get my head around how to port the application to AWS lambda where API_gateway will provide web server services and just call my app.
I am using the recommended lambda.rb script from https://github.com/aws-samples/serverless-sinatra-sample/blob/master/lambda.rb as my entrypoint. What I don't get is how, in the AWS Lambda micro-clime, do I assemble the modules/layers of my application without rackup and config.ru?
I assume that my noob brain is just missing something really basic in spite of the fact that I have read every blog post, bit of Sinatra and Rack documentation I know of, and the great "Sinatra Up and Running" book. What am I missing?
Upon re-reading the book, "Sinatra: Up and Running" again, it seems that there is a fairly elegant solution. If I set up my lambda.handler entrypoint to point to a simple Sinatra router, then it will be the router that composes the individual controllers into a 'system' which will handle all of my end points. the example from the book:
Example 4-32. Using Sinatra as router
require 'sinatra/base'
class Foo < Sinatra::Base
get('/foo') { 'foo' }
end
class Bar < Sinatra::Base
get('/bar') { 'bar' }
end
class Routes < Sinatra::Base
get('/foo') { Foo.call(env) }
get('/bar') { Bar.call(env) }
end
run Routes
Of course, in my case, I won't need the 'run Routes' line. I will just have my lambda.handler call Routes, as in Routes.call. This seems very doable!
I have a Rails API backend and a ReactJS frontend. My backend sends custom emails that often have confirmation-like links (like email confirmation)
Should the confirmation links point to my backend directly, or should they rather load the frontend first and then make an API call to the backend?
The two alternatives I'm thinking of are:
1 - The email confirmation link hits directly
backend.example.com/email_confirmations/:confirmation_token, which then redirects to a specific success (error) page frontend.example.com/email_confirmation/success(/failure) on my frontend.
On the backend I would only need a Metal controller with minimum modules to perform redirection to the frontend app, the controller is always responding with redirects). If further actions need to be taken from the frontend, they'll hit a different API Endpoint
2 - The email confirmation links opens up my frontend at
frontend.example.com/email_confirmations/:confirmation_token that triggers an API request to backend.example.com/email_confirmations/:confirmation_token.
Then my (Json:api) backend makes a jsonapi-compliant response with a Rails APIController.
What are you doing in practice?
I decided to opt in for the first scenario but maybe systematically calling/loading the frontend first makes more sense?
How do you wire backend/frontend in those scenarios?
I have currently implemented a very simple (Metal) Controller that would just process the incoming parameters perform redirections only to the frontend. I was hoping to define "url helpers" that would point to my frontend like so:
namespace :email_redirection do
# Controller that gets hit by email confirmation links in scenario #1
resources :confirmations
end
namespace :frontend do
frontend_root = Rails.configuration.frontend_host_with_http_and_port
scope frontend_root do
# Generation of URL Helpers for the frontend app
resources :confirmations, only: [] do
get 'successful'
get 'unsuccessful'
end
end
end
And my confirmation controller would be like
class EmailRedirection::ConfirmationsController
def index
svc = MyConfirmationService.new(token: params[:confirmation_token])
if svc.confirm
redirect_to(
frontend_successful_confirmation_url,
email: svc.confirmable.email
)
else
redirect_to(frontend_unsuccessful_confirmation_url)
end
end
end
I'm getting several error and I believe maybe the url helpers are not useable with different host/port... (or I have to pass them explicitely to each call)
How can I handle that ? And if 1. is a good choice, what would be the redirection codes you'd send on success/failure (since they can only be 3xx) ?
Both solutions involve quite some wiring between the backend/frontend and I'm not sure how to best wire things up.
Note : my models use devise but because devise isn't so great with APIs/etc. I'm using my own ConfirmationService that also handles some side-effects. I don't consider Devise to be relevant here
Creating a rails route to an external URL
I have an app I created on Heroku which is written in Ruby (not rails) and Sinatra.
It is hosted on the default herokuapp domain so I can address the app with both HTTP and HTTPS.
The app requests user credentials which I forward on to an HTTPS call so the forwarding part is secure.
I want to ensure my users always connect securely to my app so the credentials aren't passed in clear text.
Despite lots of research, I've not found a solution to this simple requirement.
Is there a simple solution without changing my app to Ruby rails or otherwise?
Thanks,
Alan
I use a helper that looks like this:
def https_required!
if settings.production? && request.scheme == 'http'
headers['Location'] = request.url.sub('http', 'https')
halt 301, "https required\n"
end
end
I can then add it to any single route I want to force to https, or use it in the before filter to force on a set of urls:
before "/admin/*" do
https_required!
end
Redirect in a Before Filter
This is untested, but it should work. If not, or if it needs additional refinement, it should at least give you a reasonable starting point.
before do
redirect request.url.sub('http', 'https') unless request.secure?
end
See Also
Filters
Request Object
RackSsl::Enforcer
I'm building a Grape API alongside Sinatra. So far I've been mounting them in separate routes like this:
run Rack::URLMap.new("/" => Frontend::Server.new,
"/api" => API::Server.new)
Where the "/api" is served by a Grape app and "/" by a Sinatra app. But I wanted to use subdomains to separate those concerns instead of the actual "sub-URL". Any clues on how to do this?
Thanks in advance for the help.
There is a rack-subdomain gem, but it only handles redirection to paths, not rack apps. You could fork it and make it redirect to rack apps instead.
You could also just roll your own :
class SubdomainDispatcher
def initialize
#frontend = Frontend::Server.new
#api = API::Server.new
end
def call(env)
if subdomain == 'api'
return #api.call(env)
else
return #frontend.call(env)
end
end
private
# If may be more robust to use a 3rd party plugin to extract the subdomain
# e.g ActionDispatch::Http::URL.extract_subdomain(#env['HTTP_HOST'])
def subdomain
#env['HTTP_HOST'].split('.').first
end
end
run SubdomainDispatcher.new
How do I mount/run multiple rack apps without using map or Rack::UrlMap? Using these will dispatch the apps fine, but will also prefix the route used for dispatch to the beginning of the matcher, so:
class API < Sinatra::Base
get "/api" do
# blah
end
end
map( "/api" ) { run API }
The route above is accessed at "/api/api" which is not what I want, just "/api" is what I want. I don't want to dig into the request object with a filter and remove prefixes if there's a better way.
I've tried:
use API.app # the app is wrapped in a `def self.app` for convenience.
run Web.app
but use causes problems if the app itself has used use within it too. Doing this:
run API.app
run Web.app
will only serve routes from the last app given to run.
I'm about to try Rack::Cascade but I've never used that before and don't know if it's a good answer to this problem.
The answer is indeed Rack::Cascade:
run Rack::Cascade.new( [API, Web] )