ruby on rails - How can I have Grape return error messages in CSV format? -
i have rails app , have implemented api using grape gem. now, created custom error formatter (csvformatter) return error response in csv format.
and, have in application's v2.rb file:
error_formatter :csv, api::base::errors::csvformatter
when hit url this:
it shows error in console , means custom error formatter working properly:
error trim_start invalid trim_end invalid
but, need download error message in csv file. after looking @ grape's documentation, found way of setting content-type , tried this:
rack = rack::response.new(as_csv , 422, { "content-type" => "text/csv" }).finish rack[2].body[0]
but, not working expected.
edit:
looks there no clean way of doing using grape without forcefully overriding status code according answer of simon. but, 1 may not wish may result other issues in application if other program tries read data api , gets incorrect response or without knowing why.
you're looking the content-disposition header. include in response this:
content-disposition: attachment; filename=error.csv
and web browser treat response body file downloaded (to "error.csv", in example).
however, modifying code complicated 2 things:
from the grape source code it's apparent there's no way set response headers within error formatter, you'll need add custom exception handler formats response body , sets response headers appropriately each output format plan support.
according experimentation, browsers ignore content-disposition header if http status code indicates error (e.g. in 400 or 500 range), status code need overridden when user requests csv file.
try adding api class:
# handle exceptions error response appropriate requested # output format rescue_from :all |e| # edit hash override http response status specific output # formats format_specific_status = { :csv => 200 } # edit hash add custom headers specific each output format format_specific_headers = { :csv => { 'content-disposition' => 'attachment; filename=error.csv' } } # output format requested user format = env['api.format'] # set http status appropriately requested output format , # error type status = format_specific_status[format] || (e.respond_to? :status) && e.status || 500 # set http headers appropriately requested format headers = { 'content-type' => options[:content_types][format] || 'text/plain' }.merge(format_specific_headers[format] || { }) # format message body using appropriate error formatter error_formatter = options[:error_formatters][format] || options[:default_error_formatter] body = error_formatter.call(e.message, nil, options, env) # return error response client in correct format # correct http headers format rack::response.new(body, status, headers).finish end
now if configure api class handle 2 different formats (i've picked csv , plain-text here simplicity), this:
module errors module csverrorformatter def self.call(message, backtrace, options, env) as_csv = "csv formatter:" + "\n" message.split(",").each |msg| as_csv += msg + "\n" end # note method returns response body as_csv end end module texterrorformatter def self.call(message, backtrace, options, env) as_txt = "text formatter:" + "\n" message.split(",").each |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
you should find api returns error response suitable requested format, , triggers browser download response when csv format requested. naturally can extended support many formats like, explicitly declaring content types , error formatters.
note there's 1 case in code doesn't automatically right thing, , that's when error response invoked directly using error!
. in case you'll have supply correct body , headers part of call itself. i'll leave extracting relevant parts of above code reusable methods exercise reader.
Comments
Post a Comment