You can use callbacks to alter get/post parameters, the URL or set the post body (doing so overrides normal parameter insertion in to the body) before a request or to adjust the response after a request. This can either be a block or a named method (like ActionController's before_callback
/before_action
methods).
The callback is passed the name of the method (e.g. :save
) and an object (a request object for before_request
and a response object for after_request
). The request object has four public attributes post_params
(a Hash of the POST parameters), get_params
(a Hash of the GET parameters), headers
and url
(a String
containing the full URL without GET parameters appended).
require 'secure_random'
class Person < Flexirest::Base
before_request do |name, request|
if request.post? || name == :save
id = request.post_params.delete(:id)
request.get_params[:id] = id
end
end
before_request :replace_token_in_url
before_request :add_authentication_details
before_request :replace_body
before_request :override_default_content_type
private
def replace_token_in_url(name, request)
request.url.gsub!("#token", SecureRandom.hex)
end
def add_authentication_details(name, request)
request.headers["X-Custom-Authentication-Token"] = ENV["AUTH_TOKEN"]
end
def replace_body(name, request)
if name == :create
request.body = request.post_params.to_json
end
end
def override_default_content_type(name, request)
if name == :save
request.headers["Content-Type"] = "application/json"
end
end
end
If you need to, you can create a custom parent class with a before_request
callback and all children will inherit this callback.
class MyProject::Base < Flexirest::Base
before_request do |name, request|
request.get_params[:api_key] = "1234567890-1234567890"
end
end
class Person < MyProject::Base
# No need to declare a before_request for :api_key, already defined by the parent
end
After callbacks work in exactly the same way:
class Person < Flexirest::Base
get :all, "/people"
get :all, "/people"
after_request :fix_empty_content
after_request :cache_all_people
private
def fix_empty_content(name, response)
if response.status == 204 && response.body.blank?
response.body = '{"empty": true}'
end
end
def cache_all_people(name, response)
if name == :all
response.response_headers["Expires"] = 1.hour.from_now.iso8601
end
end
end
Note: since v1.3.21 the empty response trick above isn't necessary, empty responses for 204 are accepted normally (the method returns true
), but this is here to show an example of an after_request
callback adjusting the body. The cache_all_people
example shows how to cache a response even if the server doesn't send the correct headers.
If you manually set caching responses like the above, you may want to invalidate that cache when you make a create, update, delete etc request for the resource, for example:
class Animal < Flexirest::Base
after_request :cache
before_request :cache_cleanup
get :get, '/animals/:id'
post :update, '/animals/:id'
delete :delete, '/animals/:id'
def cache_cleanup(name, request)
if name == :update || name == :delete
Flexirest::Logger.info(" \033[1;4;32m#{Flexirest.name}\033[0m Invalidating cache for #{self.class.name} #{request.url}")
Rails.cache.delete("#{self.class.name}:#{request.url}")
end
end
def cache(name, response)
if name == :get
response.response_headers["Expires"] = 1.hour.from_now.iso8601
end
end
end
If you want to trap an error in an after_request
callback and retry the request, this can be done - but retries will only happen once for each request (so we'd recommend checking all conditions in a single after_request
and then retrying after fixing them all). You achieve this by raising a Flexirest::CallbackRetryRequestException
from the callback.
class Person < Flexirest::Base
get :all, "/people"
after_request :fix_invalid_request
private
def fix_invalid_request(name, response)
if response.status == 401
# Do something to fix the state of caches/variables used in the
# before_request, etc
raise Flexirest::CallbackRetryRequestException.new
end
end
end
If you want to cancel the request from within a callback, you can simply return false
and the mapped method result will also be false
, however, we would normally advocate raising an exception so the reason for the callback failure is more explicit.