hanami / router

Ruby/Rack HTTP router
http://hanamirb.org
MIT License
362 stars 92 forks source link

Standalone routing with method override #190

Closed waiting-for-dev closed 5 years ago

waiting-for-dev commented 5 years ago

It seems it is not possible to route to a rack application using Rack::MethodOverride middleware with browser not supported methods like DELETE or PUT.

Script reproducing the issue:

# config.ru
require 'hanami/router'
require 'rack/builder'
require 'rack/method_override'

app = Rack::Builder.new.tap do |b|
  b.use Rack::MethodOverride

  b.run ->(env) { [200, {}, ['Routed!!']] }
end

router = Hanami::Router.new do
  get '/with_get', to: app
  delete '/with_delete', to: app
end

run router

Then:


rackup config.ru --port 9999 &

curl http://localhost:9999/with_get
# 127.0.0.1 - - [29/Jul/2019:14:13:12 +0000] "GET  HTTP/1.1" 200 - 0.0003
# Routed!!

curl http://localhost:9999/with_delete?_method=DELETE
# 127.0.0.1 - - [29/Jul/2019:14:13:43 +0000] "GET /with_delete?_method=DELETE HTTP/1.1" 405 - 0.0009

curl --header "HTTP_X_HTTP_METHOD_OVERRIDE: DELETE" http://localhost:9999/with_delete
# 127.0.0.1 - - [29/Jul/2019:14:14:47 +0000] "GET /with_delete HTTP/1.1" 405 - 0.0003
jodosha commented 5 years ago

@waiting-for-dev You cannot use Hanami::Router like that. In the code above you're mounting Rack::MethodOverride inside the router.

The logic that turns _method into a meaningful HTTP method for the router is inside the router, so it will never get hit.

What you want to do is this instead:

# frozen_string_literal: true

require "hanami/router"
require "rack/method_override"

use Rack::MethodOverride

router = Hanami::Router.new do
  get "/with_get", to: ->(*) { [200, {}, ["with GET"]] }
  delete "/with_delete", to: ->(*) { [200, {}, ["with DELETE"]] }
end

run router
  1. Use the implicit builder of config.ru. Alternatively you can wrap both use Rack::MethodOverride and run router into an explicit Rack::Builder

  2. The incoming requests will be handled by Rack::MethodOverride first, to apply its logic.

  3. Finally the manipulated Rack env can hit the router.


One note regarding cURL examples: by looking at Rack::MethodOverride code, only POST requests are considered for method overriding.

The correct manual testing would be:

$ curl -i -X POST -d '_method=DELETE' http://localhost:9999/with_delete
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Server: WEBrick/1.4.2 (Ruby/2.6.3/2019-04-16)
Date: Mon, 29 Jul 2019 14:54:06 GMT
Connection: Keep-Alive

with DELETE
waiting-for-dev commented 5 years ago

Oh! Thanks for the clarification @jodosha , that makes sense :)