views:

42

answers:

3

I'm trying to get my Rails app to serve XHTML content properly, with the correct content-type of application/xhtml+xml. Ideally with content negotiation so that IE users get a chance to use the site too.

Given that all the HTML generated by Rails is marked at XHTML 1.0 Transitional, I'm a bit surprised that there is no obvious option to make Rails serve markup as XHTML. I found this http://blog.codahale.com/2006/05/23/rails-plugin-xhtml_content_type/, but it seems to be for 1.1.2 and I can't get it working properly under 2.3.8.

Have I missed something here?

A: 

You can force the content type in any controller function or using an after filter. Either of these methods can set the content type via:

response.content_type = "application/xhtml+xml"
danivovich
Thanks, this has put me on the right track, but Rails aliases "text/html" with "application/xhtml+xml" (why?) so am having trouble finding out whether the client has sent the XHTML in the accept header. It always gets removed/replaced by the time it gets to my before_filter. What's the best/cleanest way to detect whether the client accepts application/xhtml+xml?
derkyjadex
Fiddler2 for any web traffic, or Firebug for Firefox.
danivovich
A: 

Add this to your application_controller.rb:

 def correct_safari_and_ie_accept_headers
    ajax_request_types = [ 'text/javascript', 'application/json', 'text/xml']
    request.accepts.sort!{ |x, y| ajax_request_types.include?(y.to_s) ? 1 : -1 } if request.xhr?
 end

This corrects the safari and ie accept headers so that it defaults to text/xml instead of text/html. It works for me. Tested both on IE and Safari. Other browsers default to text/xml anyways.

EDIT: I have set my DOCTYPE to <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt; and not XHTML Transitional.

Shripad K
A: 

Ok, I've got something that works now. Thanks to @danivovich for starting me in the right place. The first thing I had to do was sort out the Mime types in mime_types.rb so that HTML wasn't aliased with XHTML:

module Mime
  remove_const('HTML') # remove this so that we can re-register the types
end

Mime::Type.register "text/html", :html
Mime::Type.register "application/xhtml+xml", :xhtml

The I just added this to my application controller:

  before_filter :negotiate_xhtml
  after_filter :set_content_type

  def negotiate_xhtml
    @serving_polyglot = false
    if params[:format].nil? or request.format == :html
      @serving_polyglot = ((not request.accepts.include? :xhtml) or params[:format] == 'html')
      request.format = :xhtml
    end
  end

  def set_content_type
    if @serving_polyglot
      response.content_type = 'text/html'
    end
  end    

This makes sure that XHTML is always servered as such, unless the client doesn't accept it, or HTML has been explicitly requested. HTML is always just XHTML served as a polyglot. The @serving_polyglot variable is available in the views where any switching is needed.

This is working for me under Chrome, Safari, Firefox, Opera and IE[6-8].

derkyjadex