Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
416 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
--- | ||
layout: documentation | ||
title: "JSON Request Middleware" | ||
permalink: /middleware/json-request | ||
hide: true | ||
prev_name: UrlEncoded Middleware | ||
prev_link: ./url-encoded | ||
next_name: Retry Middleware | ||
next_link: ./retry | ||
top_name: Back to Middleware | ||
top_link: ./list | ||
--- | ||
|
||
The `Json Request` middleware converts a `Faraday::Request#body` hash of key/value pairs into a JSON request body. | ||
The middleware also automatically sets the `Content-Type` header to `application/json`, | ||
processes only requests with matching Content-Type or those without a type and | ||
doesn't try to encode bodies that already are in string form. | ||
|
||
### Example Usage | ||
|
||
```ruby | ||
conn = Faraday.new(...) do |f| | ||
f.request :json | ||
... | ||
end | ||
|
||
conn.post('/', { a: 1, b: 2 }) | ||
# POST with | ||
# Content-Type: application/json | ||
# Body: {"a":1,"b":2} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
--- | ||
layout: documentation | ||
title: "JSON Response Middleware" | ||
permalink: /middleware/json-response | ||
hide: true | ||
prev_name: Instrumentation Middleware | ||
prev_link: ./instrumentation | ||
next_name: Logger Middleware | ||
next_link: ./logger | ||
top_name: Back to Middleware | ||
top_link: ./list | ||
--- | ||
|
||
The `Json Response` middleware parses response body into a hash of key/value pairs. | ||
The behaviour can be customized with the following options: | ||
* **parser_options:** options that will be sent to the JSON.parse method. Defaults to {} | ||
* **content_type:** Single value or Array of response content-types that should be processed. Can be either strings or Regex. Defaults to `/\bjson$/` | ||
* **preserve_raw:** If set to true, the original un-parsed response will be stored in the `response.env[:raw_body]` property. Defaults to `false` | ||
|
||
### Example Usage | ||
|
||
```ruby | ||
conn = Faraday.new('http://httpbingo.org') do |f| | ||
f.response :json, **options | ||
end | ||
|
||
conn.get('json').body | ||
# => {"slideshow"=>{"author"=>"Yours Truly", "date"=>"date of publication", "slides"=>[{"title"=>"Wake up to WonderWidgets!", "type"=>"all"}, {"items"=>["Why <em>WonderWidgets</em> are great", "Who <em>buys</em> WonderWidgets"], "title"=>"Overview", "type"=>"all"}], "title"=>"Sample Slide Show"}} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'json' | ||
|
||
module Faraday | ||
class Request | ||
# Request middleware that encodes the body as JSON. | ||
# | ||
# Processes only requests with matching Content-type or those without a type. | ||
# If a request doesn't have a type but has a body, it sets the Content-type | ||
# to JSON MIME-type. | ||
# | ||
# Doesn't try to encode bodies that already are in string form. | ||
class Json < Middleware | ||
MIME_TYPE = 'application/json' | ||
MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}.freeze | ||
|
||
def on_request(env) | ||
match_content_type(env) do |data| | ||
env[:body] = encode(data) | ||
end | ||
end | ||
|
||
private | ||
|
||
def encode(data) | ||
::JSON.generate(data) | ||
end | ||
|
||
def match_content_type(env) | ||
return unless process_request?(env) | ||
|
||
env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE | ||
yield env[:body] unless env[:body].respond_to?(:to_str) | ||
end | ||
|
||
def process_request?(env) | ||
type = request_type(env) | ||
body?(env) && (type.empty? || MIME_TYPE_REGEX =~ type) | ||
end | ||
|
||
def body?(env) | ||
(body = env[:body]) && !(body.respond_to?(:to_str) && body.empty?) | ||
end | ||
|
||
def request_type(env) | ||
type = env[:request_headers][CONTENT_TYPE].to_s | ||
type = type.split(';', 2).first if type.index(';') | ||
type | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# 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 | ||
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) | ||
::JSON.parse(body, @parser_options || {}) unless body.strip.empty? | ||
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 =~ 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 | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe Faraday::Request::Json do | ||
let(:middleware) { described_class.new(->(env) { Faraday::Response.new(env) }) } | ||
|
||
def process(body, content_type = nil) | ||
env = { body: body, request_headers: Faraday::Utils::Headers.new } | ||
env[:request_headers]['content-type'] = content_type if content_type | ||
middleware.call(Faraday::Env.from(env)).env | ||
end | ||
|
||
def result_body | ||
result[:body] | ||
end | ||
|
||
def result_type | ||
result[:request_headers]['content-type'] | ||
end | ||
|
||
context 'no body' do | ||
let(:result) { process(nil) } | ||
|
||
it "doesn't change body" do | ||
expect(result_body).to be_nil | ||
end | ||
|
||
it "doesn't add content type" do | ||
expect(result_type).to be_nil | ||
end | ||
end | ||
|
||
context 'empty body' do | ||
let(:result) { process('') } | ||
|
||
it "doesn't change body" do | ||
expect(result_body).to be_empty | ||
end | ||
|
||
it "doesn't add content type" do | ||
expect(result_type).to be_nil | ||
end | ||
end | ||
|
||
context 'string body' do | ||
let(:result) { process('{"a":1}') } | ||
|
||
it "doesn't change body" do | ||
expect(result_body).to eq('{"a":1}') | ||
end | ||
|
||
it 'adds content type' do | ||
expect(result_type).to eq('application/json') | ||
end | ||
end | ||
|
||
context 'object body' do | ||
let(:result) { process(a: 1) } | ||
|
||
it 'encodes body' do | ||
expect(result_body).to eq('{"a":1}') | ||
end | ||
|
||
it 'adds content type' do | ||
expect(result_type).to eq('application/json') | ||
end | ||
end | ||
|
||
context 'empty object body' do | ||
let(:result) { process({}) } | ||
|
||
it 'encodes body' do | ||
expect(result_body).to eq('{}') | ||
end | ||
end | ||
|
||
context 'object body with json type' do | ||
let(:result) { process({ a: 1 }, 'application/json; charset=utf-8') } | ||
|
||
it 'encodes body' do | ||
expect(result_body).to eq('{"a":1}') | ||
end | ||
|
||
it "doesn't change content type" do | ||
expect(result_type).to eq('application/json; charset=utf-8') | ||
end | ||
end | ||
|
||
context 'object body with vendor json type' do | ||
let(:result) { process({ a: 1 }, 'application/vnd.myapp.v1+json; charset=utf-8') } | ||
|
||
it 'encodes body' do | ||
expect(result_body).to eq('{"a":1}') | ||
end | ||
|
||
it "doesn't change content type" do | ||
expect(result_type).to eq('application/vnd.myapp.v1+json; charset=utf-8') | ||
end | ||
end | ||
|
||
context 'object body with incompatible type' do | ||
let(:result) { process({ a: 1 }, 'application/xml; charset=utf-8') } | ||
|
||
it "doesn't change body" do | ||
expect(result_body).to eq(a: 1) | ||
end | ||
|
||
it "doesn't change content type" do | ||
expect(result_type).to eq('application/xml; charset=utf-8') | ||
end | ||
end | ||
end |
Oops, something went wrong.