class Grape::Middleware::Versioner::Header
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.a-cool-resource-v1+json
The following rack env variables are set:
env['api.type'] => 'application' env['api.subtype'] => 'vnd.mycompany.a-cool-resource-v1+json' env['api.vendor] => 'mycompany.a-cool-resource' env['api.version] => 'v1' env['api.format] => 'json'
If version does not match this route, then a 406 is raised with X-Cascade header to alert Grape::Router to attempt the next matched route.
Constants
- HAS_VENDOR_REGEX
- HAS_VERSION_REGEX
- VENDOR_VERSION_HEADER_REGEX
Public Instance Methods
# File lib/grape/middleware/versioner/header.rb, line 32 def before strict_header_checks if strict? if media_type || env[Grape::Env::GRAPE_ALLOWED_METHODS] media_type_header_handler elsif headers_contain_wrong_vendor? fail_with_invalid_accept_header!('API vendor not found.') elsif headers_contain_wrong_version? fail_with_invalid_version_header!('API version not found.') end end
Private Instance Methods
# File lib/grape/middleware/versioner/header.rb, line 62 def an_accept_header_with_version_and_vendor_is_present? header.qvalues.keys.any? do |h| VENDOR_VERSION_HEADER_REGEX =~ h.sub('application/', '') end end
# File lib/grape/middleware/versioner/header.rb, line 99 def available_media_types available_media_types = [] content_types.each do |extension, _media_type| versions.reverse_each do |version| available_media_types += [ "application/vnd.#{vendor}-#{version}+#{extension}", "application/vnd.#{vendor}-#{version}" ] end available_media_types << "application/vnd.#{vendor}+#{extension}" end available_media_types << "application/vnd.#{vendor}" content_types.each do |_, media_type| available_media_types << media_type end available_media_types.flatten end
By default those errors contain an `X-Cascade` header set to `pass`, which allows nesting and stacking of routes (see Grape::Router for more information). To prevent # this behavior, and not add the `X-Cascade` header, one can set the `:cascade` option to `false`.
# File lib/grape/middleware/versioner/header.rb, line 160 def cascade? if version_options && version_options.key?(:cascade) version_options[:cascade] else true end end
# File lib/grape/middleware/versioner/header.rb, line 168 def error_headers cascade? ? { Grape::Http::Headers::X_CASCADE => 'pass' } : {} end
# File lib/grape/middleware/versioner/header.rb, line 89 def fail_with_invalid_accept_header!(message) raise Grape::Exceptions::InvalidAcceptHeader .new(message, error_headers) end
# File lib/grape/middleware/versioner/header.rb, line 94 def fail_with_invalid_version_header!(message) raise Grape::Exceptions::InvalidVersionHeader .new(message, error_headers) end
# File lib/grape/middleware/versioner/header.rb, line 68 def header @header ||= rack_accept_header end
# File lib/grape/middleware/versioner/header.rb, line 121 def headers_contain_wrong_vendor? header.values.all? do |header_value| vendor?(header_value) && request_vendor(header_value) != vendor end end
# File lib/grape/middleware/versioner/header.rb, line 127 def headers_contain_wrong_version? header.values.all? do |header_value| version?(header_value) && !versions.include?(request_version(header_value)) end end
# File lib/grape/middleware/versioner/header.rb, line 72 def media_type @media_type ||= header.best_of(available_media_types) end
# File lib/grape/middleware/versioner/header.rb, line 76 def media_type_header_handler type, subtype = Rack::Accept::Header.parse_media_type(media_type) env[Grape::Env::API_TYPE] = type env[Grape::Env::API_SUBTYPE] = subtype return unless VENDOR_VERSION_HEADER_REGEX =~ subtype env[Grape::Env::API_VENDOR] = Regexp.last_match[1] env[Grape::Env::API_VERSION] = Regexp.last_match[2] # weird that Grape::Middleware::Formatter also does this env[Grape::Env::API_FORMAT] = Regexp.last_match[3] end
# File lib/grape/middleware/versioner/header.rb, line 133 def rack_accept_header Rack::Accept::MediaType.new env[Grape::Http::Headers::HTTP_ACCEPT] rescue RuntimeError => e fail_with_invalid_accept_header!(e.message) end
# File lib/grape/middleware/versioner/header.rb, line 179 def request_vendor(media_type) _, subtype = Rack::Accept::Header.parse_media_type(media_type) subtype.match(VENDOR_VERSION_HEADER_REGEX)[1] end
# File lib/grape/middleware/versioner/header.rb, line 184 def request_version(media_type) _, subtype = Rack::Accept::Header.parse_media_type(media_type) subtype.match(VENDOR_VERSION_HEADER_REGEX)[2] end
# File lib/grape/middleware/versioner/header.rb, line 147 def strict? version_options && version_options[:strict] end
# File lib/grape/middleware/versioner/header.rb, line 51 def strict_accept_header_presence_check return unless header.qvalues.empty? fail_with_invalid_accept_header!('Accept header must be set.') end
# File lib/grape/middleware/versioner/header.rb, line 46 def strict_header_checks strict_accept_header_presence_check strict_version_vendor_accept_header_presence_check end
# File lib/grape/middleware/versioner/header.rb, line 56 def strict_version_vendor_accept_header_presence_check return unless versions.present? return if an_accept_header_with_version_and_vendor_is_present? fail_with_invalid_accept_header!('API vendor or version not found.') end
# File lib/grape/middleware/versioner/header.rb, line 143 def vendor version_options && version_options[:vendor] end
@param [String] #media_type a content type @return [Boolean] whether the content type sets a vendor
# File lib/grape/middleware/versioner/header.rb, line 174 def vendor?(media_type) _, subtype = Rack::Accept::Header.parse_media_type(media_type) subtype[HAS_VENDOR_REGEX] end
@param [String] #media_type a content type @return [Boolean] whether the content type sets an API version
# File lib/grape/middleware/versioner/header.rb, line 191 def version?(media_type) _, subtype = Rack::Accept::Header.parse_media_type(media_type) subtype[HAS_VERSION_REGEX] end
# File lib/grape/middleware/versioner/header.rb, line 151 def version_options options[:version_options] end
# File lib/grape/middleware/versioner/header.rb, line 139 def versions options[:versions] || [] end