15

In my rails app which is json only, I want to send a 406 code whenever someone calls my rails app with accept header set to anything except application/json. I also want it to send a 415 when I get the content type set to anything except application / json

My controllers have respond_to :json put on them. I only render json in all actions. However how do I ensure that I return error code 406/415 for all calls for anything that is called for all other accept headers/content-type and with format set to anything except json.

Eg. If I my resource is books/1 I want to allow books/1.json or books/1 with application/json in accept header and content type

Any ideas on how I can do these two actions?

pdu
  • 10,295
  • 4
  • 58
  • 95
av501
  • 6,645
  • 2
  • 23
  • 34
  • 2
    Wouldn't that be a `406 Not Acceptable` instead of a `415`? – pdu Jan 29 '13 at 09:43
  • @pduersteler, yes, Let me correct my question.I need both! – av501 Jan 29 '13 at 09:44
  • Similar question answering both json-only request & json-only reponse: http://stackoverflow.com/questions/21978580/rails-4-respond-only-to-json-and-not-html/30647637#30647637 – csi Jun 04 '15 at 15:38

3 Answers3

37

Basically, you can limit your responses in two ways.

First, there is respond_to for your controllers. This would automatically trigger a 406 Not Acceptable if a request for a format is made which is not defined.

Example:

class SomeController < ApplicationController
  respond_to :json


  def show
    @record = Record.find params[:id]

    respond_with @record
  end
end

The other way would be to add a before_filter to check for the format and react accordingly.

Example:

class ApplicationController < ActionController::Base
  before_filter :check_format


  def check_format
    render :nothing => true, :status => 406 unless params[:format] == 'json' || request.headers["Accept"] =~ /json/
  end
end
Amy
  • 7,388
  • 2
  • 20
  • 31
pdu
  • 10,295
  • 4
  • 58
  • 95
  • 1
    Instead of `params[:format] == "json" || request.headers["Accept"] =~ /json/` you can use `request.format.symbol == :json`. – ciastek Aug 24 '15 at 10:23
  • 3
    The `respond_to` method has been removed from Rails 4, use the [responders gem](https://github.com/plataformatec/responders) if you want to use it. – d_rail Nov 16 '15 at 20:06
  • You are saying "return error unless format is json OR accepts json". That seems wrong to me. You want to return an error unless BOTH conditions are met. – berkes Oct 11 '16 at 15:39
  • Because someone tried to edit it; The question ans this answer therefore is for rails3; for more up-to-date rails versions, use `before_action` instead of `before_filter` (which is not present in rails3, thus the edit is being rejected) – pdu Nov 10 '17 at 10:31
10

You can do it with a before_filter in ApplicationController

before_filter :ensure_json_request

def ensure_json_request
  return if params[:format] == "json" || request.headers["Accept"] =~ /json/
  render :nothing => true, :status => 406
end
Erez Rabih
  • 15,562
  • 3
  • 47
  • 64
-1

On rails 4.2+ respond_to has been removed, so unless you want to import the full responders gem just for this, your best bet is to roll your own. This is what I'm using in my rails 5 api:

    class ApplicationController < ActionController::API
      before_action :force_json

      private
      def force_json
        # if params[_json] it means request was parsed as json 
        # if body.read.blank? there was no body (GET/DELETE) so content-type was meaningless anyway
        head :not_acceptable unless params['_json'] || request.body.read.blank?
      end
    end
bluehallu
  • 10,205
  • 9
  • 44
  • 61