1

Rails アプリがあり、Grape gem を使用して API を実装しました。ここで、CSV 形式でエラー応答を返すカスタム エラー フォーマッタ (CSVFormatter) を作成しました。

また、アプリケーションの v2.rb ファイルにもこれがあります。

error_formatter :csv, Api::Base::Errors::CSVFormatter

次のような URL にアクセスすると、次のようになります。

http://example.com/api/v2/datasets/CODE/data.csv?&trim_start=06/01/99&trim_end=2014-05/28&sort_order=desc

次のようにコンソールにエラーが表示されます。これは、カスタム エラー フォーマッタが適切に機能していることを意味します。

Error 
trim_start is invalid 
trim_end is invalid

ただし、このエラー メッセージを csv ファイルでダウンロードする必要があります。Grape のドキュメントを見た後、Content-type を設定する方法を見つけたので、これを試しました。

      rack = Rack::Response.new(as_csv , 422, { "Content-type" => "text/csv" }).finish
      rack[2].body[0]

しかし、これは私が期待したように機能していません。

編集:

サイモンの回答によると、ステータスコードを強制的にオーバーライドせずにグレープを使用してそれを行うクリーンな方法はないようです。ただし、他のプログラムが API からデータを読み取ろうとして誤った応答を取得したり、理由がわからなくても、アプリケーションで他の問題が発生する可能性があるため、これを実行したくない場合があります。

4

1 に答える 1

1

Content-Disposition ヘッダーを探しています。次のように応答に含めます。

Content-Disposition: attachment; filename=error.csv

Web ブラウザーは、応答本文をダウンロードするファイルとして扱います (この例では "error.csv" に)。

ただし、これを行うためにコードを変更すると、次の 2 つの点で複雑になります。

  • Grape のソース コードから、エラー フォーマッタ内から応答ヘッダーを設定する方法がないことは明らかです。そのため、応答本文をフォーマットし、サポートする予定の出力形式ごとに応答ヘッダーを適切に設定するカスタム例外ハンドラーを追加する必要があります。 .

  • 私の実験によると、HTTP ステータス コードがエラー (400 または 500 の範囲内のものなど) を示している場合、ブラウザーは Content-Disposition ヘッダーを無視するため、ユーザーが CSV ファイルを要求したときにステータス コードもオーバーライドする必要があります。

これを API クラスに追加してみてください。

# Handle all exceptions with an error response appropriate to the requested
# output format
rescue_from :all do |e|
  # Edit this hash to override the HTTP response status for specific output
  # formats
  FORMAT_SPECIFIC_STATUS = {
    :csv => 200
  }

  # Edit this hash to add custom headers specific to each output format
  FORMAT_SPECIFIC_HEADERS = {
    :csv => {
      'Content-Disposition' => 'attachment; filename=error.csv'
    }
  }

  # Get the output format requested by the user
  format = env['api.format']

  # Set the HTTP status appropriately for the requested output format and
  # the error type
  status = FORMAT_SPECIFIC_STATUS[format] ||
             (e.respond_to? :status) && e.status ||
             500

  # Set the HTTP headers appropriately for the requested format
  headers = {
    'Content-Type' => options[:content_types][format] || 'text/plain'
  }.merge(FORMAT_SPECIFIC_HEADERS[format] || { })

  # Format the message body using the appropriate error formatter
  error_formatter =
    options[:error_formatters][format] || options[:default_error_formatter]
  body = error_formatter.call(e.message, nil, options, env)

  # Return the error response to the client in the correct format
  # with the correct HTTP headers for that format
  Rack::Response.new(body, status, headers).finish
end

ここで、2 つの異なる形式を処理するように API クラスを構成すると (ここでは簡単にするために CSV とプレーンテキストを選択しました)、次のようになります。

module Errors
  module CSVErrorFormatter
    def self.call(message, backtrace, options, env)
      as_csv = "CSV formatter:" + "\n"
      message.split(",").each do |msg|
        as_csv += msg + "\n"
      end

      # Note this method simply returns the response body
      as_csv
    end
  end

  module TextErrorFormatter
    def self.call(message, backtrace, options, env)
      as_txt = "Text formatter:" + "\n"
      message.split(",").each do |msg|
        as_txt += msg + "\n"
      end

      as_txt
    end
  end
end

content_type :csv, 'text/csv'
content_type :txt, 'text/plain'

error_formatter :csv, Api::Base::Errors::CSVErrorFormatter
error_formatter :txt, Api::Base::Errors::TextErrorFormatter

API は常に、要求された形式に適したエラー応答を返し、CSV 形式が要求された場合にのみブラウザーをトリガーして応答をダウンロードすることがわかります。もちろん、コンテンツ タイプとエラー フォーマッタを明示的に宣言することで、これを拡張して好きなだけ多くのフォーマットをサポートできます。

このコードが自動的に正しいことをしないerror!ケースが 1 つあります。それは、 を使用してエラー応答が直接呼び出された場合です。その場合、呼び出し自体の一部として正しい本文​​とヘッダーを指定する必要があります。上記のコードの関連部分を再利用可能なメソッドに抽出することは、読者の演習として残します。

于 2014-06-26T09:57:30.237 に答える