Question

I am trying to make a Grape API answering in json format to all its verbs. The problem is that I can't make it answer to a routing error in json format. I even can't rescue the ActionController::RoutingError.

I have read this link https://github.com/intridea/grape/pull/342 and I've put the cascade directive to false but the API only answers "Not Found" in plain text.

$ curl -X GET http://localhost:3000/api/wrong_uri
Not Found

with verbose option:

$ curl -X GET http://localhost:3000/api/wrong_uri -v
* About to connect() to localhost port 3000 (#0)
*   Trying 127.0.0.1... connected
> GET /api/wrong_uri HTTP/1.1
> User-Agent: curl/7.22.0 (i686-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: localhost:3000
> Accept: */*
> 
< HTTP/1.1 404 Not Found 
< Content-Type: text/html
< Cache-Control: no-cache
< X-Request-Id: 3cc74a78-3295-4c1f-b989-66d6fac50e5c
< X-Runtime: 0.002980
< Server: WEBrick/1.3.1 (Ruby/2.1.1/2014-02-24)
< Date: Tue, 06 May 2014 05:46:33 GMT
< Content-Length: 9
< Connection: Keep-Alive
< 
* Connection #0 to host localhost left intact
* Closing connection #0
Not Found

My grape file /app/api/api.rb

module API
  class Base < Grape::API
    format :json
    default_error_formatter :json
    cascade false

    rescue_from CanCan::AccessDenied do |e|
      Rack::Response.new({ error: "Forbidden", detail: "Access denied", status: '403' }.to_json, 403).finish
    end

    # it doesn't catch ActionController::RoutingError
    # rescue_from :all do |e|
    #   error_response({ message: "rescued from #{e.class.name}" })
    # end

    helpers ApiHelpers

    mount API::Auth
    mount API::V2
    mount API::V1
  end
end
Was it helpful?

Solution

By specifying cascade false you're saying Grape should be allowed to handle HTTP errors without interference from Rails. Consequently, when an unmatched URI beneath your API's mount point is requested, no ActionController::RoutingError is generated (this would have been done by Rails) and instead the output from Grape's default "unmatched route" handler is returned verbatim. Somewhat unhelpfully, this handler generates a plain "Not Found" message even when format :json has been specified, and that is the behaviour you're seeing.

The solution is to provide your own matchers to catch routes other than those specified in your V1 and V2 modules. Try adding this code to API::Base beneath the three mount statements:

# Generate a properly formatted 404 error for all unmatched routes except '/'
route :any, '*path' do
  error!({ error:  'Not Found',
           detail: "No such route '#{request.path}'",
           status: '404' },
         404)
end

# Generate a properly formatted 404 error for '/'
route :any do
  error!({ error:  'Not Found',
           detail: "No such route '#{request.path}'",
           status: '404' },
         404)
end

Naturally you'll want to edit the body of each route block to suit your needs.

For some reason both blocks seem to be necessary to catch all unmatched routes; this is a limitation of either Grape's matcher syntax or my understanding.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top