How to handle non-root URLs in a singlepage app?

2019-02-07 04:47发布

问题:

I try to make a single page app with Rails 3.2 and Backbone.js with pushState option but faced with something that I do not understand.

If I load the root URL of the app (/), everything goes right: Rails return an HTML-layout with JS which bootstraps Backbone which makes some XHRs for JSON-entities and renders the content.

But if I start using app from non-root URL (e.g. by manually typing it in the browser's address bar) then Rails will try to handle this request using theirs routing rules from routes.rb - that's wrong, cause it's a "Backbone's" route. How do I load the page and bootstrap Backbone for handling this URL in that case?

回答1:

Finally I found the solution.

I put the following code into my routes.rb

class XHRConstraint
  def matches?(request)
    !request.xhr? && !(request.url =~ /\.json$/ && ::Rails.env == 'development')
  end
end

match '(*url)' => 'home#index', :constraints => XHRConstraint.new

With this matcher all non-XHR requests are routed to HomeController which returns an HTML page. And XHR requests will be handled by other controllers which return JSON responses. Also I left requests ending with ".json" as valid in development environment for debugging.



回答2:

This is a somewhat tricky issue, but basically in a nutshell, you need to respond to all valid (HTML) requests in rails with the same (root) page, from there backbone will take over and route to the correct route handler (in your bakckbone router).

I've discussed this issue in more detail here: rails and backbone working together

Basically what I do is to create actions for every page that I want to handle, and blank views. I use respond_with to return the page (which is the same in each case) and because I handle GET actions only for HTML requests, I add this line at the top of the controller:

respond_to :html, :only => [ :show, :new ]

JSON requests are handled with respond_with as well, but unlike the HTML requests actually return the requested resource (and perform the requested action in the case of PUT, POST and DELETE).



回答3:

Backbone will not be informed of your url change if you do it manually. This change will be catch by the browser and it will do its job sending the request to the server as usual.

Same if you click in a normal link, it will follow its href without inform Backbone.

If you want Backbone being in charge of a url change you have to do it through the Backbone tools you have available and this is the own Router.

So if you want to make an URL change in the Backbone way you have to do it explicitly, something like:

app.router.navigate("my/route", {trigger: true});