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.

Public Instance Methods

before() click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 27
def before
  match_best_quality_media_type! do |media_type|
    env.update(
      Grape::Env::API_TYPE => media_type.type,
      Grape::Env::API_SUBTYPE => media_type.subtype,
      Grape::Env::API_VENDOR => media_type.vendor,
      Grape::Env::API_VERSION => media_type.version,
      Grape::Env::API_FORMAT => media_type.format
    )
  end
end

Private Instance Methods

accept_header() click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 57
def accept_header
  env[Grape::Http::Headers::HTTP_ACCEPT]
end
accept_header_check!() click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 68
def accept_header_check!
  return if accept_header.present?

  invalid_accept_header!('Accept header must be set.')
end
allowed_methods() click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 53
def allowed_methods
  env[Grape::Env::GRAPE_ALLOWED_METHODS]
end
available_media_types() click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 115
def available_media_types
  [].tap do |available_media_types|
    base_media_type = "application/vnd.#{vendor}"
    content_types.each_key do |extension|
      versions&.reverse_each do |version|
        available_media_types << "#{base_media_type}-#{version}+#{extension}"
        available_media_types << "#{base_media_type}-#{version}"
      end
      available_media_types << "#{base_media_type}+#{extension}"
    end

    available_media_types << base_media_type
    available_media_types.concat(content_types.values.flatten)
  end
end
fail!(grape_allowed_methods) click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 96
def fail!(grape_allowed_methods)
  return grape_allowed_methods if grape_allowed_methods.present?

  media_types = q_values_mime_types.map { |mime_type| Grape::Util::MediaType.parse(mime_type) }
  vendor_not_found!(media_types) || version_not_found!(media_types)
end
invalid_accept_header!(message) click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 88
def invalid_accept_header!(message)
  raise Grape::Exceptions::InvalidAcceptHeader.new(message, error_headers)
end
invalid_version_header!(message) click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 92
def invalid_version_header!(message)
  raise Grape::Exceptions::InvalidVersionHeader.new(message, error_headers)
end
match_best_quality_media_type!() { |media_type| ... } click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 41
def match_best_quality_media_type!
  return unless vendor

  strict_header_checks!
  media_type = Grape::Util::MediaType.best_quality(accept_header, available_media_types)
  if media_type
    yield media_type
  else
    fail!(allowed_methods)
  end
end
q_values_mime_types() click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 80
def q_values_mime_types
  @q_values_mime_types ||= Rack::Utils.q_values(accept_header).map(&:first)
end
strict_header_checks!() click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 61
def strict_header_checks!
  return unless strict?

  accept_header_check!
  version_and_vendor_check!
end
vendor_not_found!(media_types) click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 103
def vendor_not_found!(media_types)
  return unless media_types.all? { |media_type| media_type&.vendor && media_type.vendor != vendor }

  invalid_accept_header!('API vendor not found.')
end
version_and_vendor?() click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 84
def version_and_vendor?
  q_values_mime_types.any? { |mime_type| Grape::Util::MediaType.match?(mime_type) }
end
version_and_vendor_check!() click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 74
def version_and_vendor_check!
  return if versions.blank? || version_and_vendor?

  invalid_accept_header!('API vendor or version not found.')
end
version_not_found!(media_types) click to toggle source
# File lib/grape/middleware/versioner/header.rb, line 109
def version_not_found!(media_types)
  return unless media_types.all? { |media_type| media_type&.version && versions&.exclude?(media_type.version) }

  invalid_version_header!('API version not found.')
end