§2023-02-27

Our web API is currently returning a JSON document labeled with the media type for HTML. We need to fix this ASAP.

$ curl -i http://localhost:4567/users
HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8       <-- labled as HTML
Content-Length: 203
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Connection: keep-alive
Server: thin
....
# webapi.rb
require 'sinatra'
require 'json'

users = {
  'thibault': { first_name: 'Thibault', last_name: 'Denizet', age: 25 },
  'simon':    { first_name: 'Simon', last_name: 'Random', age: 26 },
  'john':     { first_name: 'John', last_name: 'Smith', age: 28 }
}

# WE ADDED THIS!
before do
  content_type 'application/json'
end

get '/' do
  'Master Ruby Web APIs - Chapter 2'
end

get '/users' do
  users.map { |name, data| data.merge(id: name) }.to_json
end
$ curl -i http://localhost:4567/users
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 203
X-Content-Type-Options: nosniff
Connection: keep-alive
Server: thin
....

4.2. Offering JSON and XML

# webapi.rb
require 'sinatra'
require 'json'

# We require gyoku here in order to use it in the
# XML route.
require 'gyoku'

users = {
  thibault: { first_name: 'Thibault', last_name: 'Denizet', age: 25 },
  simon:    { first_name: 'Simon', last_name: 'Random', age: 26 },
  john:     { first_name: 'John', last_name: 'Smith', age: 28 }
}

before do
  content_type 'application/json'
end

get '/' do
  'Master Ruby Web APIs - Chapter 2'
end

# Looping through the list of routes for which
# we want to return a JSON representation.
['/users', '/users.json'].each do |path|
  get path do
    users.map { |name, data| data.merge(id: name) }.to_json
  end
end

# Defining /users.xml with the application/xml media type
# and simply calling Gyoku on our users hash with a root
# element 'users'.
get '/users.xml' do
  content_type 'application/xml'
  Gyoku.xml(users: users)
end
$ curl  http://localhost:4567/users
[{"first_name":"Thibault","last_name":"Denizet","age":25,"id":"thibault"},{"first_name":"Simon","last_name":"Random","age":26,"id":"simon"},{"first_name":"John","last_name":"Smith","age":28,"id":"john"}] 

$ curl  http://localhost:4567/users.json
[{"first_name":"Thibault","last_name":"Denizet","age":25,"id":"thibault"},{"first_name":"Simon","last_name":"Random","age":26,"id":"simon"},{"first_name":"John","last_name":"Smith","age":28,"id":"john"}]

$ curl  http://localhost:4567/users.xml
<users><thibault><firstName>Thibault</firstName><lastName>Denizet</lastName><age>25</age></thibault><simon><firstName>Simon</firstName><lastName>Random</lastName><age>26</age></simon><john><firstName>John</firstName><lastName>Smith</lastName><age>28</age></john></users>

4.3. The Fundamental Issue With This Approach

That’s going against HTTP fundamentals and the Uniform Interface constraint from REST. A better way of presenting the users concept would be to have only one URI, /users, that could respond to different representations using HTTP header fields.

4.4. Following The RFC Recommendations

The HTTP header we need for this to work is called Accept. This header field allows the client to specify which media types it would like to get. It is defined as a string listing all media types that the client can understand, with an optional priority parameter named q that can be added to each media type in the list.