You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby/Rails网络请求如何优雅捕获所有类型错误?

Great question—dealing with all the edge cases of HTTP requests in Rails can feel like herding cats, especially when you're repeating error-handling code everywhere. Let's break down a clean, DRY approach that covers both network-level errors and response validation issues.

1. Wrap Request Logic in a Reusable Service Class

The first step to DRYing up your code is to move all HTTP request logic into a dedicated service object. This way, you only write your error-handling code once, and every part of your app uses the same implementation.

Let's use Faraday (a popular HTTP client for Ruby) as an example, but this pattern works with Net::HTTP or any other client too:

# app/services/http_service.rb
class HttpService
  class RequestError < StandardError; end
  class ResponseValidationError < StandardError; end

  def self.get(url, params = {})
    perform_request(:get, url, params)
  end

  def self.post(url, body = {})
    perform_request(:post, url, body)
  end

  private

  def self.perform_request(method, url, payload)
    connection = Faraday.new(url: url) do |faraday|
      faraday.response :json # Auto-parse JSON responses
      faraday.adapter Faraday.default_adapter
    end

    response = connection.public_send(method) do |req|
      req.params = payload if method == :get
      req.body = payload if method == :post
    end

    # Validate response structure after successful request
    validate_response(response)

    { success: true, data: response.body }
  rescue Faraday::NotFoundError => e
    handle_error("Resource not found (404): #{e.message}")
  rescue Faraday::TimeoutError, Timeout::Error, SocketError => e
    handle_error("Network timeout or connection failure: #{e.message}")
  rescue Faraday::Error => e
    handle_error("HTTP request failed: #{e.message}")
  rescue StandardError => e
    handle_error("Unexpected error: #{e.message}")
  end

  def self.validate_response(response)
    # Example: Check if required fields exist in the response
    required_fields = [:id, :name] # Adjust to your app's needs
    missing_fields = required_fields.select { |field| response.body[field].nil? }

    unless missing_fields.empty?
      raise ResponseValidationError, "Missing required fields: #{missing_fields.join(', ')}"
    end
  end

  def self.handle_error(message)
    Rails.logger.error("[HttpService] #{message}")
    { success: false, error: message }
  end
end
2. Use a Consistent Result Object (Optional but Powerful)

Instead of returning a hash, you can create a simple Result class to make success/error handling even clearer. This eliminates the need to check hash keys everywhere:

# app/services/result.rb
class Result
  attr_reader :success, :data, :error

  def initialize(success:, data: nil, error: nil)
    @success = success
    @data = data
    @error = error
  end

  def self.success(data)
    new(success: true, data: data)
  end

  def self.failure(error)
    new(success: false, error: error)
  end
end

Then update the perform_request and handle_error methods in HttpService to return Result instances:

# In HttpService#perform_request
Result.success(response.body)

# In HttpService#handle_error
Result.failure(message)
3. Use the Service Everywhere

Now, whenever you need to make an HTTP request, just call the service, and handle the result cleanly:

result = HttpService.get("https://api.example.com/users", id: 123)

if result.success?
  # Do something with the valid response data
  render json: result.data
else
  # Handle error uniformly across your app
  render json: { error: result.error }, status: :bad_request
end
Key Benefits of This Approach
  • DRY: No more repeating rescue blocks across controllers/jobs. All error handling lives in one maintainable place.
  • Comprehensive Coverage: Catches network timeouts, 404s, 500s, connection errors, and even unexpected edge cases via the StandardError fallback.
  • Response Validation: Ensures the response has all the fields you need, turning silent failures (like missing data) into explicit, actionable errors.
  • Consistent Interface: Callers only need to check success? instead of handling a dozen different exception types.

You can extend this further by adding more specific custom exceptions (like RateLimitError if you hit API rate limits) or adjusting the validation logic to match your app's unique data requirements.

内容的提问来源于stack exchange,提问作者arjun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:19:34