1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 64 65 66 67 68 69 70 71 72 73 74
|
# frozen_string_literal: true
require 'json'
module Faraday
class Response
# Parse response bodies as JSON.
class Json < Middleware
def initialize(app = nil, parser_options: nil, content_type: /\bjson$/, preserve_raw: false)
super(app)
@parser_options = parser_options
@content_types = Array(content_type)
@preserve_raw = preserve_raw
process_parser_options
end
def on_complete(env)
process_response(env) if parse_response?(env)
end
private
def process_response(env)
env[:raw_body] = env[:body] if @preserve_raw
env[:body] = parse(env[:body])
rescue StandardError, SyntaxError => e
raise Faraday::ParsingError.new(e, env[:response])
end
def parse(body)
return if body.strip.empty?
decoder, method_name = @decoder_options
decoder.public_send(method_name, body, @parser_options || {})
end
def parse_response?(env)
process_response_type?(env) &&
env[:body].respond_to?(:to_str)
end
def process_response_type?(env)
type = response_type(env)
@content_types.empty? || @content_types.any? do |pattern|
pattern.is_a?(Regexp) ? type.match?(pattern) : type == pattern
end
end
def response_type(env)
type = env[:response_headers][CONTENT_TYPE].to_s
type = type.split(';', 2).first if type.index(';')
type
end
def process_parser_options
@decoder_options = @parser_options&.delete(:decoder)
@decoder_options =
if @decoder_options.is_a?(Array) && @decoder_options.size >= 2
@decoder_options.slice(0, 2)
elsif @decoder_options&.respond_to?(:load) # rubocop:disable Lint/RedundantSafeNavigation
# In some versions of Rails, `nil` responds to `load` - hence the safe navigation check above
[@decoder_options, :load]
else
[::JSON, :parse]
end
end
end
end
end
Faraday::Response.register_middleware(json: Faraday::Response::Json)
|