Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strange behavior in modular application #1227

Closed
AvailCat opened this issue Jan 1, 2017 · 4 comments
Closed

Strange behavior in modular application #1227

AvailCat opened this issue Jan 1, 2017 · 4 comments

Comments

@AvailCat
Copy link

AvailCat commented Jan 1, 2017

Step:

  1. Add "error 403 do ... end" in application controller , and return {code: status, message: body}

  2. Add halt 403, 'a' in AController

  3. Add halt 403, 'b' in BController

  4. Add halt 403, 'c' in CController

  5. Try request url '/a', got
    {"code":403,"message":["Oooooops, A"]}

  6. Try request url '/b/, got
    {"code":403,"message":"{\"code\":403,\"message\":\"Oooooops, B\"}"}

  7. Try request url '/c', got
    {"code":403,"message":["{\"code\":403,\"message\":[\"{\\\"code\\\":403,\\\"message\\\":[\\\"Oooooops, C\\\"]}\"]}"]}

That's the problem, Why response like a "Matryoshka doll"?
How many times body repeat is depend on 'use' order in config.ru

Here is sample code, run it with bundle install then rackup -p 9192
sample_code.tar.gz

@mwpastore
Copy link
Member

mwpastore commented Jan 1, 2017

You are seeing this behavior because you are using the three controllers as middleware. This is exactly the expected behavior: the inner-most Rack application processes the request and passes the response to the second inner-most Rack application, and so forth and so on. Because all the controllers inherit the same error-handler, they all apply the same logic to the response.

If you can explain what you are trying to do, we might be able to help you figure out the best way to do it.

@AvailCat
Copy link
Author

AvailCat commented Jan 2, 2017

@mwpastore Maybe i misunderstood something, I just want use my own error page template.

http://stackoverflow.com/questions/36127041/in-ruby-sinatra-how-to-halt-with-an-erb-template-and-error-message

The answer in this link is the right way? I'm a beginner in Ruby...

@mwpastore
Copy link
Member

Your error handler is fine, but the way you're structuring your controllers and middleware stack is causing the nesting. All of your controllers inherit the error handler from ApplicationController and apply it on error 403.

There are a few different ways to tackle this architectural problem. To do it like you're trying to do it, you can put your error-handling logic in its own middleware and include it at the top of the stack, like so:

# test.ru
require 'json'
require 'sinatra/base'

class ErrorController < Sinatra::Base    
  error 403 do
    { code: status, message: body.join }.to_json
  end
end

class ApplicationController < Sinatra::Base    
  get '/' do
    'hello'
  end
end

class AController < ApplicationController
  get '/a' do
    halt 403, 'Oooooops, A'
  end
end

class BController < ApplicationController
  get '/b' do
    halt 403, 'Oooooops, B'
  end
end

class CController < ApplicationController
  get '/c' do
    halt 403, 'Oooooops, C'
  end
end

use ErrorController
use AController
use BController
use CController
run ApplicationController

Another option is to use Rack::URLMap instead of (or alongside) a middleware stack, and namespace your controllers:

# test.ru
require 'json'
require 'sinatra/base'

class ApplicationController < Sinatra::Base    
  error 403 do
    { code: status, message: body.join }.to_json
  end

  get '/' do
    'hello'
  end
end

class AController < ApplicationController
  get '/' do
    halt 403, 'Oooooops, A'
  end
end

class BController < ApplicationController
  get '/' do
    halt 403, 'Oooooops, B'
  end
end

class CController < ApplicationController
  get '/' do
    halt 403, 'Oooooops, C'
  end
end

run Rack::URLMap.new(
  '/a' => AController,
  '/b' => BController,
  '/c' => CController,
  '/' => ApplicationController
)

Now the routing is done entirely in Rack before the requests hit any of the Sinatra controllers.

These are just some options. As you're getting started, I would recommend you continue learning Ruby and dig into the philosophy of Rack a bit more. It's useful to keep in mind that Sinatra is a relatively thin DSL on top of Rack and isn't very opinionated.

@AvailCat
Copy link
Author

AvailCat commented Jan 2, 2017

Thank you for the code and advice. I will continue learn Ruby and it's philosophy in depth.

This is my first Ruby + Sinatra project after some fragmented time learning. I think i should forget some experience in other language, just think it in the 'Ruby way'.

BTW: Sorry for my bad English, hope you can understand it.

@AvailCat AvailCat closed this as completed Jan 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants