Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make RaiseError middleware configurable to not raise error on certain status codes (e.g. 404) #1590

Merged
merged 3 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions docs/middleware/included/raising-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,28 @@ and raised as `Faraday::NilStatusError`, which inherits from `Faraday::ServerErr

The behavior of this middleware can be customized with the following options:

| Option | Default | Description |
|---------------------|---------|-------------|
| **include_request** | true | When true, exceptions are initialized with request information including `method`, `url`, `url_path`, `params`, `headers`, and `body`. |
| Option | Default | Description |
|----------------------|---------|-------------|
| **include_request** | true | When true, exceptions are initialized with request information including `method`, `url`, `url_path`, `params`, `headers`, and `body`. |
| **allowed_statuses** | [] | An array of status codes that should not raise an error. |

### Example Usage

```ruby
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
faraday.response :raise_error, include_request: true
faraday.response :raise_error, include_request: true, allowed_statuses: [404]
end

begin
conn.get('/wrong-url') # => Assume this raises a 404 response
rescue Faraday::ResourceNotFound => e
e.response[:status] #=> 404
e.response[:headers] #=> { ... }
e.response[:body] #=> "..."
e.response[:request][:url_path] #=> "/wrong-url"
conn.get('/wrong-url') # => Assume this raises a 404 response
conn.get('/protected-url') # => Assume this raises a 401 response
rescue Faraday::UnauthorizedError => e
e.response[:status] # => 401
e.response[:headers] # => { ... }
e.response[:body] # => "..."
e.response[:request][:url_path] # => "/protected-url"
end
```

In this example, a `Faraday::UnauthorizedError` exception is raised for the `/protected-url` request, while the
`/wrong-url` request does not raise an error because the status code `404` is in the `allowed_statuses` array.
32 changes: 15 additions & 17 deletions lib/faraday/response/raise_error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,30 @@ class RaiseError < Middleware
# rubocop:disable Naming/ConstantName
ClientErrorStatuses = (400...500)
ServerErrorStatuses = (500...600)
ClientErrorStatusesWithCustomExceptions = {
400 => Faraday::BadRequestError,
401 => Faraday::UnauthorizedError,
403 => Faraday::ForbiddenError,
404 => Faraday::ResourceNotFound,
408 => Faraday::RequestTimeoutError,
409 => Faraday::ConflictError,
422 => Faraday::UnprocessableEntityError,
429 => Faraday::TooManyRequestsError
}.freeze
# rubocop:enable Naming/ConstantName

DEFAULT_OPTIONS = { include_request: true }.freeze
DEFAULT_OPTIONS = { include_request: true, allowed_statuses: [] }.freeze

def on_complete(env)
return if Array(options[:allowed_statuses]).include?(env[:status])

case env[:status]
when 400
raise Faraday::BadRequestError, response_values(env)
when 401
raise Faraday::UnauthorizedError, response_values(env)
when 403
raise Faraday::ForbiddenError, response_values(env)
when 404
raise Faraday::ResourceNotFound, response_values(env)
when *ClientErrorStatusesWithCustomExceptions.keys
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neat!

raise ClientErrorStatusesWithCustomExceptions[env[:status]], response_values(env)
when 407
# mimic the behavior that we get with proxy requests with HTTPS
msg = %(407 "Proxy Authentication Required")
raise Faraday::ProxyAuthError.new(msg, response_values(env))
when 408
raise Faraday::RequestTimeoutError, response_values(env)
when 409
raise Faraday::ConflictError, response_values(env)
when 422
raise Faraday::UnprocessableEntityError, response_values(env)
when 429
raise Faraday::TooManyRequestsError, response_values(env)
when ClientErrorStatuses
raise Faraday::ClientError, response_values(env)
when ServerErrorStatuses
Expand Down
20 changes: 20 additions & 0 deletions spec/faraday/response/raise_error_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,24 @@
end
end
end

describe 'allowing certain status codes' do
let(:conn) do
Faraday.new do |b|
b.response :raise_error, allowed_statuses: [404]
b.adapter :test do |stub|
stub.get('bad-request') { [400, { 'X-Reason' => 'because' }, 'keep looking'] }
stub.get('not-found') { [404, { 'X-Reason' => 'because' }, 'keep looking'] }
end
end
end

it 'raises an error for status codes that are not explicitly allowed' do
expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError)
end

it 'does not raise an error for allowed status codes' do
expect { conn.get('not-found') }.not_to raise_error
end
end
end