1

Whenever I make an API request from my angular front end to my Rails back end, the API calls are interpreted as content-type: :html. However I want them to be interpreted as content-type: :json

Inspecting the incoming request the content_type is correctly set to be "application/json" however the request.format evaluates to being :html

17:23:27 web.1  | (rdb:1) request.content_type
17:23:27 web.1  | "application/json"
17:23:31 web.1  | (rdb:1) request.format
17:23:31 web.1  | #<Mime::Type:0x007fb523405d00 @synonyms=["application/xhtml+xml"], @symbol=:html, @string="text/html">

What am I doing wrong, should I be forcing the headers on the client-side or do I have the wrong end of one of these sticks?

Edit

I guess that Rails is using the file ending to determine the required format - is there any way to make it respect the headers instead?

Community
  • 1
  • 1
Peter Nixey
  • 16,187
  • 14
  • 79
  • 133

2 Answers2

3

Usually when you want to hook up an Angular application to a Rails project, it's best practice to create an api namespace for all of your angular resources. In your routes.rb file, you're going to want to do something like this:

# Set the default format to json
namespace :api, defaults: {format: :json} do
  # Edit and New routes are generally not required for RESTful JSON endpoints
  resources :my_json_resources, except: [:edit, :new]
  # Other resources
end

Then you can hit your routes without the json extension like so: /api/my_json_resources/:id. Setting the default on the route ensures that the endpoints will return JSON instead of HTML by default. Here's a cool tutorial (that might be a little outdated by now) that shows you how to integrate angular with rails 4. https://shellycloud.com/blog/2013/10/how-to-integrate-angularjs-with-rails-4

Also, note that the section where you set the CSRF headers is not recommended by the Angular documentation. For that please refer to this stack overflow post.

For Rails route namespace best practices, please refer to this documentation

Community
  • 1
  • 1
Mike Quinlan
  • 2,873
  • 17
  • 25
  • Very helpful, thanks Mike. I'm not sure that I'm ready to make the jump to another namespace yet (though as you point out it's the most sensible thing) but you've got my mind thinking. Thank you – Peter Nixey Jun 02 '14 at 17:05
2

As @mike pointed out it will ultimately make sense to setup a separate API namespace for the application in which things default to content-type: 'application/json'.

However that's a longer term shift for me and in the meantime, this is how I solved the problem.

Rails determines the format from the 'Accept' header

From this question, and this source code it's clear that Rails is using the Accept header to figure out the actual format.

You can set the accept header easily in Angular

It's easy enough to set the accept header in Angular

editorAppServices.factory('Magazine', ['$resource', 
  function($resource){ 
    return $resource('magazines/:toParam', {toParam: '@to_param'}, {
      query: {
        method: 'GET', params:{}, isArray:true
      },
      save: {
        method: 'PUT', headers: { 'Accept': 'application/json' }
      }
    })
  }])

And that's all there is to it.

Community
  • 1
  • 1
Peter Nixey
  • 16,187
  • 14
  • 79
  • 133
  • 1
    Huh. Good to know! I've never had to bother with that. Another thing you might want to consider is setting the default headers in the angular application `config` block instead of every one of your `ngResource` instantiations. [Documentation here](https://docs.angularjs.org/api/ng/service/$http) in the "Setting HTTP Headers" section. – Mike Quinlan Jun 02 '14 at 22:47
  • Ha - brilliant, that's what I needed to know - good team @mike ;) – Peter Nixey Jun 03 '14 at 09:03