Class: Grape::Middleware::Versioner::Header

Inherits:
Base
  • Object
show all
Defined in:
lib/grape/middleware/versioner/header.rb

Overview

This middleware sets various version related rack environment variables based on the HTTP Accept header with the pattern: application/vnd.:vendor-:version+:format

Example: For request header Accept: application/vnd.mycompany-v1+json

The following rack env variables are set:

env['api.type'] => 'application' env['api.subtype'] => 'vnd.mycompany-v1+json' env['api.vendor] => 'mycompany' env['api.version] => 'v1' env['api.format] => 'format'

If version does not match this route, then a 406 is raised with X-Cascade header to alert Rack::Mount to attempt the next matched route.

Instance Attribute Summary

Attributes inherited from Base

#app, #env, #options

Instance Method Summary collapse

Methods inherited from Base

#after, #call, #call!, #content_type, #content_type_for, #content_types, #default_options, #initialize, #mime_types, #request, #response

Constructor Details

This class inherits a constructor from Grape::Middleware::Base

Instance Method Details

#beforeObject



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/grape/middleware/versioner/header.rb', line 26

def before
  header = Rack::Accept::MediaType.new env['HTTP_ACCEPT']

  if strict?
    # If no Accept header:
    if header.qvalues.empty?
      throw :error, status: 406, headers: error_headers, message: 'Accept header must be set.'
    end
    # Remove any acceptable content types with ranges.
    header.qvalues.reject! do |media_type, _|
      Rack::Accept::Header.parse_media_type(media_type).find { |s| s == '*' }
    end
    # If all Accept headers included a range:
    if header.qvalues.empty?
      throw :error, status: 406, headers: error_headers, message: 'Accept header must not contain ranges ("*").'
    end
  end

  media_type = header.best_of available_media_types

  if media_type
    type, subtype = Rack::Accept::Header.parse_media_type media_type
    env['api.type']    = type
    env['api.subtype'] = subtype

    if /\Avnd\.([a-z0-9*.]+)(?:-([a-z0-9*\-.]+))?(?:\+([a-z0-9*\-.+]+))?\z/ =~ subtype
      env['api.vendor']  = $1
      env['api.version'] = $2
      env['api.format']  = $3  # weird that Grape::Middleware::Formatter also does this
    end
  # If none of the available content types are acceptable:
  elsif strict?
    throw :error, status: 406, headers: error_headers, message: '406 Not Acceptable'
  # If all acceptable content types specify a vendor or version that doesn't exist:
  elsif header.values.all? { |header_value| has_vendor?(header_value) || has_version?(header_value) }
    throw :error, status: 406, headers: error_headers, message: 'API vendor or version not found.'
  end
end